作者 | Video++极链科后端Team刘聪
整理 | 包包
所谓事务(Transaction) ,是指作为单个逻辑工作单元执行的一系列操作。事务必须满足ACID原则(原子性、一致性、隔离性和持久性)。简单来说事务其实就是打包一组操作(或者命令)作为一个整体,在事务处理时将顺序执行这些操作,并返回结果,如果其中任何一个环节出错,所有的操作将被回滚。
在Redis中实现事务主要依靠以下几个命令来实现:
Redis事务从开始到结束通常会通过三个阶段:
1.事务开始
2.命令入队
3.事务执行
以下是一个最简单的Redis事务流程:
第一步跟其他的关系型数据库类似,也是需要开启一个事务,在Redis中的命令如下:
Redis中使用MULTI命令标记事务的开始,可以理解为在传统关系型数据库中的BEGIN TRANCATION语句,Redis将执行该命令的客户端从非事务状态切换成事务状态,这一切换是通过在客户端状态的flags属性中打开REDIS_MULTI标识完成, 我们看下Redis中对应部分的源码实现:
在打开事务标识的客户端里,这些命令都会被暂存到一个命令队列里,不会因为用户会的输入而立即执行。
第二步就是执行事务内路基,即真正的业务逻辑:
最后一个阶段是提交事务(或者回滚事务):
这两个命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。
这里需要注意的是,在客户端打开了事务标识后,只有命令:EXEC,DISCARD,WATCH,MULTI命令会被立即执行,其它命令服务器不会立即执行,而是将这些命令放入到一个事务队列里面,然后向客户端返回一个QUEUED回复 ;Redis客户端有自己的事务状态,这个状态保存在客户端状态mstate属性中,mstate的结构体类型是multiState,我们看下multiState的定义:
我们再看下结构体类型multiCmd的结构:
事务队列以先进先出的保存方法,较先入队的命令会被放到数组的前面,而较后入队的命令则会被放到数组的后面。
当开启事务标识的客户端发送EXEC命令的时候,服务器就会执行,客户端对应的事务队列里的命令,我们来看下EXEC 的实现细节:
最后我们再回顾一下事务本身的特性, 在传统关系型数据库中的事务必须依靠ACID来保证事务的可靠性和安全性,在Redis中事务总是具有一致性(Consistency)和隔离性(Isolation),并且当Redis运行在某种特定的持久化模式下,事务也具有耐久性(Durability); 但是并不总是能够保证原子性(Atomicity),在正常状态下一个事务的所有命令是能按照原子性的原则执行的,但是执行的中途遇到错误,不会回滚,而是继续执行后续命令, 如下:
如果在set k2 v2处失败,set k1已成功不会回滚,set k3还会继续执行;Redis的事务和传统的关系型数据库事务的最大区别在于,Redis不支持事务的回滚机制,即使事务队列中的某个命令在执行期间出现错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止,我们看下面的例子:
Redis的作者在事务功能的文档中解释说,不支持事务回滚是因为这种复杂的功能和Redis追求的简单高效的设计主旨不符合,并且他认为,Redis事务的执行时,错误通常都是编程错误造成的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他认为没有必要为Redis开发事务回滚功能。所以我们在讨论Redis事务回滚的时候,一定要区分命令发生错误的时候。
领取专属 10元无门槛券
私享最新 技术干货