08 事务
事务
1 事务简介
Redis通过MULTI、EXEC、WATCH等命令实现事务(transaction)功能。事务提供一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制。在事务执行期间,服务器不会中断事务去执行其他客户端的命令请求。
事务以MULTI开始,接着是多个命令放入事务之中,最后由EXEC将这个事务提交(commit)到服务器执行。
一个事务包含了多个命令,服务器在执行事务期间,不会改去执行其它客户端的命令请求。
事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为流水线,它可以减少客户端与服务器之间的网络通信次数从而提升性能。
Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来。
事务队列
每个Redis客户端都有自己的事务状态,保存在客户端状态的mstate属性中:
1 | typedef struct redisClient { |
事务实现
一个事务从开始到结束经历三个阶段:
- 事务开始
- 命令入队
- 事务执行
事务开始
MULTI命令标志着事务的开始,它将客户端从非事务状态切换到事务状态,即打开客户端状态的flags属性的REDIS_MULTI标识:
1 | def MULTI(): |
命令入队
客户端切换到事务状态后,服务器会根据不同的命令执行不同的操作:
EXEC、DISCARD、WATCH、MULTI其中一个,服务器立即执行该命令。- 否则,服务器将命令放入一个事务队列,然后向客户端返回
QUEUED回复。
执行事务
服务器收到EXEC命令后,会遍历客户端的事务列表,执行其中的所有命令。最后将执行所得的结果返回给客户端。
1 | def EXEC(): |
2 WATCH命令的实现
WATCH命令是个乐观锁,它可以再EXEC执行之前,监视任意数量的数据库键,并在EXEC执行时,检查被监视的键是否至少有一个已经被修改过了。如果是,服务器将拒绝执行事务,并返回客户端事务执行失败的空回复。
使用 WATCH 命令监视数据库键
每个Redis数据库都保存了一个watched_keys字典,键是某个被WATCH的数据库键,值是一个链表,记录了所有监视该键的客户端:
1 | typedef struct redisDb { |
监视机制的触发
所有对数据库进行修改的命令,执行之后都会调用multi.h/touchWatchKey函数对watched_keys字典进行检查。如果被监视的键被修改,那么打开监视该键的客户端的REDIS_DIRTY_CAS标识,表示该客户端的事务安全性已遭破坏。
判断事务是否安全
服务器收到EXEC命令后,根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务。
悲观锁与乐观锁
我正在买票ticket -1 , money -100而票只有1张, 如果在我multi之后,和exec之前, 票被别人买了,即ticket变成0了.我该如何观察这种情景,并不再提交
悲观的想法:
世界充满危险,肯定有人和我抢, 给 ticket上锁, 只有我能操作. [悲观锁]
乐观的想法:
没有那么人和我抢,因此,我只需要注意,
–有没有人更改ticket的值就可以了 [乐观锁]
Redis的事务中,启用的是乐观锁,只负责监测key没有被改动
1 |
|
3 事务的ACID性质
Redis的事务总是具有原子性(atomicity)、一致性(consistency)、隔离性(isolation),且当Redis运行在某种特定的持久化模式下,事务也具有耐久性(durability)。
原子性
事务的原子性是指,事务中的多个操作当做一个整体来执行,要么执行所有,要么一个也不执行。
Redis的事务与传统关系型数据库事务的区别在于,Redis不支持事务的回滚机制(rollback),即使事务队列中的某个命令执行出现错误,整个事务也会继续执行下去,直到所有命令执行完毕。
一致性
事务的一致性是指,如果数据库在事务执行前是一致的,那么执行后,无论事务是否执行成功,数据库也应该是一致的。「一致」是数据符合数据库本身的定义和要求,没有包含非法或无效的错误数据。
Redis通过谨慎的错误检测和简单的设计来保证事务的一致性。
入队错误。
如果事务在入队命令的过程中,出现了命令不存在,或者命令格式不正确等情况,Redis会拒绝执行该事务。执行错误。
执行过程中的错误是不能再入队时被服务器发现的,这些错误只会在命令实际执行时被触发。事务的执行过程中出现错误,服务器也不会中断事务的执行,而是继续执行其他命令,一致性的命令不会被出错的命令影响。服务器停机。
执行事务的过程中停机,不管服务器使用的何种持久化模式,Redis总能保持重启后的数据库一致性。
隔离性
事务的隔离性是指,即使数据库中有多个事务并发执行,各个事务之间不会相互影响,且与串行执行的结果相同。
Redis采用单线程执行事务,所以事务总是以串行的方式执行,也当然具有隔离性。
持久性
事务的持久性是指,一个事务执行完毕后,结果已经被保存到永久性存储介质中。即使服务器停机,执行事务所得的结果也不会丢失。
Redis没有为事务提供额外的持久化功能,事务的持久化由Redis使用的持久化模式决定的:
- 无持久化:事务不具持久性,一旦停机,所有服务器的数据都将丢失。
- RDB持久化:只有执行
BGSAVE才会对数据库进行保存,且异步执行的BGSAVE不能保证事务数据在第一时间被保存。因此RDB持久化也不能保证事务的持久性。 - AOF持久化,且
appendfsync选项为always时:程序执行命令后会调用同步操作,将命令数据保存到硬盘。这时事务是有持久性的。 - AOF持久化,且
appendfsync选项为everysec时:每秒一次同步命令数据到硬盘,事务也不具有持久性。 - AOF持久化,且
appendfsync选项为no时:程序交由操作系统来决定何时同步到硬盘,事务也不具有持久性。










