首页 > 代码库 > redis 事务

redis 事务

事务相关命令

 
 
Redis通过multi,discard,exec和watch四个命令来实现事务功能,
 
一般事务
  • multi
  • discard
  • exec
 
特殊:watch
 
服务器在执行完事务中的所有命令之后,才会继续处理其他客户端的其他命令
 
 
事务由multi开启,将多个命令入队到事务中,最后exec命令触发事务,一并执行性:
一个事务从开始到执行会经历以下三个阶段:
  • 开始事务。
    • multi命令将服务器切换到事务状态
  • 命令入队。
    • 将命令放进事务队列里
  •  执行事务。
    • 从事务对列中取出命令并执行
 
 
事务队列是一个数组,每个数组项包含三个属性
  • 要执行的命令(cmd)
  • 命令的参数(argv)
  • 参数的个数(argc)
 
例子:
举个例子,如果客户端执行以下命令:
redis> MULTI
OK
redis> SET book-name "Mastering C++ in 21 days"
QUEUED
redis> GET book-name
QUEUED
redis> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis> SMEMBERS tag
QUEUED
 

  

程序为客户端创建一下事务队列
技术分享
技术分享

 

exec,discard,multi,watch 是不放入事务队列中的,而是直接执行的。
 
事务队列的执行结果,放入回复队列中,当执行exec命令会将回复队列作为自己的执行结果放回客户端,
并从事务状态返回到非事务状态。
 
无论在事务状态下,还是在非事务状态下,Redis 命令都由同一个函数执行,所以它们共享很
多服务器的一般设置,比如 AOF 的配置、 RDB 的配置,以及内存限制,等等
 
事务中命令和普通命令的区别
  • 非事务状态下的命令以单个命令为单位执行,前一个命令和后一个命令的客户端不一定是同一个;
    • 而事务状态则是以一个事务为单位,执行事务队列中的所有命令:
    • 除非当前事务执行完毕,否则服务器不会中断事务,也不会执行其他客户端的其他命令。
  • 在非事务状态下,执行命令所得的结果会立即被返回给客户端;
    • 而事务则是将所有命令的结果集合到回复队列,再作为 EXEC 命令的结果返回给客户端
 
Redis 的事务是不可嵌套的,处于事务状态的客户端在发送multi,则服务器只会返回一个简单错误不做任何操作,
继续等待其他命令入队。
 
DISCARD
  • 命令用于取消一个事务,它清空客户端 的整个事务队列,然后将客户端从事务状态调回非事务状态
 
WATCH
  • 只能在客户端进入事务状态之前执行,在事务状态下发送 WATCH 命令会引发一个简单错误,不会做其他改变
  • 命令用于在事务开始之前监视任意数量的键:
    • 当调用 EXEC 命令执行事务时,如果任意一个被监视的键已经被其他客户端修改了,
    • 那么整个事务不再执行,直接返回失败。
    • watch一个key,这个key只能是自己改动,别人不能改,如果别人改了,我就不玩儿了。
    • 也就是事务的安全性
 
WATCH命令的实现
 
存储
watch_keys 字典,字典的键是这个数据库被监控的键值,而字典的值则是一个链表,链表保存所有监视这个键的客户端。
技术分享
技术分享

 

 
watch 触发
在任何对数据库键空间( key space)进行修改的命令成功执行之后(比如 FLUSHDB 、 SET、DEL 、LPUSH 、SADD 、ZREM ,诸如此类),
multi.c/touchWatchKey 函数都被调用,检查数据库的watch_keys字典,看是否有客户端在监视已经被命令修改的键。
如果有, 程序将所有监视这个/这些被修改键的客户端的 REDIS_DIRTY_CAS 选项打开,表明事务安全性已经被破坏。
技术分享
 技术分享

 

当客户端发送exec命令,触发事务执行性,服务器会对客户端状态进行检查:
REDIS_DIRTY_CAS
  • 被打开事务安全性已经被破坏,放弃事务,返回执行失败
  • 否则,服务器执行所有事务
 
最后,当一个客户端结束它的事务时,无论事务是成功执行,还是失败,watched_keys字典中和这个客户端相关的资料都会被清除。
 
事务的ACID性质

在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的安全性。
Redis 事务保证了其中的一致性( C)和隔离性( I),但并不保证原子性( A)和持久性( D)
 
原子性(Atomicity)
事务中命令要么全部执行成功,有一条失败则全部回滚
 
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以Redis事务的执行并不是原子性的
如果 Redis 服务器进程在执行事务的过程中被停止,那么事务执行失败,但是不会回滚。
 
