Redis学习笔记(七)Transactions

Redis还提供了事务机制,MULTI, EXEC, DISCARD以及WATCH等命令构成了Redis事务机制的基础。本文将会介绍Redis事务的一些基本特性及使用方法等,并对乐观锁做了一些基础的介绍。

1.Redis事务特性

Redis事务机制可以使用户一次执行多个命令,并且提供了两点重要保证:

(1)顺序性

一个事务中的所有命令都是严格串行化、顺序化的执行的。也就是说,在Redis服务执行事务的过程中,Redis服务绝不会去服务由另一个客户端发出的请求。这就保证了,事务中的命令集被当做一个单独的操作被执行。

(2)原子性

Redis事务同时还具有原子性:事务中的命令要么全部执行,要么一个也不执行。首先, Redis事务使用MULTI命令来标记一个事务的开始,后续的命令将被放入队列中,EXEC命令来执行事务原子操作。所以,如果在执行EXEC命令之前,客户端与Redis服务断开了连接,那么事务中的所有命令都不会被执行;相反地,一旦Redis服务顺利的接收到了EXEC命令,那么事务中的所有命令都会被执行。

但是也会存在一些意外情况,如果Redis服务崩溃或者被系统管理员强制杀死,那么Redis服务也有可能只注册了一部分的操作。

2.使用方法

(1)MULTI

MULTI命令标识一个Redis事务的开始,该命令总是返回OK。输入MULTI命令之后,用户可以输入多个待执行的命令,Redis并不会去立即执行这些命令,而是将其添加到一个队列中去。

(2)EXEC

该命令表示执行先前加入到队列中的所有命令。比如,有如下示例:

(3)DISCARD

该命令将会清空事务队列里的命令,并退出事务:

3.错误检查

Redis事务中常见的错误有两种:一种错误在执行EXEC前就会被发现;另一种错误需要在EXEC运行之后才可能被发现。

(1)EXEC前

在执行EXEC前,命令会被放入到一个命令队列中去。如果这个命令有严重的语法错误,那么将无法将其加入命令队列中,比如:

这种错误命令名、错误参数的命令以及诸如内存不足的错误,会在将命令加入队列中的时候就被发现,并提示用户。

(2)EXEC后

还有一种错误,比如,对一个string类型的对象错误的调用了list类型对象的操作方法:

如上所示,第一个命令成功返回,第二个命令报错:string类型的对象并没有提供LPOP操作,这种错误往往在运行的时候才会进行错误检查。

但是,特别需要注意的是:EXEC之后,即使命令队列中的某个命令存在错误,Redis还是会将所有的命令全部执行下去。看如下示例:

由上可知,即使第二个命令报错了,但是counter的值还是从1增加到了2。所以,即使一个命令失败了,Redis还是会把所有的命令都顺序的执行完毕。这点需要牢记。

4.回滚

Redis并不支持回滚!

在事务期间,Redis命令有可能会发生错误,Redis对此错误的处理方法是不采取操作。这个做法对于熟悉关系型数据库的我们来说,有点奇怪。那么,是什么原因让Redis这样设计的呢?

首先,如前所述,Redis命令错误的原因主要有两个方面:一是语法错误;二是对某一类型的对象执行该对象不存在的操作。Redis认为:这两种情况导致的错误都是在编程过程中产生的(并且很有可能被检测出来),而不是在实际的生产过程中产生的。所以,Redis认为它并不需要回滚操作。

其次,Redis不需要回滚的能力,能够使其内部更加精简和快速。

5.乐观锁

Redis还提供了WATCH命令,该命令可以用来为Redis事务提供检查和设置(Check-and-set, CAS)的行为。比如,使用WATCH监视某个或某些key之后,如果其中的某个或某些key在执行EXEC命令之前发生了修改,将会导致整个事务中断。

比如,实际工作中我们可能面对如下的业务逻辑:

单个用户操作以上示例,一般不会有问题。然而,如果在同一时间内,有多个客户端操作以上示例就比较危险了。比如,用户A和用户B都将val=10读到各自的内存中,随后A,B用户都会对val做自增操作,并将操作结果响应到mykey中。这样一来,导致mykey对应的value值为11而不是预期的12。

对此,WATCH命令(WATCH key [key …])提供了解决方案:

使用WATCH命令监视mykey,一旦mykey有任何修改,事务中断,保证了数据的正确性。这既是Redis提供的一种锁机制——乐观锁。

如果想要解除监视状态,则需要使用UNWATCH命令。该命令会刷新所有被监视的key,使其恢复到未被监视的状态。

6.其他

Redis脚本是事务性的,这意味着,任何可以通过Redis事务做的事情,都可以同样的通过Redis脚本实现,并且将会更加简单、快速。

 

参考:
https://redis.io/topics/transactions

发表评论

电子邮件地址不会被公开。 必填项已用*标注