zoukankan      html  css  js  c++  java
  • Redis之品鉴之旅(五)

    Redis事务

    • 原子性:就是最小的单位
    • 一致性:好多命令,要么全部执行成功,要么全部执行失败
    • 隔离性:一个会话和另一个会话之间是互相隔离的
    • 持久性:执行了就执行了,数据保存在硬盘上

    典型例子:银行转账,A给B转账100万,首先要A的账户减去100万,然后B的账户增加100万,如果中间断了那就出问题了。所以我们人为的将这件事进行原子化,做成一个最小单位。要么一起成功要么一起失败。

    redis是一种简单的数据存储形式,redis的事务是不支持回滚的。

    之前的sqlserver的事务,开了一个事务,去修改表的数据。然后有一个新的会话,或者新的连接,这个时候如果我们这儿连接去修改或者查询这个表时,可能会一直等待,直到事务操作完毕。

    而redis则不然,如果同样的操作使用redis事务的话,会导致事务操作失败。redis在开启事务前监听了3个要修改key的版本号,如果在这个事务期间,其他的连接可以修改这3个key对应的值,但是会影响当前开启事务这个会话的操作,让事务提交不成功。

    //事务模式 
    using (RedisClient client = new RedisClient("127.0.0.1", 6379, "12345", 10))
    {
    	//删除当前数据库中的所有Key  默认删除的是db0
    	client.FlushDb();
    	//删除所有数据库中的key 
    	//client.FlushAll();
    
    	client.Set("a", "1");
    	client.Set("b", "1");
    	client.Set("c", "1");
    
    	//获取当前这三个key的版本号 实现事务
    	client.Watch("a", "b", "c");
    	using (var trans = client.CreateTransaction())
    	{
    		trans.QueueCommand(p => p.Set("a", "3"));
    		trans.QueueCommand(p => p.Set("b", "3"));
    		trans.QueueCommand(p => p.Set("c", "3"));
    		//提交事务  如果在触发事务过程中,其他进程操作了当前的key,则事务提交失败,就是没有指令没有修改成功
    		var flag = trans.Commit();
    		// ID KEY
    		Console.WriteLine(flag);
    	}
    	//根据key取出值,返回string
    	Console.WriteLine(client.Get<string>("a") + ":" + client.Get<string>
    	("b") + ":" + client.Get<string>
    	("c"));
    	Console.ReadLine();
    }
    
    

    所以,在redis使用事务的情况下,必须使用watch进行监听一下对应的key值,凡是需要事务操作的key,都要包含在watch里面进行监听。

    单线程理解误区:(看代码)

    //单线程理解误区
    using (RedisClient client1 = new RedisClient("127.0.0.1", 6379, "12345", 10))
    {
    	//删除当前数据库中的所有Key  默认删除的是db0
    	client1.FlushDb();
    	//删除所有数据库中的key 
    	//client.FlushAll();
    
    	client1.Del("number");
    
    	for (int i = 0; i < 10; i++)
    	{
    		Task.Run(() =>
    		{
    			using (RedisClient client = new RedisClient("127.0.0.1", 6379, "12345", 10))
    			{
    				if (client.Get("number") == null)
    				{
    					Console.WriteLine("number == null");
    					client.Set<string>("number", "100");
    				}
    				else
    				{
    					Console.WriteLine("number != null");
    				}
    			}
    		});
    	}
    }
    Console.ReadLine();
    
    

    在我的电脑上最终打印结果:

    number == null
    number == null
    number == null
    number == null
    number != null
    number != null
    number != null
    number != null
    number != null
    number != null
    

    这里面的语句(if (client.Get("number") == null))会出现4次执行成功,这会误导我们对单线程的理解。说明进入这个判断时,有4个同时进入了,这4个获取过程会排队到redis里面,redis会一个个的进行执行,那么这4个就都是成功的,所以会有4次成功执行,而这4次执行完之后,key就被赋值了,其他线程进入时,就会不为null。这里面的单线程指的是redis执行的过程,而不是程序中线程执行的过程。如果想要只进入一次,就需要对这个过程加锁。(我的理解是,因为我的CPU是2核4线程,所以会有4个线程同时进入)

    //单线程理解误区
    using (RedisClient client1 = new RedisClient("127.0.0.1", 6379, "12345", 10))
    {
    	//删除当前数据库中的所有Key  默认删除的是db0
    	client1.FlushDb();
    	//删除所有数据库中的key 
    	//client.FlushAll();
    
    	client1.Del("number");
    
    	for (int i = 0; i < 10; i++)
    	{
    		Task.Run(() =>
    		{
    			using (RedisClient client = new RedisClient("127.0.0.1", 6379, "12345", 10))
    			{
    				lock ("123")
    				{
    					if (client.Get("number") == null)
    					{
    						Console.WriteLine("number == null");
    						client.Set<string>("number", "100");
    					}
    					else
    					{
    						Console.WriteLine("number != null");
    					}
    				}
    			}
    		});
    	}
    }
    Console.ReadLine();
    
    
  • 相关阅读:
    暑假学习
    暑假学习
    暑假学习
    暑假学习
    暑假学习
    经验教训+总结
    NT 时刻
    联赛模拟测试 17
    联赛模拟测试 16
    联赛模拟测试 15
  • 原文地址:https://www.cnblogs.com/vigorous/p/13556296.html
Copyright © 2011-2022 走看看