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();

原文地址:https://www.cnblogs.com/vigorous/p/13556296.html