redis事务执行

事务提供了一种将多个命令打包,一次性按顺序执行的机制,并且事务在执行期间不会主动中断(服务段在执行完事务中的所有命令之后才会执行其他客户端的其他命令),redis通过MUTIL、DISCARD、EXEC和WATCH四个命令实现事务操作。

事务执行:

开启事务

multi,这个命令唯一做的是将客户端的REDIS_MUTIL选项打开,让客户端从非事务状态进入事务状态

写入命令

当客户端进入事务状态之后,服务端收到客户端的命令不会立即执行,而是将命令放到事务队列当中,然后返回QUEUED,表示命令已进入队。

事务队列是一个数组,每个数组项都包含三个属性:①要执行的命令②命令的参数③参数的个数

执行事务

前面所说当客户端进入事务状态后,客户端发送的命令将会放到队列当中,但是EXEC、DISCARD、MULTI、WATCH这四个命令例外,服务端遇到这四个命令会立即执行

EXEC、MULTI、DISCARD、WATCH命令

EXEC

如果客户端正处于事务状态、那么当exec命令执行时,服务端会根据事务队列中的命令,FIFO的执行命令,执行事务中命令的结果会以FIFO的顺序保存到一个回复队列当中,等整个事务完成之后将回复队列返回给客户端。

redis的事务是不可能嵌套的,当客户端处于事务状态,客户端继续发送multi命令,服务端给客户端发送一个简单的错误,然后继续等待客户端的其他命令入队,multi不会造成整个事务的失败,也不会修改事务队列中的数据

DISCARD

discard命令取消一个事务,它清空客户端整个事务队列,然后将客户端从事务状态变为非事务状态,最后返回OK给客户端,说明事务已经取消。

WATCH

watch命令只能在客户端进去事务状态之前执行,在事务状态下发送watch命令得到一个错误的信息,但不会造成事务的失败,也不会修改事务队列中的数据。

带WATCH的事务

WATCH命令用于在事务开始之前监控任意数量的key,当客户端发送exec命令执行事务时,如果被监控的任意一个键的值被其他客户端修改了,那么整个事务不再执行,直接返回失败。

WATCH原理

在每个代表数据库的结构类型中,都保存了一个watched_keys字典,字典的将时这个数据库被监视的key,而字典的值是一个链表,链表中保存了所有监控这个将的客户端。

WATCH命令的作用就是将客户端和要监控的键在watched_keys中进行关联。如果程序想检查某个键是否被监控,只要查watched_keys字典是否有这个key即可,如果先知道一个键被哪些客户端监控,那只要取出链表,对链表进行遍历即可。

WATCH的触发

在任何对数据库进行修改命令执行之后,都会检查数据库的watched_keys字典,看是否有客户端监控已经被修改的key,如果有的话,程序键所有监控这个key的客户端的REDIS_DIRTY_CAS选项打开,当客户端发送exec命令触发事务执行时,服务端会对客户端的REDIS_DIRTY_CAS选项是否打开,如果 打开,服务器放弃执行这个事务,直接先客户端返回空回复,表示事务执行失败。

redis中的ACID

redis事务保证的事务的一致性(C)和隔离性(I),但并不保证原子性(A)和持久性(D)

原子性

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以Redis 事务的执行并不是原子性的。如果一个事务队列中的所有命令都被成功地执行,那么称这个事务执行成功。另一方面,如果 Redis 服务器进程在执行事务的过程中被停止——比如接到 KILL 信号、宿主机器停机,等等,那么事务执行失败。当事务失败时,Redis 也不会进行任何的重试或者回滚动作。

一致性

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文件,有以下两种情况发生:1)如果事务语句未写入到 AOF 文件,或 AOF 未被 SYNC 调用保存到磁盘,那么当进程被杀死之后,Redis 可以根据最近一次成功保存到磁盘的 AOF 文件来还原数据库,只
要 AOF 文件本身没有因为其他问题而出错,那么还原后的数据库总是一致的,但其中的数据不一定是最新的。2)如果事务的部分语句被写入到 AOF 文件,并且 AOF 文件被成功保存,那么不完整的事务执行信息就会遗留在 AOF 文件里,当重启 Redis 时,程序会检测到 AOF 文件并不完整,Redis 会退出,并报告错误。需要使用 redis-check-aof 工具将部分成功的事务命令移除之后,才能再次启动服务器。还原之后的数据总是一致的,而且数据也是最新的(直到事务执行之前为止)。

隔离性(Isolation)

Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。

持久性(Durability)

因为事务不过是用队列包裹起了一组 Redis 命令,并没有提供任何额外的持久性功能,所以事务的持久性由 Redis 所使用的持久化模式决定:
①在单纯的内存模式下,事务肯定是不持久的。
②在 RDB 模式下,服务器可能在事务执行之后、RDB 文件更新之前的这段时间失败,所以 RDB 模式下的 Redis 事务也是不持久的。
③在 AOF 的“总是 SYNC ”模式下,事务的每条命令在执行成功之后,都会立即调用 fsync或 fdatasync 将事务数据写入到 AOF 文件。但是,这种保存是由后台线程进行的,主线程不会阻塞直到保存成功,所以从命令执行成功到数据保存到硬盘之间,还是有一段非常小的间隔,所以这种模式下的事务也是不持久的。其他 AOF 模式也和“总是 SYNC ”模式类似,所以它们都是不持久的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章