Redis篇之Redis事务

news/2025/2/12 8:04:58/

Redis事务

Redis事务的本质是一组命令的集合

一个事务中所有命令会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

事务执行三阶段:

  1. 开启:以 MULTI 开始一个事务

  2. 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面

  3. 执行:由 EXEC 命令触发事务

1、事务相关命令

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

2、相关操作

# 标准事务执行
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set k1 hello # 命令入队
QUEUED
127.0.0.1:6379> set k2 redis # 命令入队
QUEUED
127.0.0.1:6379> EXEC # 执行事务
1) OK
2) OK
127.0.0.1:6379> get k1 # 验证是否执行
"hello"
127.0.0.1:6379> get k2
"redis"# 事务取消
127.0.0.1:6379> MULTI # 开始事务
OK
127.0.0.1:6379> set k3 test1
QUEUED
127.0.0.1:6379> set k4 test2
QUEUED
127.0.0.1:6379> DISCARD # 取消事务
OK
127.0.0.1:6379> EXEC # 因为事务被取消了,所以执行报错
(error) ERR EXEC without MULTI# 事务出现从错误处理
# 1、语法错误(编译器错误)
# 语法错误会使得事务提交失败,数据恢复原样
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set k1 11
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> error k3 33 # 执行一条错误的命令
(error) ERR unknown command `error`, with args beginning with: `k3`, `33`, 
127.0.0.1:6379> exec # 执行事务,报错,命令皆未执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 # 验证是否执行
"hello" # 值未变
127.0.0.1:6379> get k2
"redis"# 2、类型错误(运行时错误)
# 触发运行时错误时,事务将会跳过错误的命令继续执行,其他正常的命令可以被执行成功
# 此处可以说明:Redis单条指令保证原子性,但是Redis事务不能保证原子性
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set k1 11
QUEUED
127.0.0.1:6379> INCR k2 # 类型错误,对string类型进行自增操作
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC # 执行事务,可以发现虽然第二条命令报错了,但是其他的命令正常执行了
1) OK
2) (error) ERR value is not an integer or out of range
3) "redis"
127.0.0.1:6379> get k1 # 获取k1的值为事务中修改后的值
"11"

3、监控

3.1、乐观锁和悲观锁

悲观锁(Pessimistic Lock)

  • 很悲观,认为什么时候都会出现问题,无论做什么都会加锁
  • 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁,读锁,写锁等,都是在操作之前先上锁
  • 在Java中,synchronized的思想也是悲观锁

乐观锁(Optimistic Lock)

  • 很乐观,认为什么时候都不会出现问题,做什么都不加锁。只有在更新数据的时候去判断一下该数据在此期间是否有过变更
  • 乐观锁适用于多读的应用类型,这样可以提高吞吐量
  • 乐观锁策略:提交版本必须大于记录当前版本才能执行更新

乐观锁一般会使用版本号机制或CAS操作实现

一般会使用版本号机制或CAS操作实现:

  1. version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

    核心SQL代码:

    update table set x=x+1, version=version+1 where id=#{id} and version=#{version};

  2. CAS(Check And Set【先检查再动手设置】)

    CAS操作方式:即 compare and set,CAS是乐观锁技术,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。

3.2、Watch监控

使用watch key监控指定数据,相当于乐观锁加锁,其执行流程如下

Redis执行事务过程
上图源自:http://c.biancheng.net/view/4544.html

# 正常执行
127.0.0.1:6379> set money 100 # 设置初始金额
OK
127.0.0.1:6379> set use 0 # 设置已消费金额
OK
127.0.0.1:6379> WATCH money # 监控money,相当于给money上锁
OK
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> DECRBY money 20 # 消费20,初始金额-20
QUEUED
127.0.0.1:6379> INCRBY use 20 # 消费金额+20
QUEUED
127.0.0.1:6379> EXEC # 执行事务,正常无报错
1) (integer) 80
2) (integer) 20# 模拟多线程修改值
# 开启两个Xshell客户端连接同一个redis
# 在第一个客户端1开始操作
127.0.0.1:6379> WATCH money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> # 执行到此处,停止操作,模拟另一个线程修改数据,切换到另一个客户端2127.0.0.1:6379> INCRBY money 500 # 操作money,改变其值+500
(integer) 580
127.0.0.1:6379> # 无需再操作,切回客户端1执行事务127.0.0.1:6379> EXEC # 发现事务执行返回空,即所有命令都未执行
(nil)
127.0.0.1:6379> get money # 查询money,发现和客户端2增加后的值一致,说明客户端1后来的事务并未执行
"580"
127.0.0.1:6379> get use # 同理
"20"

UNWATCH命令用于解除WATCH的监控,但是需要注意的是:每次提交执行EXEC后都会自动释放锁,不管是否成功,所以UNWATCH无需在EXEC后执行


http://www.ppmy.cn/news/39575.html

相关文章

基础练习 十进制转十六进制

number int(input()) hex_num hex(number) print(hex_num[2:].upper())

ORACLE EBS 系统主数据管理(2)

ORACLE EBS 系统主数据管理 五、结语 (三)Item 的类别(Category) 上面所讲到的Item编码中的分类(UNSPSC),一般来说还不是系统(各应用功能模块)中真正使用到的类别&…

Java I/O流

I/O流I/O流IO流体系字节流体系FileOutPutStream(字节输出流)FileInPutStream(字节输入流)练习:文件拷贝Java中编码与解码的方法字符流FileReaderFileWriter综合练习缓冲流体系字节缓冲流字符缓冲流综合练习2转换流序列化流(对象操作输出流)/反序列化流(对象操作输入…

Ngnix配置config

Docker 安装启动Nginx 1.搜索镜像 search 2.下载镜像 pull 3.运行测试 [rootiZ2ze4nuf6nscouxcmwnasZ ~]# docker images \REPOSITORY TAG IMAGE ID CREATED SIZE mysql 5.7 a70d36bc331a 6 days ago 449MB nginx latest f6d0b4767a6c 12 days ago 133MB centos latest 300e315…

麻瓜+AI混合工作流试验 7:Prompt Engineer与产品经理,以及如何给小学生上一堂AI课...

最近越发感觉PE(Prompt Engineer)很像产品经理,区别在于向技术人员还是AI提需求。PE又有点像“从自己干到做管理”,思维方式会转变。碰到问题的第一反应变了,从自己琢磨如何解决,变成了琢磨如何把问题澄清、…

训练自己的ai模型(二)学习笔记与项目实操

ai模型大火,作为普通人,我也想做个自己的ai模型 训练自己的ai模型通常需要接下来的的六步 一、 收集和准备数据集:需要收集和准备一个数据集,其中包含想要训练模型的数据。这可能需要一些数据清理和预处理,以确保数据…

【springboot微服务】Lucence实现Mysql全文检索

目录 一、前言 1.1 常规调优手段 1.1.1 加索引 1.1.2 代码层优化 1.1.3 减少关联表查询 1.1.4 分库分表 1.1.5 引入第三方存储 二、一个棘手的问题 2.1 前置准备 2.1.1 创建一张表 2.1.2 插入一些数据 2.2 问题引发 2.2.1 关键字模糊查询 2.2.2 执行计划分析 2.…

Socket 发布到 MavenCentral

1. Sonatype 账号 1.1 第一次使用,注册账号 Sonatype Signup,如图: 1.2 注册过了,直接登录账号 Sonatype Login,如图: 1.3 创建一个新的问题工单,填写信息,如下图: 1.4 创建完成之后,将开启一个issue,会显示 Waiting for Response,会有相关机器人和管理员进行验…