十六、事务(redis)

Redis通过MULTI、EXEC、WATCH等命令来实现事务(transaction)功能。事务提供 了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行 期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都 执行完毕,然后才去处理其他客户端的命令请求

一、事务的实现

一个事务从开始到结束通常会经历以下三个阶段:事务开始、命令人队、事务执行

1.1 事务开始

 MULTI
            MULTI命令的执行标志着事务的开始
            令可以将执行该命令的客户端从非事务状态切换至事务状态,这一切换是通 过在客户端状态的flags属性中打开REDIS_MULTI标识来完成的

1.2 命令入队

当一个客户端处于非事务状态时,这个客户端发送的命令会立即被服务器执行

当一个客户端切换到事务状态之后,服务器会根据这个客户端发来的不 同命令执行不同的操作
            如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令的其中一 个,那么服务器立即执行这个命令
            如果客户端发送的命令是EXEC、DISCARD、WATCH、MULTI四个命令 以外的其他命令,那么服务器并不立即执行这个命令,而是将这个命令放入一个事 务队列里面,然后向客户端返回QUEUED回复

1.3 事务队列

每个Redis客户端都有自己的事务状态,这个事务状态保存在客户端状态的mstate属性 里面
        事务状态包含一个事务队列’以及一个已人队命令的计数器(也可以说是事务队列的长度)
        事务队列是一个multiCmd类型的数组,数组中的每个multiCmd结构都保存了一个 已人队命令的相关信息,包括指向命令实现函数的指针、命令的参数,以及参数的数量
        事务队列以先进先出(FIFO)的方式保存人队的命令,较先人队的命令会被放到数组 的前面,而较后人队的命令则会被放到数组的后面

1.4 执行事务

当一个处于事务状态的客户端向服务器发送MULTI命令时,这个命令将立即被服 务器执行。服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行 命令所得的结果全部返回给客户端

 

二、WATCH命令的实现

WATCH命令是一个乐观锁(optimistic locking),它可以在EXEC命令执行之前,监视 任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改 过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复

2.1 使用WATCH命令监视数据库键

每个Redis数据库都保存着一个watched_keys字典,这个字典的键是某个被WATCH命 令监视的数据库键,而字典的值则是一个链表,链表中记录了所有监视相应数据库键的客户端
        通过watched_keyS字典,服务器可以清楚地知道哪些数据库键正在被监视,以及哪 些客户端正在监视这些数据库键

 2.2 监视机制的触发

所有对数据库进行修改的命令,比如SET、LPUSH、SADD、ZREM、DEL、FLUSHDB等等,在执行之后都会调用multi . c/touchWatchKey函数对watched_keys字典 进行检查,查看是否有客户端正在监视刚刚被命令修改过的数据库键,如果有的话,那么 touchWatchKey函数会将监视被修改键的客户端的REDIS_DIRTY_CAS标识打开,表示 该客户端的事务安全性已经被破坏

2.3 判断事务是否安全

当服务器接收到一个客户端发来的命令时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执 行事务
            如果客户端的REDIS_DIRTY_ CAS标识已经被打开,那么说明 客户端所监视的键当中,至少有 一个键已经被修改过了,在这种 情况下,客户端提交的事务已经 不再安全,所以服务器会拒绝执 行客户端提交的事务
            如果客户端的REDIS DIRTY CAS标识没有被打开,那么说明客户端监视的所有键都没有被修改过(或者客户端没有监视任何键),事务仍然是安全的,服务器将执行 客户端提交的这个事务

 

三、事务的ACID性质

因为事务的安全性和可靠性也是大家关注的焦点,所以本章最后将以常见的ACID性质 对Redis事务的原子性、一致性、隔离性和耐久性进行说明。

3.1原子性

 事务具有原子性指的是,数据库将事务中的多个操作当作一个整体来执行,服务器要么 就执行事务中的所有操作,要么就一个操作也不执行

Redis的事务和传统的关系型数据库事务的最大区别在于,Redis不支持事务回滚机制 (rollback),即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下 去,直到将事务队列中的所有命令都执行完毕为止

3.2 —致性

事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后, 无论事务是否执行成功,数据库也应该仍然是一致的。

3.2.1 入队错误

如果一个事务在人队命令的过程中,出现了命令不存在,或者命令的格式不正确等情 况,那么Redis将拒绝执行这个事务

3.2.2 执行错误

执行过程中发生的错误都是一些不能在入队时被服务器发现的错误,这些错误只会 在命令实际执行时被触发

在事务的执行过程中发生了错误,服务器也不会中断事务的执行,它会继续执 行事务中余下的其他命令,并且已执行的命令(包括执行命令所产生的结果)不会 被出错的命令影响。

3.2.3 服务器停机

如果服务器运行在无持久化的内存模式下,那么重启之后的数据库将是空白的,因 此数据总是一致的

如果服务器运行在RDB模式下,那么在事务中途停机不会导致不一致性,因为服务器可以根据现有的RDB文件来恢复数据,从而将数据库还原到一个一致的状态。如 果找不到可供使用的RDB文件,那么重启之后的数据库将是空白的,而空白数据库 总是一致的

如果服务器运行在AOF模式下,那么在事务中途停机不会导致不一致性,因为服务 器可以根据现有的AOF文件来恢复数据,从而将数据库还原到一个一致的状态。如 果找不到可供使用的AOF文件,那么重启之后的数据库将是空白的,而空白数据库 总是一致的

综上所述,无论Redis服务器运行在哪种持久化模式下,事务执行中途发生的停机都不会影响数据库的一致性

3.3 隔离性

即使数据库中有多个事务并发地执行,各个事务之间也不会互相 影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全相同

Redis使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证, 在执行事务期间不会对事务进行中断,因此,Redis的事务总是以串行的方式运行的,并且 事务也总是具有隔离性的

3.4 耐久性

当一个事务执行完毕时,执行这个事务所得的结果已经被保存到 永久性存储介质(比如硬盘)里面了,即使服务器在事务执行完毕之后停机,执行事务所得 的结果也不会丟失

Redis的事务不过是简单地用队列包裹起了一组Redis命令,Redis并没有为事务 提供任何额外的持久化功能,所以Redis事务的耐久性由Redis所使用的持久化模式决定

1.当服务器在无持久化的内存模式下运作时,事务不具有耐久性:一旦服务器停机. 包括事务数据在内的所有服务器数据都将丢失

2.当服务器在RDB持久化模式下运作时,服务器只会在特定的保存条件被满足时,才 会执行命令,对数据库进行保存操作,并且异步执行的不能保 证事务数据被第一时间保存到硬盘里面,因此RDB持久化模式下的事务也不具有耐 久性

3.当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时, 程序总会在执行命令之后调用同步(sync)函数,将命令数据真正地保存到硬盘里 面,因此这种配置下的事务是具有耐久性的。

4.当服务器运行在AOF持久化模式下,并且appendfsync选项的值为everysec 时,程序会每秒同步一次命令数据到硬盘。因为停机可能会恰好发生在等待同步的 那一秒钟之内,这可能会造成事务数据丢失,所以这种配置下的事务不具有耐久性

5.当服务器运行在AOF持久化模式下,并且appendfsync选项的值为no时,程序 会交由操作系统来决定何时将命令数据同步到硬盘。因为事务数据可能在等待同步 的过程中丢失,所以这种配置下的事务不具有耐久性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值