Redis MULTI
Redis实现事务,是基于COMMANDS队列。
如果没有开启事务,command将会被立即执行并返回执行结果,并且直接写入磁盘;
如果事务开启,command不会被立即执行,而是排入队列并返回排队状态(具体依赖于客户端(例如:spring-data-redis)自身实现)。
调用EXCE才会执行COMMANDS队列。
用法
使用MULTI命令显式开启Redis事务。 该命令总是以OK回应。
此时用户可以发出多个命令,Redis不会执行这些命令,而是将它们排队。
EXEC被调用后,所有的命令都会被执行。
而调用DISCARD可以清除事务中的commands队列并退出事务。
Redis 事务的错误
在调用EXEC命令之前出现错误(COMMAND排队失败)。
例如,命令可能存在语法错误(参数数量错误,错误的命令名称…);
或者可能存在某些关键条件,如内存不足的情况(如果服务器使用maxmemory指令做了内存限制)。
客户端会在EXEC调用之前检测第一种错误。
通过检查排队命令的状态回复(注意:这里是指排队的状态回复,而不是执行结果),如果命令使用QUEUED进行响应,则它已正确排队,否则Redis将返回错误。
如果排队命令时发生错误,大多数客户端将中止该事务并清除命令队列。
然而:
在Redis 2.6.5之前,这种情况下,在EXEC命令调用后,客户端会执行命令的子集(成功排队的命令)而忽略之前的错误。 从Redis 2.6.5开始,服务端会记住在累积命令期间发生的错误,当EXEC命令调用时,将拒绝执行事务,并返回这些错误,同时自动清除命令队列。
示例如下:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INC a b c
(error) ERR unknown command `INC`, with args beginning with: `a`, `b`, `c`,
这是由于INCR命令的语法错误,将在调用EXEC之前被检测出来,并终止事务(version2.6.5+)。
在调用EXEC命令之后出现错误。
例如,使用错误的值对某个key执行操作(如针对String值调用List操作) EXEC命令执行之后发生的错误并不会被特殊对待:即使事务中的某些命令执行失败,其他命令仍会被正常执行。
示例如下:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set name 1
QUEUED
127.0.0.1:6379> set age 12 12 1 2 12 12 1 2
QUEUED
127.0.0.1:6379> set address 10
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR syntax error
3) OK
127.0.0.1:6379> get name
"1"
127.0.0.1:6379> get address
"10"
127.0.0.1:6379> get age
(nil)
EXEC返回一个包含两个元素的字符串数组,一个元素是OK,另一个是-ERR……。
能否将错误合理的反馈给用户这取决于客户端library(如:Spring-data-redis.redisTemplate)的自身实现。
需要注意的是,即使命令失败,队列中的所有其他命令也会被处理—-Redis不会停止命令的处理
Redis事务不支持Rollback(重点)
事实上Redis命令在事务执行时可能会失败,但仍会继续执行剩余命令而不是Rollback(事务回滚)。
如果你使用过关系数据库,这种情况可能会让你感到很奇怪。然而针对这种情况具备很好的解释:
Redis命令可能会执行失败,仅仅是由于错误的语法被调用(命令排队时检测不出来的错误),或者使用错误的数据类型操作某个Key: 这意味着,实际上失败的命令都是编程错误造成的,都是开发中能够被检测出来的,生产环境中不应该存在。(这番话,彻底甩锅,“都是你们自己编程错误,与我们无关”。)
由于不必支持Rollback,Redis内部简洁并且更加高效。
“如果错误就是发生了呢?”这是一个反对Redis观点的争论。然而应该指出的是,通常情况下,回滚并不能挽救编程错误。鉴于没有人能够挽救程序员的错误,并且Redis命令失败所需的错误类型不太可能进入生产环境,所以我们选择了不支持错误回滚(Rollback)这种更简单快捷的方法。
参考资料
- spring data
http://blog.51cto.com/aiilive/1627455
https://juejin.im/post/5b1e5d4ee51d4506c60e4275
https://www.cnblogs.com/softidea/p/5720938.html