提到事务,相信大家都不陌生,事务的ACID四大特性,也是面试时经常问的,不过一般情况下,我们可能想到的是传统关系型数据库的事务,其实,Redis也是提供了事务机制的,本篇博客就来讲解下Redis的事务机制。

1. 事务演示

Redis的事务提供了一种将多个命令请求打包,然后一次性、按顺序性地执行多个命令的机制。

在事务执行期间,服务器不会中断事务而去执行其它客户端的命令请求,它会将事务中的所有命令执行完毕,然后才去处理其它客户端的命令请求。

下图展示了一个Redis事务的执行过程:

可以看出,事务以MULTI命令开始,然后将多个命令放到事务当中,最后由EXEC命令将这个事务提交给服务器执行。

2. 事务实现原理

一个事务从开始到结束会经历以下3个阶段:

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

2.1 事务开始

MULTI命令的执行标志着事务的开始。

执行完该命令后,客户端状态的flags属性会打开REDIS_MULTI标识,表示该客户端从非事务状态切换至事务状态。

2.2 命令入队

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

当一个客户端处于事务状态时,这个客户端发送的命令,服务器是否会立即执行,分为以下2种情况:

  1. 如果客户端发送的命令为MULTIEXECWATCHDISCARD四个命令中的其中1个,服务器会立即执行这个命令。
  2. 如果客户端发送的命令为以上4个命令外的其它命令,服务器不会立即执行这个命令,而是将其放到事务队列里,然后向客户端返回QUEUED回复。

以上流程可以使用以下流程图来表示:

这里首先提下DISCARD命令,这个命令用于取消事务,放弃执行事务块内的所有命令,如下所示:

然后提下事务队列,每个Redis客户端都有自己的事务状态,事务状态存储在客户端状态的mstates属性里:

事务状态包含1个事务队列和1个已入队命令的数量,如下所示:

事务队列是一个multiCmd类型的数组,数组中的每个multiCmd结构保存了一个已入队命令的相关信息,比如:

  1. 指向命令实现函数的指针,如GET命令、SET命令
  2. 命令的参数
  3. 参数的数量

事务队列以先进先出(FIFO)的方式保存入队的命令。

2.3 事务执行

当一个处于事务状态的客户端执行EXEC命令时,服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令(按先入先出顺序),然后将执行命令的结果一次性返回给客户端。

3. WATCH命令的实现原理

WATCH命令用于监视任意数量的数据库键,并在EXEC命令执行时,检测被监视的键是否被修改,如果被修改了,服务器将拒绝执行事务,并向客户端返回空回复。

为了更好的理解,我们做个演示,首先,我们打开客户端1,执行WATCH命令监视键“name”,然后开启一个事务:

此时,先不要执行EXEC命令,打开客户端2,执行以下命令修改“name”键的值:

然后,在客户端1执行EXEC命令时,会返回空回复,因为“name”键的值在客户端2已经被修改:

那么,WATCH命令的实现原理是什么样的呢?我们从以下3个方面来分析:

  1. 使用WATCH命令监视数据库键
  2. 监视机制的触发
  3. 判断事务是否安全

3.1 使用WATCH命令监视数据库键

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

举个例子,假如客户端1正在监视键“name”,客户端2正在监视键“age”,那么watched_keys字典存储的数据大概如下:

如果此时客户端3执行了以下WATCH命令:

那么watched_keys字典存储的数据就变为:

3.2 监视机制的触发

那么问题来了,既然watched_keys字典存储了被WATCH命令监视的键,那么监视机制是如何被触发的呢?

答案是所有对数据库修改的命令,比如SETLPUSHSADD等,在执行之后都会对watched_keys字典进行检查,如果有客户端正在监视刚刚被命令修改的键,那么所有监视该键的客户端的REDIS_DIRTY_CAS标识将被打开,表示该客户端的事务安全性已经被破坏。

以上图为例,如果键“name”的值被修改,那么客户端1、客户端3的REDIS_DIRTY_CAS标识会被打开。

3.3 判断事务是否安全

最后非常关键的一步是,当服务器接收到一个客户端发来的EXEC命令时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务,判断的流程图如下所示:

4. 事务执行失败举例

先来看第1个例子,这个事务因为命令入队出错被服务器拒绝执行,事务中的所有命令都不会被执行:

再来看第2个例子,事务入队时出现了不存在的命令,服务器将拒绝执行这个事务:

再来看第3个例子,RPUSH命令在执行期间报错了,但后续命令仍然继续执行,并且之前执行的命令没有受到任何影响:

这个例子也说明Redis事务不支持回滚机制

5. 总结

Redis的事务提供了一种将多个命令打包,然后一次性、有序地执行的机制,

它的原理是多个命令会被入队到事务队列中,然后按先进先出(FIFO)的顺序执行,

并且事务在执行过程中不会被中断,当事务队列中的所有命令都被执行完毕之后,事务才会结束。

6. 参考

黄健宏 《Redis设计与实现》


