Redis之坑:理解Redis事务

相关命令

命令 格式 做用 返回结果
WATCH WATCH key [key ...] 将给出的Keys标记为监测态,做为事务执行的条件 always OK.
UNWATCH UNWATCH 清除事务中Keys监测态,若是调用了EXEC or DISCARD,则没有必要再手动调用UNWATCH always OK.
MULTI MULTI 显式开启redis事务,后续commands将排队,等候使用EXEC进行原子执行 always OK.
EXEC EXEC 执行事务中的commands队列,恢复链接状态。若是WATCH在以前被调用,只有监测中的Keys没有被修改,命令才会被执行,不然中止执行(详见下文,CAS机制 成功: 返回数组 —— 每一个元素对应着原子事务中一个 command的返回结果;
失败: 返回NULLRuby 返回`nil`);
DISCARD DISCARD 清除事务中的commands队列,恢复链接状态。若是WATCH在以前被调用,释放 监测中的Keys always OK.

注意:程序员

------MULTI,EXEC,DISCARD才是显式开启并控制事务的经常使用命令,可类比关系型数据库中的 BEGAIN,COMMIT,ROLLBACK(事实上,差距很大);redis

------WATCH命令的使用是为了解决 事务并发 产生的不可重复读幻读的问题(简单理解为给Key加锁);数据库


Redis事务

MULTI, EXEC, DISCARD and WATCH 是Redis事务的基础。用来显式开启并控制一个事务,它们容许在一个步骤中执行一组命令。并提供两个重要的保证:编程

  • 事务中的全部命令都会被序列化并按顺序执行。在执行Redis事务的过程当中,不会出现由另外一个客户端发出的请求。这保证 命令队列 做为一个单独的原子操做被执行。
  • 队列中的命令要么所有被处理,要么所有被忽略。EXEC命令触发事务中全部命令的执行,所以,当客户端在事务上下文中失去与服务器的链接,
  • 若是发生在调用MULTI命令以前,则不执行任何commands
  • 若是在此以前EXEC命令被调用,则全部的commands都被执行。

同时,redis使用AOF(append-only file),使用一个额外的write操做将事务写入磁盘。若是发生宕机,进程奔溃等状况,可使用redis-check-aof tool 修复append-only file,使服务正常启动,并恢复部分操做。数组


用法

使用MULTI命令显式开启Redis事务。 该命令老是以OK回应。此时用户能够发出多个命令,Redis不会执行这些命令,而是将它们排队EXEC被调用后,全部的命令都会被执行。而调用DISCARD能够清除事务中的commands队列退出事务bash

  • 如下示例以原子方式,递增键foo和bar。
>MULTI
OK
>INCR foo
QUEUED
>INCR bar
QUEUED
>EXEC
1)(整数)1
2)(整数)1
复制代码

从上面的命令执行中能够看出,EXEC返回一个数组其中每一个元素都是事务中单个命令的返回结果,并且顺序与命令的发出顺序相同。 当Redis链接处于MULTI请求的上下文中时,全部命令将以字符串QUEUED从Redis协议的角度做为状态回复发送)做为回复,并在命令队列中排队。只有EXEC被调用时,排队的命令才会被执行,此时才会有真正的返回结果服务器


事务中的错误

事务期间,可能会遇到两种命令错误:并发

  • 在调用EXEC命令以前出现错误(COMMAND排队失败)。
  • 例如,命令可能存在语法错误(参数数量错误,错误的命令名称...);
  • 或者可能存在某些关键条件,如内存不足的状况(若是服务器使用maxmemory指令作了内存限制)。

客户端会在EXEC调用以前检测第一种错误。 经过检查排队命令的状态回复(***注意:这里是指排队状态回复,而不是执行结果***),若是命令使用QUEUED进行响应,则它已正确排队,不然Redis将返回错误。若是排队命令时发生错误,大多数客户端将停止该事务并清除命令队列。然而:app

  • Redis 2.6.5以前,这种状况下,在EXEC命令调用后,客户端会执行命令的子集(成功排队的命令)而忽略以前的错误。
  • Redis 2.6.5开始,服务端会记住在累积命令期间发生的错误,当EXEC命令调用时,将拒绝执行事务,并返回这些错误,同时自动清除命令队列
  • 示例以下:
>MULTI
+OK
>INCR a b c
-ERR wrong number of arguments for 'incr' command
复制代码

这是因为INCR命令的语法错误,将在调用EXEC以前被检测出来,并终止事务(version2.6.5+)。ui

  • 在调用EXEC命令以后出现错误。
  • 例如,使用错误的值对某个key执行操做(如针对String值调用List操做)

EXEC命令执行以后发生的错误并不会被特殊对待即便事务中的某些命令执行失败,其余命令仍会被正常执行

  • 示例以下:
>MULTI
+OK
>SET a 3
+QUEUED
>LPOP a
+QUEUED
>EXEC
*2
+OK
-ERR Operation against a key holding the wrong kind of value
复制代码
  • EXEC返回一个包含两个元素的字符串数组,一个元素是OK,另外一个是-ERR……
  • 可否将错误合理的反馈给用户这取决于客户端library(如:Spring-data-redis.redisTemplate)的自身实现。
  • 须要注意的是,即便命令失败,队列中的全部其余命令也会被处理----Redis不会中止命令的处理

Redis事务不支持Rollback(重点

事实上Redis命令在事务执行时可能会失败,但仍会继续执行剩余命令而不是Rollback(事务回滚)。若是你使用过关系数据库,这种状况可能会让你感到很奇怪。然而针对这种状况具有很好的解释:

  • Redis命令可能会执行失败,仅仅是因为错误的语法被调用(命令排队时检测不出来的错误),或者使用错误的数据类型操做某个Key: 这意味着,实际上失败的命令都是编程错误形成的,都是开发中可以被检测出来的,生产环境中不该该存在。(这番话,完全甩锅,“都是大家本身编程错误,与咱们无关”。)
  • 因为没必要支持Rollback,Redis内部简洁而且更加高效。

若是错误就是发生了呢?”这是一个反对Redis观点的争论。然而应该指出的是,一般状况下,回滚并不能挽救编程错误。鉴于没有人可以挽救程序员的错误,而且Redis命令失败所需的错误类型不太可能进入生产环境,因此咱们选择了不支持错误回滚(Rollback)这种更简单快捷的方法。


清除命令队列

DISCARD被用来停止事务。事务中的全部命令将不会被执行,链接将恢复正常状态。

> SET foo 1
OK
> MULTI
OK
> INCR foo
QUEUED
> DISCARD
OK
> GET foo
"1"
复制代码
相关文章
相关标签/搜索