Redis事务实现原理
一:简介
Redis事务通常会使用MULTI,EXEC,WATCH等命令来完成,redis实现事务实现的机制与常见的关系型数据库有很大的却别,比如redis的事务不支持回滚,事务执行时会阻塞其它客户端的请求执行。
二:事务实现细节
redis事务从开始到结束通常会通过三个阶段:
1.事务开始
2.命令入队
3.事务执行
我们从下面的例子看下
redis> MULTI
OKredis > SET "username" "bugall"
QUEUEDredis > SET "password" 161616 QUEUED redis > GET "username" redis > EXEC 1) ok 2) "bugall" 3) "bugall"
redis >MULTI
标记事务的开始,MULTI命令可以将执行该命令的客户端从非事务状态切换成事务状态,这一切换是通过在客户端状态的flags属性中打开REDIS_MULTI标识完成,我们看下redis中对应部分的源码实现
void multiCommand(client *c) {if (c->flags & CLIENT_MULTI) {addReplyError(c,"MULTI calls can not be nested"); return; } c->flags |= CLIENT_MULTI; //打开事务标识 addReply(c,shared.ok); }
在打开事务标识的客户端里,这些命令,都会被暂存到一个命令队列里,不会因为用户会的输入而立即执行
redis> SET "username" "bugall"
redis > SET "password" 161616 redis > GET "username"
执行事务队列里的命令。
redis> EXEC
这里需要注意的是,在客户端打开了事务标识后,只有命令:EXEC,DISCARD,WATCH,MULTI命令会被立即执行,其它命令服务器不会立即执行,而是将这些命令放入到一个事务队列里面,然后向客户端返回一个QUEUED回复,redis客户端有自己的事务状态,这个状态保存在客户端状态mstate属性中,mstate的结构体类型是multiState,我们看下multiState的定义
typedef struct multiState {multiCmd *commands;//存放MULTI commands的数组int count; //命令数量
} multiState;
我们再看下结构体类型multiCmd的结构
typedef struct multiCmd {robj **argv; //参数int argc; //参数数量 struct redisCommand *cmd; //命令指针 } multiCmd;
事务队列以先进先出的保存方法,较先入队的命令会被放到数组的前面,而较后入队的命令则会被放到数组的后面.
三:执行事务
当开启事务标识的客户端发送EXEC命令的时候,服务器就会执行,客户端对应的事务队列里的命令,我们来看下EXEC的实现细节
void execCommand(client *c) {int j;robj **orig_argv;int orig_argc; struct redisCommand *orig_cmd; int must_propagate = 0; //同步持久化,同步主从节点 //如果客户端没有开启事务标识 if (!(c->flags & CLIENT_MULTI)) { addReplyError(c,"EXEC without MULTI"); return; } //检查是否需要放弃EXEC //如果某些被watch的key被修改了就放弃执行 if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC)) { addReply(c, c->flags & CLIENT_DIRTY_EXEC ? shared.execaborterr : shared.nullmultibulk); discardTransaction(c); goto handle_monitor; } //执行事务队列里的命令 unwatchAllKeys(c); //因为redis是单线程的所以这里,当检测watch的key没有被修改后就统一clear掉所有的watch orig_argv = c->argv; orig_argc = c->argc; orig_cmd = c->cmd; addReplyMultiBulkLen(c,c->mstate.count); for (j = 0; j < c->mstate.count; j++) { c->argc = c->mstate.commands[j].argc; c->argv = c->mstate.commands[j].argv; c->cmd = c->mstate.commands[j].cmd; //同步主从节点,和持久化 if (!must_propagate && !(c->cmd->flags & CMD_READONLY)) { execCommandPropagateMulti(c); must_propagate = 1; } //执行命令 call(c,CMD_CALL_FULL); c->mstate.commands[j].argc = c->argc; c->mstate.commands[j].argv = c->argv; c->mstate.commands[j].cmd = c->cmd; } c->argv = orig_argv; c->argc = orig_argc; c->cmd = orig_cmd; //取消客户端的事务标识 discardTransaction(c); if (must_propagate) server.dirty++; handle_monitor: if (listLength(server.monitors) && !server.loading) replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc); }
四:watch/unwatch/discard
watch:
命令是一个乐观锁,它可以在EXEC命令执行之前,监视任意数量的数据库键,并在执行EXEC命令时判断是否至少有一个被watch的键值
被修改如果被修改就放弃事务的执行,如果没有被修改就清空watch的信息,执行事务列表里的命令。
unwatch:
顾名思义可以看出它的功能是与watch相反的,是取消对一个键值的“监听”的功能能
discard:
清空客户端的事务队列里的所有命令,并取消客户端的事务标记,如果客户端在执行事务的时候watch了一些键,则discard会取消所有
键的watch.
五:redis事务的ACID特性
在传统的关系型数据库中,常常用ACID特质来检测事务功能的可靠性和安全性。
在redis中事务总是具有原子性(Atomicity),一致性(Consistency)和隔离性(Isolation),并且当redis运行在某种特定的持久化模式下,事务也具有耐久性(Durability).
1 原子性
事务具有原子性指的是,数据库将事务中的多个操作当作一个整体来执行,服务器要么就执行事务中的所有操作,要么就一个操作也不执行。
但是对于redis的事务功能来说,事务队列中的命令要么就全部执行,要么就一个都不执行,因此redis的事务是具有原子性的。我们通常会知道两种关于redis事务原子性的说法,一种是要么事务都执行,要么都不执行。另外一种说法是redis事务当事务中的命令执行失败后面的命令还会执行,错误之前的命令不会回滚。其实这个两个说法都是正确的。但是缺一不可。我们接下来具体分析下
我们先看一个可以正确执行的事务例子
redis> MULTI
OKredis > SET username "bugall"
QUEUEDredis > EXEC 1) OK 2) "bugall"
与之相反,我们再来看一个事务执行失败的例子。这个事务因为命令在放入事务队列的时候被服务器拒绝,所以事务中的所有命令都不会执行,因为前面我们有介绍到,redis的事务命令是统一先放到事务队列里,在用户输入EXEC命令的时候再统一执行。但是我们错误的使用"GET"命令,在命令放入事务队列的时候被检测到事务,这时候还没有接收到EXEC命令,所以这个时候不牵扯到回滚的问题,在EXEC的时候发现事务队列里有命令存在错误,所以事务里的命令就全都不执行,这样就达到了事务的原子性,我们看下例子。
redis > MULTI
OKredis >GET
(error) ERR wrong number of arguments for 'get' command redis > GET username QUEUED redis > EXEC (error) EXECABORT Transaction discarded because of previous errors
redis的事务和传统的关系型数据库事务的最大区别在于,redis不支持事务的回滚机制,即使事务队列中的某个命令在执行期间出现错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止,我们看下面的例子
redis> SET username "bugall"
OKredis > MULTI
OKredis > SADD member "bugall" "litengfe" "yangyifang" QUEUED redis > RPUSH username "b" "l" "y" //错误对键username使用列表键命令 QUEUED redis > SADD password "123456" "123456" "123456" QUEUED redis > EXEC 1) (integer) 3 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) (integer) 3
redis的作者在十五功能的文档中解释说,不支持事务回滚是因为这种复杂的功能和redis追求的简单高效的设计主旨不符合,并且他认为,redis事务的执行时
错误通常都是编程错误造成的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他认为没有必要为redis开发事务回滚功能。所以
我们在讨论redis事务回滚的时候,一定要区分命令发生错误的时候。
2 一致性
事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论事务是否执行成功,数据库也应该仍然一致的。
”一致“指的是数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据。redis通过谨慎的错误检测和简单的设计来保证事务一致性。
3 隔离性
事务的隔离性指的是,即使数据库中有多个事务并发在执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全
相同。
因为redis使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证,在执行事务期间不会对事物进行中断,因此,redis的事务总是以串行
的方式运行的,并且事务也总是具有隔离性的
4 持久性
事务的耐久性指的是,当一个事务执行完毕时,执行这个事务所得的结果已经被保持到永久存储介质里面。
因为redis事务不过是简单的用队列包裹起来一组redis命令,redis并没有为事务提供任何额外的持久化功能,所以redis事务的耐久性由redis使用的模式
决定
- 当服务器在无持久化的内存模式下运行时,事务不具有耐久性,一旦服务器停机,包括事务数据在内的所有服务器数据都将丢失
- 当服务器在RDB持久化模式下运作的时候,服务器只会在特定的保存条件满足的时候才会执行BGSAVE命令,对数据库进行保存操作,并且异步执行的BGSAVE不
能保证事务数据被第一时间保存到硬盘里面,因此RDB持久化模式下的事务也不具有耐久性 - 当服务器运行在AOF持久化模式下,并且appedfsync的选项的值为always时,程序总会在执行命令之后调用同步函数,将命令数据真正的保存到硬盘里面,因此
这种配置下的事务是具有耐久性的。 - 当服务器运行在AOF持久化模式下,并且appedfsync的选项的值为everysec时,程序会每秒同步一次命令数据到磁盘因为停机可能会恰好发生在等待同步的那一秒内,这种可能造成事务数据丢失,所以这种配置下的事务不具有耐久性
本文转载自:https://blog.csdn.net/bugall/article/details/52386698
Redis事务实现原理相关推荐
- Redis 事务机制实现
1.首先,我们来看一下Redis中事物相关的指令, 命令原型 命令描述 MULTI 用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子执行. ...
- Redis 事务机制实现过程及原理,以及使用事务机制防止库存超卖
一.Redis 事务实现的过程和原理 第一步:观察数据 客户端在修改数据之前,先使用watch命令观察要修改的数据,这一步相当于记下了数据的版本号 第二步:开启事务 使用 multi 命令开启事务. ...
- redis事务原理,使用,详解
声明:本博客内容来自<Redis深度历险>一书 为了确保连续多个操作的原子性,一个成熟的数据库通常都会有事务支持,Redis 也不例外.Redis 的事务使用非常简单,不同于 ...
- Redis事务和watch
redis的事务 严格意义来讲,redis的事务和我们理解的传统数据库(如mysql)的事务是不一样的. redis中的事务定义 Redis中的事务(transaction)是一组命令的集合. 事务同 ...
- python redis事务_python redis事务源码及应用分析
在多个客户端同时处理相同的数据时,不谨慎的操作很容易导致数据出错.一般的关系型数据库中有事务保证了数据操作的原子性,同样Redis中也设置了事务,可以理解为"将多个命令打包,然后一次性.按顺 ...
- 面试问到 Redis 事务,我脸都绿了。。
前言 前几天有读者说自己面试被问到Redis的事务,虽然不常用,但是面试竟然被问到,平时自己没有注意Redis的事务这一块,面试的时候被问到非常不好受. 虽然,这位读者面试最后算是过了,但是薪资方面没 ...
- Redis:事务、管道、Lua脚本
1. Redis事务定义 Redis中的事务(transaction)是一组命令的集合.事务同命令一样都是Redis的最小执行单位,一个事务中的命令要么都执行,要么都不执行. 事务的原理是先将属于一个 ...
- redis setnx原子性_不支持原子性的 Redis 事务也叫事务吗?
文章收录在 GitHub JavaKeeper ,N线互联网开发必备技能兵器谱 假设现在有这样一个业务,用户获取的某些数据来自第三方接口信息,为避免频繁请求第三方接口,我们往往会加一层缓存,缓存肯定要 ...
- redis怎么修改_面试官问我Redis事务,还问我有哪些实现方式
❝ 「第12期」 距离大叔的80期小目标还有68期,今天大叔要跟大家分享的内容是 -- Reids中的事务.同样,这也是redis中重要指数为四颗星的必备基础知识点.下面一起来了解一下吧. ❞ 相信大 ...
最新文章
- python学习总目录
- 天草脱壳视频学习笔记(逆向 OD)
- 以下不是python文件读写方法的是-python 文件读写 - 刘江的python教程
- WCF与 Web Service的区别
- 使用OpenCV进行相机校准
- 十大有用但又偏执的Java编程技术
- 【BZOJ1831】[AHOI2008]逆序对(动态规划)
- 用keepalived配置高可用,监控NGINX服务
- 修改静态参数scope=spfile错误,导致spfile文件被破坏,无法重启的解决方法
- Beginning Python chapter 2 Lists and Tuples:1 Indexing and slicing
- Highlighting高亮插件使用说明
- android 应用开启以后,动态检测并或者相机权限。
- lq到底是什么意思_lq是什么意思
- django tests 小结
- 用elasticsearch和nuxtjs搭建bt搜索引擎
- 轻松读书——麦肯锡教我的写作武器
- 量子力学的传说——波尔篇(上)
- java 数组的class_Java Class isArray()方法
- js简单分页,只有上一页和下一页
- Linux运维工程师笔试题系列6(30题)
热门文章
- 普通人如何快速赚得人生的第一个100万?
- Macbook prom1+android虚拟机+pycharm+pytest+appium 实现微信登录
- matlab拉普拉斯算子边缘提取_(二十四)用二阶微分(拉普拉斯算子)实现图像锐化...
- 浅谈决策树算法以及matlab实现ID3算法
- Hadoop基础必知必会
- 一季度跳槽图鉴:6成人想跳槽,降薪也要跳!
- IdentityServer4揭秘---登录
- 【Android 教程系列第 26 篇】如何查看一个 apk 的签名信息
- 均匀直线阵的常规波束形成matlab
- 我与OV的斗争-----OPPO部分手机输入法默认白色