`

Redis事务的实现

阅读更多
        Redis 事务是通过 MULTI、EXEC、WATCH 等命令来实现的。本节接下来会对这些命令的实现细节进行一一展开。
        一个 Redis 事务从开始到结束通常会经历以下三个阶段:
        1)事务开始。MULTI 命令的执行标志着事务的开始。执行该命令的客户端会从非事务状态切换至事务状态,这一切换是通过打开客户端状态的 flags 属性中的 REDIS_MULTI 标识来完成的。
        2)命令入队。当一个客户端切换到事务状态后,如果客户端发送的命令为 EXEC、DISCARD、WATCH 和 MULTI 四个命令中的其中一个,那么服务器会立即执行这个命令;否则,服务器将之放入一个事务队列里面,然后向客户端返回 QUEUED 回复。
        3)事务执行。当一个处于事务状态的客户端向服务器发送 EXEC 命令时,服务器就会执行该客户端事务队列中的所有命令,最后再将执行结果按顺序全部返回给客户端。此外,它还会清除客户端的 REDIS_MULTI 标识,让其回到非事务状态,同时清空事务队列数据。

        事务队列
        每个 Redis 客户端都将自己的事务状态保存在 redisClient.mstate 属性里面:
typedef struct redisClient{
    multiState mstate;               // MULTI/EXEC state
    /* other fields ...*/
} redisClient;

typedef struct multiState{
    multiCmd *commands;              // 事务队列,FIFO 顺序
    int count;                       // 已入队命令计数
} multiState;

typedef struct multiCmd{
    robj **argv;                     // 参数
    int argc;                        // 参数数量,包含命令名
    struct redisCommand *cmd;        // 命令指针
} multiCmd;

        事务队列中的每个 multiCmd 结构都保存了一个已入队命令的相关信息,包括指向命令实现函数的指针、命令的参数,以及参数的数量。

        WATCH 命令的实现
        WATCH 命令是一个乐观锁(optimistic locking),它可以在事务执行前监视任意数量的数据库键,并在事务执行时检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回事务执行失败的空回复,如下代码片段所示:
redis> WATCH "name"
OK
redis> SET "name" "john"      # 模拟其他客户端修改了这个 name 键
OK
redis> MULTI
OK
redis> SET "name" "peter"
QUEUED
redis> EXEC
(nil)
redis> GET "name"
"john"

        每个 Redis 数据库都保存着一个 watched_keys 字典,它的键是被 WATCH 命令监视的数据库键,而值则是一个记录了所有监视该数据库键的客户端链表。通过这个字典,服务器就可以清楚地知道哪些数据库键正在被监视,以及对应的监视客户端。
typedef struct redisDb{
    dict *watched_keys;        // 正在被 WATCH 命令监视的键
    /* other fields ...*/
} redisDb;

        所有对数据库进行修改的命令(比如 SET、LPUSH 等),在执行后都会检查 watched_keys 字典,如果发现有客户端正在监视刚刚修改过的数据库键,则会打开该客户端的 REDIS_DIRTY_CAS 标识,表示该客户端的事务安全性已经被破坏。当服务器收到客户端发来的 EXEC 命令时,它就会根据该客户端是否打开了 REDIS_DIRTY_CAS 标识来决定是否执行事务。

        Redis 事务的 ACID 性质
        在传统的关系型数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有耐久性(Durability)。
        Redis 的事务和传统的关系型数据库事务的最大区别在于,Redis 不支持事务回滚机制,即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将其中的所有命令都执行完毕为止。
        事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论是否执行成功,数据库也应该仍然是一致的。这里的“一致”的意思是数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据。
        Redis 通过谨慎的错误检测和简单的设计来保证事务的一致性。Redis 事务中可能出错的地方主要有以下三处:
        1)入队错误。如果一个事务在入队命令的过程中,出现了命令不存在,或者命令的格式不正确等情况,则最终 Redis 将拒绝执行这个事务(这种情况下,Redis 2.6.5 以前的版本依然会执行事务队列中那些正确的命令)。
        2)执行错误。事务在执行过程中发生的错误都是一些不能在入队时发现的错误(比如将一种类型的键当成另一种类型的键来操作),这些错误只能在命令实际执行时触发。不过如上所述,即使事务在执行过程中发生了错误,服务器也不会中断事务的执行,而是会继续执行余下的其他命令。
        3)服务器停机。如果 Redis 服务器在执行事务的过程中停机,那么不管服务器是否使用了持久化模式,都不会影响数据库的一致性。因为如果服务器运行在无持久化的内存模式下,则重启后的数据库将是空白的,而空白数据库总是一致的。反之,如果服务器运行在 RDB 或者 AOF 模式下,则服务器重启时可以根据现有的 RDB 文件或者 AOF 文件来恢复数据,从而将数据库还原到一个一致的状态。如果找不到可供使用的 RDB 文件或者 AOF 文件,重启后的数据库就会是空白的,所以依然是一致的。
        事务的隔离性指的是,数据库中即使有多个事务并发执行,相互之间也不会影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全相同。因为 Redis 使用单线程的方式来执行事务,并且服务器保证,在事务执行期间不会被中断。因此,Redis 的事务总是以串行的方式运行的,自然总是具有隔离性。
        关于事务的耐久性,则是指当一个事务执行完毕时,所得的结果已经被保存到永久性存储介质(如硬盘)里面了,即使服务器在事务执行完毕后停机,执行事务所得的结果也不会丢失。因为 Redis 的事务只是简单地用队列包裹了一组 Redis 命令,并没有为事务提供任何额外的持久化功能,所以 Redis 事务的耐久性由 Redis 所使用的持久化模式决定。不过即使使用了持久化模式,也只有服务器运行在 AOF 持久化模式,并且 appendfsync 选项的值为 always 时,Redis 事务才具有耐久性。因为这种情况下,程序总会在执行命令后调用同步(sync)函数,将命令数据真正地保存到硬盘中(然而当打开了 no-appendfsync-on-rewrite 配置选项时,在执行 BGSAVE 或者 BGREWRITEAOF 命令期间,服务器会暂时停止同步 AOF 文件,以尽可能地减少 I/O 阻塞。这样一来,事务结果也就可能丢失,从而也就不具有耐久性了)。而当服务器运行在 RDB 持久化模式,或者运行在 AOF 持久化模式,但 appendfsync 选项的值不为 always 时,服务器都需要在特定的保存条件满足后才会执行 BGSAVE 命令或者执行同步,这些都不能保证事务数据在第一时间保存到硬盘里面,所以可能会造成事务数据丢失,因而都不具有耐久性。不过不论在什么模式下运行,在一个事务的最后(即执行 EXEC 命令之前)加上 SAVE 命令都总可以保证事务的耐久性,但因为这种做法效率太低,所以不太具有实用性。


参考书籍:
1、《Redis设计与实现》第 19 章——事务。
分享到:
评论

相关推荐

    记录redisson实现redis分布式事务锁

    自己封装redisson方法,同时通过注解的方式加入redis分布式事务锁,可靠。

    剖析!Redis事务实现原理

    在Redis中实现事务主要依靠以下几个命令来实现:Redis事务从开始到结束通常会通过三个阶段:1.事务开始2.命令入队3.事务执行以下是一个最简单的Redis事务流程:第一步跟其他的关系型数据库类似,也是需要开启一个事务...

    cpp-Redis分布式事务

    在大型游戏中经常使用分布式,分布式中因为游戏逻辑会经常游戏事务,借助redis某些特性我们可以实现分布式锁和分布式事务。

    基于rabbitmq+redis实现分布式事务项目源码.zip

    基于rabbitmq+redis实现分布式事务项目源码.zip 基于rabbitmq+redis实现分布式事务项目源码.zip 基于rabbitmq+redis实现分布式事务项目源码.zip 基于rabbitmq+redis实现分布式事务项目源码.zip 基于rabbitmq+redis...

    【高频 Redis 面试题】Redis 事务是否具备原子性?

    一、Redis 事务的实现原理 一个事务从开始到结束通常会经历以下三个阶段: 1、事务开始 客户端发送 MULTI 命令,服务器执行 MULTI 命令逻辑。 服务器会在客户端状态(redisClient)的 flags 属性打开 REDIS_MULTI ...

    基于Redis实现的简单消息队列

    基于Redis实现的简单消息队列。针对队列事务一致性等要求不高的情况下,也可以使用Redis消息队列实现服务间的通讯

    Redis面试题50道(含答案)_.pdf

    34、你知道有哪些 Redis 分区实现方案? 35、Redis 分区有什么缺点? 36、Redis 持久化数据和缓存怎么做扩容? 37、分布式 Redis 是前期做还是后期规模上来了再做好?为 什么? 38、Twemproxy 是什么? 39、支持一致...

    尚硅谷_Redis.docx

    在本课程中,你将了解Redis是什么、能干什么、如何用,了解NoSQL的使用场景和概念,快速掌握Redis的安装配置、五大数据类型、常用操作命令、Redis持久化、主从复制、事务控制以及用Jedis操作进行Java开发等知识。...

    redis 分布式锁实现案例和源码解析备注

    redis 分布式锁实现案例和源码解析备注 * 多线程 * 使用redis事务的方法 * 加事务 乐观锁 * watch命令监控key有没有更改 * multi命令开启事务

    2023年Redis缓存面试题目汇总

    在什么情况下你会使用Redis事务? Redis事务的ACID性质是什么? Redis的Lua脚本有什么作用? 你如何理解Redis的发布/订阅功能? Redis的排行榜功能是如何实现的? Redis的缓存失效策略是什么? 在分布式环境下,如何...

    redis window最新免安装版本

    Redis 具有内置复制、 Lua 脚本编写、 LRU 垃圾清理、事务处理和不同级别的磁盘持久性,并通过 Redis Sentinel 提供高可用性和使用 Redis Cluster 自动分区。 为了实现最佳性能,Redis 使用内存中的数据集。根据用例...

    springboot+redis+rebbitmq实现商品秒杀.zip

    springboot+redis+rebbitmq实现商品秒杀demo,通过redid缓存减少数据库访问,rebbitmq消息队列异步下单,极大提高了系统的吞吐量,其中也包含redis分布式锁、redis+watch事务、redis+lua等方式实现的适用于小并发量...

    尚硅谷Redis入门视频

    在本课程中,你将了解Redis是什么、能干什么、如何用,了解NoSQL的使用场景和概念,快速掌握Redis的安装配置、五大数据类型、常用操作命令、Redis持久化、主从复制、事务控制以及用Jedis操作进行Java开发等知识。...

    基于RabbitMQ+Redis的分布式事务框架

    使用RabbitMQ和Redis实现的分布式事务框架。主要涉及AOP思想、多线程,使用注解实现事务的提交和回滚。内含基于Spring Cloud的使用案例。

    redis入门指南2

    《Redis入门指南(第2版)》是一本Redis的入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史与特性、在开发和生产环境中部署运行Redis、数据类型与命令、使用Redis实现队列、事务、复制、...

    redis面试复习.xmind

    # redis事务 ### 一个简单的介绍并说明了一下resdis事物的不保证原子性 # redis集群 ### redis集群三种模式 ### Redis哈希槽 ### 什么情况下会导致整个集群不可用 ### 集群三种方案之间都有哪些区别,其优点和缺点是...

    redisStudy.zip

    redis可以说是半支持事务(假事务),提供了一些在一定程度上支持线程安全和事务的命令。例如:multi/exec watch inc等。但是redis的事务并不支持回滚,即可以两个命令可以同时提交执行,但是如果有失败,成功的也...

    Redis面试题.pdf

    请说明Redis事务的特点和使用方式。 9. Redis集群是如何实现高可用性和横向扩展的?请描述Redis集群的工作原理。 10. Redis如何处理并发访问和数据竞争问题?请谈谈Redis的并发控制机制。 11. Redis中的管道...

    redis入门指南

    《Redis入门指南》是一本Redis的入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史与特性、在开发和生产环境中部署运行Redis、数据类型与命令、使用Redis实现队列、事务、复制、管道、持久...

Global site tag (gtag.js) - Google Analytics