Redis 事务支持
Redis中事务相关的命令有MULTI、EXEC、DISCARD、WATCH和UNWATCH。
Redis事务保证原子性:要么所有命令都执行(都执行并不代表都成功执行),要么都不执行
事务以MULTI开始,以EXEC或DISCARD结束,示例:
原子性删除多个key:
Redis会将MULTI后面的多个命令入队列,等待EXEC时将队列的命令顺序执行,在执行事务内的命令时不会去处理其它客户端的请求。
有两种事务错误的情况:
- 命令无法入队列, 因此在执行EXEC之前我们就可以知道这种错误(正常入队列返回的是"QUEUED"), 什么情况下会出现无法入队列这种情况呢?比如错误的命令, 命令参数对不上, Redis服务器内存不足等
2.6.5版本之前, 客户端需要手动地检查是否所有命令入队成功, 客户端可以感知到第一种错误(只要不是返回QUEUED就是入队发送错误), 这时调用DISCARD终止事务即可,如果不这么做,会造成这样一种后果:部分命令执行成功而部分命令执行失败,示例:
(版本2.4.5)
上面事务中有两条指令,第二条是语法错误的,因此入队列失败,调用exec时redis并不知道有命令入队列失败,因此会执行队列中的命令,也就是incr k1,这样导致对客户端来说出现不一致的现象。
在2.6.5版本及之后, 就无需自己检查是否所有的命令都入队成功了,Redis会自己记录下是否有命令入队不成功, 如果有, 那么调用EXEC时就会返回一个错误。这时调用DISCARD终止事务即可
可以看到,和2.4.5版本不一样的是,当有命令入队列不成功时,调用exec时redis压根不会去执行该事务的任何命令,而是返回一个错误
- 命令本身符合语法, 但执行过程中出错(即EXEC调用后出错), 比如incr一个非数值类型的key
演示:
(版本2.6.5)
(版本3.2.5)
可以看到这种情况,无论哪个版本都一样,都会导致部分命令执行成功而部分执行失败的现象,也就是说Redis事务是没有回滚这个概念的(-__-),官方怎么解释这个?见文章末尾.
WATCH和UNWATCH命令
redis中的watch可以实现乐观锁的效果,用watch来实现incr:
WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC
如果事务执行过程中别的client修改了mykey的值,那么该事务将会失败,这就类似于cas的操作,你可以在客户端套一层循环来实现加1的操作
在以下情况时就无需显示地调用UNWATCH:
exec或者discard被调用后,所有被watched的key是都将变成unwatched状态。
connection断开时,所有该connection上被watched的key都将变成unwatched状态
Why Redis does not support roll backs?
If you have a relational databases background, the fact that Redis commands can fail during a transaction, but still Redis will execute the rest of the transaction instead of rolling back, may look odd to you.
However there are good opinions for this behavior:
- Redis commands can fail only if called with a wrong syntax (and the problem is not detectable during the command queueing), or against keys holding the wrong data type: this means that in practical terms a failing command is the result of a programming errors, and a kind of error that is very likely to be detected during development, and not in production.
- Redis is internally simplified and faster because it does not need the ability to roll back.
An argument against Redis point of view is that bugs happen, however it should be noted that in general the roll back does not save you from programming errors. For instance if a query increments a key by 2 instead of 1, or increments the wrong key, there is no way for a rollback mechanism to help. Given that no one can save the programmer from his or her errors, and that the kind of errors required for a Redis command to fail are unlikely to enter in production, we selected the simpler and faster approach of not supporting roll backs on errors.
个人觉得这个解释比较勉强,程序员犯二是很正常的,一个事务中间错了一条命令,就会导致不一致的情况,这就比价尴尬了,所以我是不知道这个事务能用在啥地方?求路过的高人指点