Redis系列(九):Redis的事务机制的更多相关文章

  1. Redis系列九 Redis集群

    1. redis-cluster架构图 redis-cluster投票:容错 架构细节 ①所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. ②节点的fai ...

  2. Redis系列(二):Redis的数据类型及命令操作

    原文链接(转载请注明出处):Redis系列(二):Redis的数据类型及命令操作 Redis 中常用命令 Redis 官方的文档是英文版的,当然网上也有大量的中文翻译版,例如:Redis 命令参考.这 ...

  3. Redis系列(一):Redis的简介与安装

    原文链接(转载请注明出处):Redis系列(一):Redis的简介与安装 什么是 Redis Redis 是一个使用ANSI C 编写的开源.支持网络协议.基于内存.可选持久性的键值对数据库,它是一个 ...

  4. Redis系列一 Redis安装

    Redis系列一    Redis安装 1.安装所使用的操作系统为Ubuntu16.04 Redis版本为3.2.9 软件一般下载存放目录为/opt,以下命令操作目录均为/opt root@ubunt ...

  5. Redis系列(九)--几道面试题

    这里只是一点面试题,想了解更多,可以查看本人的Redis系列:https://www.cnblogs.com/huigelaile/category/1461895.html 1.Redis和Memc ...

  6. Redis系列之----Redis的两种持久化机制(RDB和AOF)

    Redis的两种持久化机制(RDB和AOF) 什么是持久化    Redis的数据是存储在内存中的,内存中的数据随着服务器的重启或者宕机便会不复存在,在生产环境,服务器宕机更是屡见不鲜,所以,我们希望 ...

  7. 深入剖析Redis系列: Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  8. redis系列:redis介绍与安装

    前言 这个redis系列的文章将会记录博主学习redis的过程.基本上现在的互联网公司都会用到redis,所以学习这门技术于你于我都是有帮助的. 博主在写这个系列是用的是目前最新版本4.0.10,虚拟 ...

  9. Redis系列之----Redis的数据类型及使用场景

       Redis是一个开源的.高性能的.基于键值对的缓存与存储系统,能够提供多种不同的键值数据类型来适应不同场景下的缓存和存储需求.    Redis中所有的数据都存储在内存中,因此读写速度非常快,相 ...

随机推荐

  1. 第三方登陆---GITEE

    第三方登陆QQ通行入口 https://www.cnblogs.com/Yangbuyi/p/13194007.html 呼~~~~ 应身边的同学要集成第三方登陆 gitee.github.qq登陆. ...

  2. 前端笔记(创建顺序数组、取选中月最后一天日期、判断变量、git命令)

    创建一个从0开始的顺序数组 [...new Array(5).keys()] //[0,1,2,3,4] 数组反向 [0,1,2,3,4,5].reverse() //[4,3,2,1,0] 取选中月 ...

  3. Python18行代码做出来这样有点浪漫,又有点极客的“内涵”图

    今天就来教大家这个套路: 如何使用 Python 来做出来这样有点浪漫,又有点极客的“内涵”图. 当然,能不能靠它得到心仪对象的青睐,就要靠你(命)了.(๑•́₃ •̀๑) 那么要怎么做呢? 我们先找 ...

  4. scrapy框架携带cookie访问淘宝购物车

    我们知道,有的网页必须要登录才能访问其内容.scrapy登录的实现一般就三种方式. 1.在第一次请求中直接携带用户名和密码. 2.必须要访问一次目标地址,服务器返回一些参数,例如验证码,一些特定的加密 ...

  5. Python爬虫教程(16行代码爬百度)

    最近在学习python,不过有一个正则表达式一直搞不懂,自己直接使用最笨的方法写出了一个百度爬虫,只有短短16行代码.首先安装必背包: pip3 install bs4 pip3 install re ...

  6. 龙芯开源社区上线.NET主页

    龙芯团队从2019年7 月份开始着手.NET Core的MIPS64支持研发,经过将近一年的研发,在2020年6月18日完成了里程碑性的工作,在github CoreCLR 仓库:https://gi ...

  7. HTML5提高

    HTML5提高 前言 我个人觉得,当你学会了一些最基本的标签其实是够用的,但是在很多网页中可以发现很多新的标签.这个时候不知道它是干嘛的实际上心里是非常没底的,所以在这里我打算写一篇HTML5提高的文 ...

  8. Mysql基础(三):MySQL基础数据类型、完整性约束、sql_mode模式

    目录 2.MySQL基础数据类型.完整性约束.sql_mode模式 1. MySQL常用数据类型 2. 完整性约束 3. MySQL的sql_mode模式说明以及设置 2.MySQL基础数据类型.完整 ...

  9. python面试题六: 剑指offer

    面试题3 二维数组中的查找 LeetCode题目:二维数组中,每行从左到右递增,每列从上到下递增,给出一个数,判断它是否在数组中思路:从左下角或者右上角开始比较 def find_integer(ma ...

  10. 数据可视化之powerBI基础(十八)Power BI度量值的格式如何修改?这里有三种方式

    https://zhuanlan.zhihu.com/p/99818030 ​在知识星球遇到的问题中,有不少朋友问如何修改数值型度量值的格式,比如,新建的度量值如何显示为百分比或者千分位等等,这篇文章 ...