`

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分布式事务锁,以及在Spring Boot环境中如何进行集成。 首先,Redis作为一个内存数据库,其高速读写性能使其成为实现分布式锁的理想选择。分布式锁的主要作用是在多...

    剖析!Redis事务实现原理

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

    springboot-redis事务

    本文将深入探讨“SpringBoot-Redis事务”这一主题,旨在帮助开发者理解如何在SpringBoot项目中利用Redis进行事务操作。 首先,SpringBoot是Spring框架的一种快速启动方式,它简化了配置,提供了自动配置的功能,...

    redis事务_动力节点Java学院整理

    Redis 事务的实现主要集中在源码的 `multi.c` 文件中。`multiCommand` 函数负责开启事务,它会设置客户端的 `REDIS_MULTI` 标志。当客户端发送 `EXEC` 命令时,Redis 会检查事务中的所有命令,并依次执行。如果在...

    redis事务常用操作详解

    Redis 事务是数据库系统中的一种特性,允许用户一次性提交...例如,Redis事务不支持回滚操作,如果事务中的某条命令失败,后续命令仍会执行。因此,在设计使用Redis的事务时,需要考虑到这些限制并进行适当的错误处理。

    cpp-Redis分布式事务

    标题中的“cpp-Redis分布式事务”表明我们讨论的是如何在C++编程环境下利用Redis来实现分布式事务处理。Redis是一款开源的、基于键值对的数据存储系统,常用于缓存、消息队列以及分布式场景中。在大型游戏中,由于...

    redis常用命令,redis配置文件,redis持久化,redis事务,redis主从复制,jedis的使用

    下面是对 Redis 的常用命令、配置文件、持久化、事务、主从复制、Jedis 使用的详细讲解。 Redis 常用命令 Redis 提供了很多有用的命令来管理和操作数据。下面是一些常用的 Redis 命令: * SET key value:设置...

    redis事务1

    Redis 事务是 Redis 数据库提供的一种批量执行命令的机制,旨在提高数据操作的效率和原子性。虽然在传统的数据库系统中,事务具有ACID(原子性、一致性、隔离性和持久性)特性,但 Redis 事务并不完全具备这些特性。...

    《Redis设计和实现 黄建宏著》配套 Redis 3.0 中文注释版源码

    5. 事务:Redis支持简单的事务功能,允许多个命令作为一个组执行,确保原子性。 6. 模块系统:从Redis 3.0开始,引入了模块系统,允许开发者扩展Redis的功能,实现自定义数据类型和命令。 7. LRU(最近最少使用)...

    第7单元 Redis事务与持久化1

    【Redis事务】 Redis事务是Redis数据库中提供的一种确保数据操作原子性的机制,它允许用户在一个操作序列中执行多条命令,这些命令会被串行化并按顺序执行,以保证在并发环境下的数据一致性。事务的开启是通过`MULTI...

    redis事务处理:(error) EXECABORT

    然而,事务处理并非总是顺利进行,"redis事务处理:(error) EXECABORT" 是一个常见的错误提示,意味着在执行事务时遇到了问题。本文将深入探讨这个错误的原因以及如何解决。 一、Redis 事务基础 Redis 的事务机制...

    基于Redis实现分布式应用限流的方法

    【基于Redis实现分布式应用限流的方法】 限流是保护系统免受高并发访问或恶意攻击的重要手段,通过限制系统的处理速度或在特定时间窗口内处理的请求数量,防止系统资源耗尽导致服务崩溃。Redis,作为一款高效且广泛...

    springboot集成redis实现简单秒杀系统

    在这个系统中,我们实现了两个秒杀模式:通过Lua脚本实现的秒杀和通过Redis事务实现的秒杀。下面我们将详细介绍这两个秒杀模式。 通过Lua脚本实现的秒杀 在这个系统中,我们使用Lua脚本来实现秒杀的逻辑。Lua脚本...

    基于Redis方式实现分布式锁

    基于关系数据库实现分布式锁主要利用数据库本身的事务机制和锁定机制。在高并发场景下,这种方法可能因为数据库的I/O开销过大而不适用于大型项目。 #### 2. ZooKeeper 分布式锁 基于ZooKeeper实现分布式锁是通过...

    基于mq和redis实现的秒杀系统

    3. **事务处理**:在Redis中,可以利用`WATCH`命令实现分布式事务,保证数据操作的完整性。 4. **回滚策略**:当秒杀过程中出现问题,应有回滚机制恢复系统状态。 5. **监控与报警**:设置完善的监控体系,及时发现...

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

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

    keepalived + redis 实现双机热备

    标题 "keepalived + redis 实现双机热备" 涉及到的是在IT行业中构建高可用性系统的关键技术。keepalived 和 redis 是两个关键组件,它们一起工作以确保服务的持续运行,即使在主服务器故障时也能无缝切换到备份...

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

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

Global site tag (gtag.js) - Google Analytics