redis事务实现原理

对于redis事务,发现还是很不错的。近日整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。

一:简介

Redis事务通常会使用MULTI,EXEC,WATCH等命令来完成,redis实现事务实现的机制与常见的关系型数据库有很大的区别,比如redis的事务不支持回滚,事务执行时会阻塞其它客户端的请求执行。

一般事务

multi

discard

exec

特殊:watch

服务器在执行完事务中的所有命令之后,才会继续处理其他客户端的其他命令

事务由multi开启,将多个命令入队到事务中,最后exec命令触发事务,一并执行性:

一个事务从开始到执行会经历以下三个阶段:

开始事务。

multi命令将服务器切换到事务状态

命令入队。

将命令放进事务队列里

执行事务。

从事务对列中取出命令并执行

事务队列是一个数组,每个数组项包含三个属性

要执行的命令(cmd)

命令的参数(argv)

参数的个数(argc)

例子:

举个例子,如果客户端执行以下命令:

redis> MULTI

程序为客户端创建一下事务队列

exec,discard,multi,watch 是不放入事务队列中的,而是直接执行的。

事务队列的执行结果,放入回复队列中,当执行exec命令会将回复队列作为自己的执行结果放回客户端,

并从事务状态返回到非事务状态。

无论在事务状态下,还是在非事务状态下,Redis 命令都由同一个函数执行,所以它们共享很

多服务器的一般设置,比如 AOF 的配置、 RDB 的配置,以及内存限制,等等

事务中命令和普通命令的区别

非事务状态下的命令以单个命令为单位执行,前一个命令和后一个命令的客户端不一定是同一个;

而事务状态则是以一个事务为单位,执行事务队列中的所有命令:

除非当前事务执行完毕,否则服务器不会中断事务,也不会执行其他客户端的其他命令。

在非事务状态下,执行命令所得的结果会立即被返回给客户端;

而事务则是将所有命令的结果集合到回复队列,再作为 EXEC 命令的结果返回给客户端

Redis 的事务是不可嵌套的,处于事务状态的客户端在发送multi,则服务器只会返回一个简单错误不做任何操作,

继续等待其他命令入队。

DISCARD

命令用于取消一个事务,它清空客户端 的整个事务队列,然后将客户端从事务状态调回非事务状态

WATCH

只能在客户端进入事务状态之前执行,在事务状态下发送 WATCH 命令会引发一个简单错误,不会做其他改变

命令用于在事务开始之前监视任意数量的键:

当调用 EXEC 命令执行事务时,如果任意一个被监视的键已经被其他客户端修改了,

那么整个事务不再执行,直接返回失败。

watch一个key,这个key只能是自己改动,别人不能改,如果别人改了,我就不玩儿了。

也就是事务的安全性

WATCH命令的实现

存储

watch_keys 字典,字典的键是这个数据库被监控的键值,而字典的值则是一个链表,链表保存所有监视这个键的客户端。

watch 触发

在任何对数据库键空间( key space)进行修改的命令成功执行之后(比如 FLUSHDB 、 SET、DEL 、LPUSH 、SADD 、ZREM ,诸如此类),

multi.c/touchWatchKey 函数都被调用,检查数据库的watch_keys字典,看是否有客户端在监视已经被命令修改的键。

如果有, 程序将所有监视这个/这些被修改键的客户端的 REDIS_DIRTY_CAS 选项打开,表明事务安全性已经被破坏。

当客户端发送exec命令,触发事务执行性,服务器会对客户端状态进行检查:

REDIS_DIRTY_CAS

被打开事务安全性已经被破坏,放弃事务,返回执行失败

否则,服务器执行所有事务

最后,当一个客户端结束它的事务时,无论事务是成功执行,还是失败,watched_keys字典中和这个客户端相关的资料都会被清除。

事务的ACID性质

在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的安全性。

Redis 事务保证了其中的一致性( C)和隔离性( I),但并不保证原子性( A)和持久性( D)

原子性(Atomicity)

事务中命令要么全部执行成功,有一条失败则全部回滚

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以Redis事务的执行并不是原子性的

如果 Redis 服务器进程在执行事务的过程中被停止,那么事务执行失败,但是不会回滚。