一致性(Consistency)
redis的一致性问题分三部分
  • 入队错误
  • 执行错误
  • redis进程被终结
 
入队错误
客户端向服务器发送了错误的命令。
  • 服务器将想客户端返回出错信息,并将客户端设为REDIS_DIRTY_EXEC状态。
  • 当执行exec命令时,Redis拒绝执行状态为 REDIS_DIRTY_EXEC 的事务。
  • 因此,带有不正确入队命令的事务不会被执行,也不会影响数据库的一致性
 
执行错误
对一个不同类型的 key 执行了错误的操作
  • Redis 只会将错误包含在事务的结果中
  • 这不会引起事务中断或整个失败
  • 不会影响已执行事务命令的结果
  • 也不会影响后面要执行的事务命令
  • 所以它对事务的一致性也没有影响
 
redis进程被终结
Redis 进程被终结 那么
根据 Redis 所使用的持久化模式,可能有以下情况出现:
  • 内存模式
    • 如果 Redis 没有采取任何持久化机制,那么重启之后的数据库总是空白的,所以数据总是一致的。
  • RDB模式
    • 在执行事务时,Redis 不会中断事务去执行保存 RDB 的工作,只有在事务执行之后,保存 RDB 的工作才有可能开始。
    • 所以当 RDB 模式下的 Redis 服务器进程在事务中途被杀死时,事务内执行的命令,不管成功了多少,都不会被保存到 RDB文件里。
    • 恢复数据库需要使用现有的 RDB 文件,而这个 RDB 文件的数据保存的是最近一次的数据库快照( snapshot),所以它的数据可能不是最新的
      • 但只要 RDB 文件本身没有因为其他问题而出错,那么还原后的数据库就是一致的。
  • AOF模式
    • 因为保存 AOF 文件的工作在后台线程进行,所以即使是在事务执行的中途,保存 AOF 文件的工作也可以继续进行
    • 因此,根据事务语句是否被写入并保存到 AOF文件,有以下两种情况发生:
      • 如果事务语句未写入到 AOF 文件,或 AOF 未被 SYNC 调用保存到磁盘
        • 那么当进程被杀死之后,Redis 可以根据最近一次成功保存到磁盘的 AOF 文件来还原数据库
        • 只要 AOF 文件本身没有因为其他问题而出错,那么还原后的数据库总是一致的,但其中的数据不一定是最新的
      • 如果事务的部分语句被写入到 AOF 文件,并且 AOF 文件被成功保存
        • 那么不完整的事务执行信息就会遗留在 AOF 文件里
        • 当重启 Redis 时,程序会检测到 AOF 文件并不完整,Redis 会退出,并报告错误
          • 需要使用 redis-check-aof 工具将部分成功的事务命令移除之后,才能再次启动服务器。
          • 还原之后的数据总是一致的,而且数据也是最新的(直到事务执行之前为止)。
 
隔离性(lsolation)
Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执
行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。
 
 
持久性(Durability)
因为事务不过是用队列包裹起了一组 Redis 命令,并没有提供任何额外的持久性功能,所以事
务的持久性由 Redis 所使用的持久化模式决定:
  • 在单纯的内存模式下,事务肯定是不持久的。
  • 在RDB模式下,服务器可能在事务执行之后,RDB文件更新之前的这段时间失败,所以RDB模式下的Redis 事务也是不持久的
  • 在AOF的“总是 SYNC ”模式下,事务的每条命令在执行成功之后,都会立即调用 fsync或 fdatasync 将事务数据写入到 AOF 文件。
    • 但是,这种保存是由后台线程进行的,主线程不会阻塞直到保存成功,所以从命令执行成功到数据保存到硬盘之间,还是有一段非常小的间隔,所以这种模式下的事务也是不持久的
    • 其他 AOF 模式也和“总是 SYNC ”模式类似,所以它们都是不持久的
 
小结
  •  事务提供了一种将多个命令打包,然后一次性、有序地执行的机制。
  •  事务在执行过程中不会被中断,所有事务命令执行完之后,事务才能结束。
  •  多个命令会被入队到事务队列中,然后按先进先出( FIFO)的顺序执行。
  •  带 WATCH 命令的事务会将客户端和被监视的键在数据库的 watched_keys 字典中进行关联,
    • 当键被修改时,程序会将所有监视被修改键的客户端的REDIS_DIRTY_CAS选项打开。
    • 只有在客户端的 REDIS_DIRTY_CAS 选项未被打开时,才能执行事务,否则事务直接返回失败。
  •  Redis 的事务保证了 ACID 中的一致性( C)和隔离性( I),但并不保证原子性( A)和持久性( D)。
 

redis 事务