虽然Redis保证了每一条指令的执行都是原子操作,但是如果我们有多条指令需要作为一个原子操作,就需要使用到它的事务功能。
但要注意的是,Redis虽然提供了事务功能,但是并没有提供回滚操作。即使我们的事务出现异常,它也有可能让部分指令执行成功。
事务命令
- multi:开启事务
- exec:执行事务里的指令。
Redis中开启一个事务的指令是multi,返回ok,表示我们事务已经开启,此时再输入数据操作指令,会返回queued,表示将我们的指令暂存在了队列里面,将我们想要作为一个事务执行的指令全部输入完成后,再输入exec开始执行我们暂存好的指令。
指令的执行顺序是我们的输入顺序,返回结果会按照执行顺序以队列的形式返回。
样例如下:我们先开启事务,然后创建字符串类型的数据s=1,再令它自增1,然后执行事务,会返回[ok,2],我们查询s的值,此时已经变成了2。
异常情况
前面说了Redis中的事务是没有回滚操作的,出现异常分为两种情况。
- 添加指令时异常。(在multi后,exec前抛出异常)
- 执行指令时异常。(在exec后抛出异常)
添加指令时异常:这种情况的异常通常是指令不存在,或者缺少参数等原因。如果异常是在exec前抛出的,那么我们事务里面的指令会全部不执行。
样例如下:我们首先开启事务,然后创建一个字符串类型的数据s=1,这条数据被正常添加到队列中,然后对s做加法运算,但是缺少参数,此时就已经会立即返回一个异常。如果我们忽视异常,继续往下执行,则事务也会再次给我们返回一个异常。我们此时获取s的键值,发现它不存在,表示我们的第一条正常的指令也没有被事务执行。
执行指令时异常:这种情况的异常有很多可能性,最常见的是操作类型错误,如我们尝试对一个字符串类型的数据使用列表类型的指令,在添加指令时会返回操作正常的queued,但是在exec后会返回异常。此时这种情况会对事务中的可正常执行的指令全部正常执行,异常指令返回异常。
样例如下:我们先开启一个事务,然后创建一个字符串类型数据s=1.1,对它进行自增1操作,由于它是浮点型数字,因此会执行失败,我们再在下一行创建一个字符串类型数据a=1,最后执行事务,从返回结果里可以看到第一条和第三条指令正常执行并返回结果,第二条执行出现异常。
通过以上的两个例子,以及截图中的错误说明,我们可以进行如下总结:如果异常是出现在multi之后exec之前,说明事务生成失败,因此没有可执行的事务,事务内所有指令都被抛弃掉。如果异常出现在exec之后,此时事务是创建成功的,并会对每条指令进行执行,对于事务来说,它只负责原子性的执行事务内的指令,并不保证每条指令的执行成功与否。因此会存在能够正常执行的指令全部正常执行成功,执行失败的指令会执行失败但是也不会影响其他执行成功的指令。
简单地一句话总结:Redis事务保证了指令执行的原子性,但是不关心每条指令的执行结果。
观察指令 WATCH
虽然事务保证了指令执行的原子性,但是也有可能数据在我们事务之前就已经被其他指令所更改。
因此我们需要使用观察指令watch
watch key [keys];开启对一个或多个键的观察,会在一次事务执行后失效,或者在unwatch指令后失效。
下面用个例子说明:我们首先创建一个字符串类型数据s=1,然后用watch对它进行观察,这时候我们再对s自增1,然后开启事务,在事务中让s再次加1,按照正常逻辑来说,s=1被执行了两次自增,且事务里面的指令也不存在错误,它的结果应该是等于3。但是实际我们在事务执行后再次查看s的值时,发现它等于2。
上面一个样例要说明的是:watch指令可以帮助我们避免事务操作的数据值,在事务执行前被修改。
即假设有数据s=1,然后有用户A,开启事务对数据s进行加一操作,在A在事务中添加指令的时候(即在exec之前),同时有另一个用户B执行了incr s,提前对s进行了修改,那么此时A的事务必定失效且不会执行。
流程图如下,只要在观察范围内,有其他连接对数据S进行了修改,则最后的exec必定无效。
watch注意点:
- 事务失效指的是整个事务都执行失效,即事务里面所有的语句都不会再执行,包括被观察的数据s,和其他没有被观察到的数据,统统不会执行成功。
- 被观察的数据只要有一个被修改,其他数据的事务也都会失效。即我们同时观察了两个数据a和b,a被修改了,b没有被修改,但是在事务中我们只涉及到对b的修改,此时这个事务也会执行失效。且执行完成后,对数据a和b的观察也都会解除。
- unwatch是对所有数据的观察解除,它的后面没有参数,不能指定某个key。
- watch key [key...]和watch key;watch key指令效果是一样的。即在一条指令中观察多个数据watch a b c和多条指令观察多个数据watch a;执行后再watch b;执行后再watch c效果是一样的。
- 被观察的数据过期,不会被认为是改变了数据。
-