事务

 


前言

Redis通过MULTI、EXEC、WATCH等命令来实现事务。事务提供了一种将多个命令请求打包,然后一次行、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务。

127.0.0.1:6379> KEYS *
(empty list or set)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 2
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
127.0.0.1:6379> KEYS *
1) "b"
2) "a"


 

一、事务的实现

事务阶段

  1. 事务开始
  2. 命令入队
  3. 事务执行

1. 事务开始

MULTI标志着事务的开始:

MULTI命令可以将执行该命令的额客户端从非事务状态切换到事务状态,通过设置客户端状态flags的REDIS_MULTI标识。

2. 命令入队

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

当客户端处于事务状态时,服务器会根据这个客户端发来的不同命令执行不同的操作:

  • 客户端发送EXEC、DISCARD、WATCH、MULTI,服务器立即执行;
  • 其他,服务器不立即执行,而是将命令放入一个队列里,然后向客户端返回QUEUED。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> MULTI
(error) ERR MULTI calls can not be nested
127.0.0.1:6379> KEYS *
QUEUED
127.0.0.1:6379> EXEC
1) 1) "b"
   2) "a"

3. 事务队列

每个Redis客户端都有自己的事务状态,保存在客户端状态的mstate:

事务状态包含一个事务队列,以及一个已入队命令的计数器:

4. 执行事务

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

二、WATCH命令的实现

WATCH命令是一个乐观锁,他可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否被修改过了,如果是,服务器将拒绝执行事务,并向客户端返回空回复。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET a 2
QUEUED
127.0.0.1:6379>
127.0.0.1:6379> EXEC
(nil)

 

127.0.0.1:6379> set a 3
OK

1.使用WATCH命令监视数据库键

每个Redis数据库都保存着一个watched_keys字典,这个字典的键是被WATCH命令监视的数据库键,而字典的值则是一个链表,链表中记录了所有监视相应数据库键的客户端:

2.监视机制的触发

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

3.判断事务是否安全

当服务器收到客户端发来的EXEC时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务:

4.一个完整的WATCH事务执行过程

三、事务的ACID性质

1. 原子性

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

Redis事务功能指。事务队列中的命令要么全部执行,要么就一个都不执行。因此,Redis的事务是具有原子性的。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> get
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

127.0.0.1:6379> get a
"3"

Redis的事务和传统数据库事务的区别是,Redis不支持事务回滚机制(rollback),即使事务队列中的某个命令在执行期间出现了错误,事务的后续命令也会继续执行,并且之前执行的命令也不会有任何影响。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> get a
QUEUED
127.0.0.1:6379> set a b c
QUEUED
127.0.0.1:6379> get a
QUEUED
127.0.0.1:6379> set b 222
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) "1"
3) (error) ERR syntax error
4) "1"
5) OK


127.0.0.1:6379> get b
"222"

Redis的作者在事务功能的文档中解释说,不支持事务回滚是因为Redis追求简单高效。并且他认为,Redis事务的执行时错误通常都是编程错误产生的,通常只会出现在开发环境中,所以没有必要开发事务回滚功能。

2. 一致性

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

“一致”指的是数据符合数据库本身的定义和要求,没有包含非法或者无效的数据错误。

Redis通过谨慎的错误检测和简单的设计来保证事务一致性。

2.1 入队错误

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

因为服务器会拒绝执行入队过程中出现错误的事务,所以Redis事务的一致性不会被带有入队错误的事务影响。

2.2 执行错误

  • 执行过程中发生的错误都是一些不能在入队时被服务器发现的错误,这能字啊实际执行时被触发;
  • 即使在事务执行中发生了错误,服务器也不会中断事务的执行,会继续执行事务中余下的命令,并且已经执行的命令不会被出错的命令影响。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set msg 1
QUEUED
127.0.0.1:6379> get msg
QUEUED
127.0.0.1:6379> RPUSH msg 2 3
QUEUED
127.0.0.1:6379> get msg
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) "1"
3) (error) WRONGTYPE Operation against a key holding the wrong kind of value
4) "1"

2.3 服务器停机

如果Redis服务器在执行事务的过程中停机,那么根据服务器所使用的持久化模式,可能有以下情况出现:

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

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

3.隔离性

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

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

4.耐久性

事务耐久性指的是,当一个事务执行完毕时,执行这个事务所得的结果已经被保存到永久性存储介质里了,即使服务器在事务执行完毕后停机,执行事务所得的结果也不会丢失。

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

  • 当服务器在无持久化的内存模式下运作时,事务不具有耐久性:一旦服务器停机、包括事务数据在内的所有服务器数据都将丢失;
  • 当服务器在RDB持久化模式下运作时,服务器只会在特定的保存条件被满足时,才会执行BGSAVE,对数据库进行保存操作,并且异步执行的BGSAVE不能保证事务数据被第一时间保存到硬盘里,因此RDB持久化模式下的事务也不具有耐久性;
  • 当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,程序总会在执行命令之后调用同步(sync),将命令数据真正的保存到硬盘里,因此这种配置下的事务是具有耐久性的;
  • 当服务器运行在AOF持久化模式下,并且appendfsync选项的值为everysec时,程序会每秒同步一次命令数据到硬盘。这种配置下的事务不具有耐久性;
  • 当服务器运行在AOF持久化模式下,并且appendfsync选项的值为no时,程序会交由操作系统来决定何时将命令数据同步到硬盘。因为事务数据可能在等待同步过程中丢失,所以这种配置下的事务不具有耐久性。

不论Redis在什么模式下运作,在一个事务最后加上SAVE总可以保证事务的耐久性,不过效率太低,不具有实用性。


总结

  • 事务提供了一种将多个命令打包,然后一次性、有序的执行机制;
  • 多个命令会被入队到事务队列中,然后按FIFO的顺序执行;
  • 事务在执行过程中不会被中断,当事务队列中的所有命令都被执行完毕后,事务才会结束;
  • 带有WATCH命令的事务会将客户端和被监视的键在数据库的watched_keys字典中进行关联,当键被修改时,程序会将所有监视被修改键的客户端的REDIS_DIRTY_CAS标志打开;
  • 只有在客户端的REDIS_DIRTY_CAS标志未被打开时,服务器才会执行客户端提交的事务,否则的话,服务器将拒绝执行客户端提交的事务;
  • Redis的事务总是具有ACID中的原子性、一致性和隔离性,当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,事务也具有耐久性。

 

参考资料

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值