一致性(Consistency)

redis的一致性问题分三部分

入队错误

执行错误

redis进程被终结

客户端向服务器发送了错误的命令。

服务器将想客户端返回出错信息,并将客户端设为REDIS_DIRTY_EXEC状态。

当执行exec命令时,Redis拒绝执行状态为 REDIS_DIRTY_EXEC 的事务。

因此,带有不正确入队命令的事务不会被执行,也不会影响数据库的一致性

对一个不同类型的 key 执行了错误的操作

Redis 只会将错误包含在事务的结果中

这不会引起事务中断或整个失败

不会影响已执行事务命令的结果

也不会影响后面要执行的事务命令

所以它对事务的一致性也没有影响

Redis 进程被终结 那么

根据 Redis 所使用的持久化模式,可能有以下情况出现:

内存模式

如果 Redis 没有采取任何持久化机制,那么重启之后的数据库总是空白的,所以数据总是一致的。

RDB模式

但只要 RDB 文件本身没有因为其他问题而出错,那么还原后的数据库就是一致的。

在执行事务时,Redis 不会中断事务去执行保存 RDB 的工作,只有在事务执行之后,保存 RDB 的工作才有可能开始。

所以当 RDB 模式下的 Redis 服务器进程在事务中途被杀死时,事务内执行的命令,不管成功了多少,都不会被保存到 RDB文件里。

恢复数据库需要使用现有的 RDB 文件,而这个 RDB 文件的数据保存的是最近一次的数据库快照( snapshot),所以它的数据可能不是最新的

AOF模式

如果事务语句未写入到 AOF 文件,或 AOF 未被 SYNC 调用保存到磁盘

如果事务的部分语句被写入到 AOF 文件,并且 AOF 文件被成功保存

那么当进程被杀死之后,Redis 可以根据最近一次成功保存到磁盘的 AOF 文件来还原数据库

只要 AOF 文件本身没有因为其他问题而出错,那么还原后的数据库总是一致的,但其中的数据不一定是最新的

需要使用 redis-check-aof 工具将部分成功的事务命令移除之后,才能再次启动服务器。

还原之后的数据总是一致的,而且数据也是最新的(直到事务执行之前为止)。

那么不完整的事务执行信息就会遗留在 AOF 文件里

当重启 Redis 时,程序会检测到 AOF 文件并不完整,Redis 会退出,并报告错误

因为保存 AOF 文件的工作在后台线程进行,所以即使是在事务执行的中途,保存 AOF 文件的工作也可以继续进行

因此,根据事务语句是否被写入并保存到 AOF文件,有以下两种情况发生:

隔离性(lsolation)

事务的隔离性指的是,即使数据库中有多个事务并发在执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全相同。

因为redis使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证,在执行事务期间不会对事物进行中断,因此,redis的事务总是以串行的方式运行的,并且事务也总是具有隔离性的

持久性(Durability)

事务的耐久性指的是,当一个事务执行完毕时,执行这个事务所得的结果已经被保持到永久存储介质里面。

因为redis事务不过是简单的用队列包裹起来一组redis命令,redis并没有为事务提供任何额外的持久化功能,所以redis事务的耐久性由redis使用的模式决定

- 当服务器在无持久化的内存模式下运行时,事务不具有耐久性,一旦服务器停机,包括事务数据在内的所有服务器数据都将丢失 - 当服务器在RDB持久化模式下运作的时候,服务器只会在特定的保存条件满足的时候才会执行BGSAVE命令,对数据库进行保存操作,并且异步执行的BGSAVE不 能保证事务数据被第一时间保存到硬盘里面,因此RDB持久化模式下的事务也不具有耐久性

- 当服务器运行在AOF持久化模式下,并且appedfsync的选项的值为always时,程序总会在执行命令之后调用同步函数,将命令数据真正的保存到硬盘里面,因此 这种配置下的事务是具有耐久性的。

- 当服务器运行在AOF持久化模式下,并且appedfsync的选项的值为everysec时,程序会每秒同步一次命令数据到磁盘因为停机可能会恰好发生在等待同步的那一秒内,这种可能造成事务数据丢失,所以这种配置下的事务不具有耐久性。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值