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

redis的事务

redis通过 multi, exec等命令来实现事务功能,是一种交互式事务

工作机制:在一个事务内,将事务内的多个请求打包,然后一次性、按顺序地执行多个命令,在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求

相关命令

  • MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列
  • EXEC:执行事务中的所有操作命令
  • DISCARD:取消事务,放弃执行事务块中的所有命令,不等于回滚
  • WATCH:监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令
  • UNWATCH:取消WATCH对所有key的监视

基本使用

通过上文命令执行,很显然Redis事务执行是三个阶段:

  1. 开启事务:以MULTI开始一个事务
  2. 命令入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
  3. 执行事务:由EXEC命令触发事务

假设 a:stock、b:stock 两个键的初始值是 5 和 10

#初始化
127.0.0.1:6379> set a:stock 5
OK
127.0.0.1:6379> set b:stock 10
OK
#开启事务
127.0.0.1:6379> multi
OK
#将a:stock减1,
127.0.0.1:6379(TX)> decr a:stock
QUEUED
#将b:stock减1
127.0.0.1:6379(TX)> decr b:stock
QUEUED
#实际执行事务
127.0.0.1:6379(TX)> exec
1) (integer) 4
2) (integer) 9

执行步骤

当一个客户端切换到事务状态之后, 服务器会根据这个客户端发来的不同命令执行不同的操作:

  • 如果客户端发送的命令为 EXEC 、 DISCARD 、 WATCH 、 MULTI 四个命令的其中一个, 那么服务器立即执行这个命令
  • 与此相反, 如果客户端发送的命令是 EXEC 、 DISCARD 、 WATCH 、 MULTI 四个命令以外的其他命令, 那么服务器并不立即执行这个命令, 而是将这个命令放入一个事务队列里面, 然后向客户端返回 QUEUED 回复

ACID性质

原子性

⚠️ 不支持原子性,没有回滚机制

  • 对于编译错误,exec之前就能识别出来,exec时事务会失败,队列中的命令都不执行
127.0.0.1:6379> get a:stock
"3"
127.0.0.1:6379> get b:stock
"9"
#开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr a:stock
QUEUED
#错误命令
127.0.0.1:6379(TX)> lpush a:stock
(error) ERR wrong number of arguments for 'lpush' command
#执行事务,因为错误命令导致事务被取消
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get a:stock
"3"
  • 对于运行时错误,例如类型使用错误,当前错误用法会失败,其他命令会执行成功且不会回滚
127.0.0.1:6379> get a:stock
"3"
127.0.0.1:6379> get b:stock
"9"
#开启事务
127.0.0.1:6379> multi
OK
#命令1
127.0.0.1:6379(TX)> incr a:stock
QUEUED
#错误命令2
127.0.0.1:6379(TX)> lpush b:stock 1
QUEUED
#命令3
127.0.0.1:6379(TX)> incr a:stock
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 4  #命令1执行成功
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value #命令2执行失败
3) (integer) 5  #命令3执行成功

隔离性

⚠️ 总是具有隔离性的

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

注意:事务真正的执行是从exec开始的,所以串行也是以exec为标志的,multi开始后的key可能被更新,所以redis提供了watch命令实现一个CAS乐观锁

watch命令

watch命令是一个CAS乐观锁,它在exec命令执行之前,监视任意数量的数据库键,并在exec命令执行时,检查被监视的键,如果有一个已经被修改过了,redis会取消事务,返回nil

  • watch命令需要客户端主动显式在multi命令外执行
  • watch命令不存在ABA问题
watch的实现机制

每个redis数据库都保存着一个watched_keys字典,字典的键是这个数据库被监视的键, 而字典的值则是一个链表, 链表中保存了所有监视这个键的客户端

如果当前客户端为 client10086 , 那么当客户端执行 WATCH key1 key2 时, 前面展示的 watched_keys 将被修改成这个样子:

  通过 watched_keys 字典, 如果程序想检查某个键是否被监视, 那么它只要检查字典中是否存在这个键即可; 如果程序要获取监视某个键的所有客户端, 那么只要取出键的值(一个链表), 然后对链表进行遍历即可

所有对数据库进行修改的命令如:set,lpush,del等,在执行后都会对watched_keys字典检查,若有客户端正在监视被修改的key,则将监视的客户端的REDIS_DIRTY_CAS标识打开,表示该客户端的事务安全性已经被破坏

当服务器接收到一个客户端发来的exec命令时,会根据该客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务,若客户端的REDIS_DIRTY_CAS被打开,服务器会拒绝事务

持久性

⚠️ 不具有持久性

Redis并没有未事务提供任何额外的持久化功能,所以Redis事务的持久性由redis所使用的持久化模式决定:

  • RDB 适合做冷备,保存的是某一个时间的数据快照
  • AOF 适合热备,一般 AOF 会每隔 1 秒,通过一个后台线程执行一次 fsync 操作,最多丢失 1 秒钟的数据

但是不论在什么模式下运行,在一个事务的最后加上SAVE命令总是可以保证事务的持久性的,但是效率太低,不实用。所以,持久性,也不一定保证

一致性

⚠️ 个人观点:redis事务不能保证一致性,首先不具有回滚能力无法保证原子性,事务执行后无法保证数据的状态一起变更,其次不具备持久性,丢失数据后也会导致数据不一致

网上和一些书籍的观点是 Redis事务是支持一致性的,侧重点是没有包含非法或者无效的错误数据

小结

Redis事务只是一组打包好的命令,保证其能一次性、有序地、不被打断的执行,不具备原子性,并不是真正意义上的事务


http://www.mrgr.cn/news/75008.html

相关文章:

  • 软件测试——自动化测试常见函数
  • 【C++】拆分详解 - 多态
  • 原生js-复用性写法
  • 同步的意义以及机制
  • AFSim脚本学习
  • 三种复制只有阅读权限的飞书网络文档的方法
  • 编程之路,从0开始:知识补充篇
  • Spring Boot 应用程序中集成 Redis 并实现存储读取字符串或者复杂对象
  • 得物精准测试平台设计与实现
  • ros1 noetic跑UR5_gripper_camera_gazebo
  • Android Framework AMS(16)进程管理
  • Excel筛选的操作教程
  • QEMU 模拟器中运行的 Linux 系统
  • Elasticsearch 重建索引 数据迁移
  • PVE纵览-构建可靠的虚拟化平台:Proxmox VE高可用性详解
  • 开关电源漏电流测试需要哪些特殊设备?-纳米软件
  • golang将word、excel转换为pdf
  • 1990-2020年中国人工林和天然林空间分布数据集
  • SwiftUI 高级开发教程系列 - 第 3 章:数据持久化
  • 【Android】Android滑动冲突解决方案
  • 异构迁移常用SQL
  • 【Go 开发】pprof 排查问题流程:排查程序 CPU 占用高的问题
  • Android Mobile Network Settings | APN 菜单加载异常
  • 解密复杂系统:理论、模型与案例(3)
  • 计算机网络(7)
  • 山泽光纤HDMI线:铜线的隐藏力量