当前位置: 首页 > news >正文

图解 Redis 事务 ACID特性 |源码解析|EXEC、WATCH、QUEUE

写在前面

Redis 通过 MULTIEXECWATCH 等命令来实现事务功能。Redis的事务是将多个命令请求打包,然后一次性、按照顺序的执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而该去执行其他客户端的命令请求。 就像下面这样:

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> SET fantwo 2
QUEUED
redis(TX)> GET fanone
QUEUED
redis(TX)> EXEC
1) OK
2) OK
3) "1"

本文我们就从redis的事务执行过程以及ACID四个方面来介绍redis的事务

事务实现

在这里插入图片描述
从上面的例子我们可以知道,redis的事务是从MULTI命令开始的,所有的命令都会按照FIFO的顺序进入一个QUEUE队列中,当执行EXEC操作后才将这些命令逐步执行。

MULTI

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

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

事务队列以FIFO先进先出的方式保存入队的命令,还是用上面的例子来画一个原型图:

在这里插入图片描述
当一个处于事务状态的客户端向服务器发送EXEC命令的时候,这个EXEC命令将立即被服务器执行。服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令所得的结果全部返回给客户端。EXEC的伪代码如下:

void EXEC() {std::vector<Reply> reply_queue;  // 创建空白的回复队列for (const auto& cmd : client.mstate.commands) { // 执行事务中的所有命令Reply reply = execute_command(cmd.command, cmd.argv, cmd.argc);reply_queue.push_back(reply);}client.flags &= ~REDIS_MULTI; // 清理事务状态client.mstate.count = 0;release_transaction_queue(client.mstate.commands);send_reply_to_client(client, reply_queue);// 发送回复给客户端
}

WATCH

接着我们来讲讲WATCH命令,其实这是一个乐观锁(optimistic locking),可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否有已经被修改过了的,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复

在这里插入图片描述
比如下面这个例子:
在这里插入图片描述

时间客户端A客户端B
T1WATCH “fanone”
T2MULTI
T3SET “fanone” 1
T4SET “fanone” 2
T5EXEC

而在时间T4,客户端B修改了fanone键的值,当客户端A在T5执行EXEC命令的时候,服务器会发现WATCH监视的键fanone已经被修改了,因此服务器拒绝执行客户端A的事务,并且向客户端A返回空回复

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

typedef struct redisDb {dict *watched_keys; // 正在被WATCH命令监视的键 
}

在这里插入图片描述

ACID

Redis的事务是否符合ACID呢?

原子性 Atomicity

在这里插入图片描述

事务具有原子性是指,数据库将事务中的多个操作当作一个整体来执行,要么全部执行,要么全部不执行。对于Redis的事务功能来说,事务队列中的命令要么就都全部都执行,要么就一个都不执行,因此Redis的事务是具有原子性的。 比如以下成功执行的事务,事务中的所有命令都被执行:

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> GET fanone
QUEUED
redis(TX)> EXEC
1) OK
2) "1"

与此相反,如果其中有一个命令是错误的,那么整个命令就不会执行。fanone这个key是一个string,并不是set,所以不能使用SADD命令执行。

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> SADD fanone 2
QUEUED
redis(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

那么大家会发现Redis的事务和传统的关系型数据库事务的最大区别在于,Redis不支持事务回滚机制,也就是rollback。

Redis的作者在事务功能的文档中也解释道,不支持事务回滚是因为这种复杂的功能和Redis追求简单高效的设计主旨不相符。

在这里插入图片描述

一致性 Consistency

事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论事务是否执行成功,数据库也应该依然是一致的。 而这个一致指的是 数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据

比如执行完事务不会多一个之前没有的命令,或者某个key是string类型,不会变成set类型。我们用上面同一个例子来说明:

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> SADD fanone 2
QUEUED
redis(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

fanone 这个key是string类型,但是无法通过SADD命令将fanone变成set类型。虽然在入队列的时候,redis没有报错,但是在EXEC的时候,redis报了,所以这个key一开始是string类型,事务执行完后也会是string类型,事务执行前后保持了一致。

在这里插入图片描述

隔离性 Isolation

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

在这里插入图片描述

因为Redis是使用单线程的方式来执行事务以及事务队列中的命令,并且在服务器稳定的情况下,执行事务不会中断,因此,redis的事务总是串行的方式执行的,所以具备隔离性。
在这里插入图片描述

持久性 Durability

事务的持久性值得是当一个事务执行完毕的时候,执行这个事务所得的结果已经被保存到永久性存储介质里面了,即使服务器在事务执行完毕之后停机,执行事务所得的结果也不会丢失。

由于Redis的事务比较简洁,没有提供持久化的能力,所以Redis的事务是依赖于Redis所使用的持久话模式,也就是AOF、RDB,我们一个个来讨论

  • 当服务器无持久化运行的时候,事务不具备持久性,一旦服务器宕机,事务数据将会丢失。

  • 当服务器在RDB持久化模式下运作的时候,服务器只会在特定的保存条件下满足,比如使用BGSAVE命令,队数据库进行保存,但是异步执行的BGSAVE也不能保证,第一时间保存在硬盘中,因此RDB持久化模式下事务不具备持久性
    在这里插入图片描述

  • 而当服务器运行在AOF持久话模式下,并且appendfsync选项是always的时候,服务器总会在执行完命令之后调用同步sync函数,将命令数据保存在硬盘中,而此时事务具备持久性,其他选择比如everysec或者no的时候都不具备持久性。

在这里插入图片描述

相关文章:

  • 第5讲:不同杂志风格主题复刻指南——打造像Nature、Science、Cell那样的高水准科研图表!
  • 软件维护类型四大类型(IEEE 14764 标准)
  • 021-C语言文件操作
  • Beta-VAE背景原理及解耦机制分析
  • 悟空统计平台在教育行业的落地:课程转化路径优化实践
  • 如何在 Ubuntu 22.04|20.04|18.04 上安装 PostGIS
  • # 家庭网络IPv6地址的一些知识
  • Jquery -函数调用使用创建立即执行函数
  • 1位的推理框架bitnet.cpp
  • 【重走C++学习之路】20、unordered_map和unordered_set
  • 跨境支付接口RT从300ms突增至2000ms,但CPU/Memory无异常,如何排查?
  • 第二大脑-个人知识库
  • 使用FME生成Delaunay三角形
  • MobX 在 React 中的使用:状态管理的新选择
  • Native层Trace监控性能
  • C语言高频面试题——指针赋值字符串与定义一个数组赋值字符串有什么区别?
  • Pygame精灵进阶:动画序列与角色控制
  • Docker中修改OpenJDK 17 TLS禁用算法
  • 数据分析管理软件 Minitab 22.2.2 中文版安装包 免费下载
  • gtest 安装及使用
  • 银川市长信箱被指乱回复:问诗词大会、答工程欠款,官方称工作失误
  • 5145篇报道中的上海车展:40年,什么变了?
  • 大家聊中国式现代化|周冯琦:转角见美,让“绿意”触手可及
  • 大漠孤烟为何“直”?物理学家在唐诗中读出“不一样的美”
  • 全球首台环形CT直线加速器在沪正式开机,系我国自主研发
  • 永辉超市一季度净利降近八成,未来12个月至18个月是改革成果集中释放期