Redis——事务

文章目录

Redis 事务

什么是事务

Redis 的事务和 MySQL 的事务 概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执行

Redis 的事务和 MySQL 事务的区别:

  • 弱化的原子性: redis 没有 "回滚机制". 只能做到这些操作 "批量执行". 不能做到 "⼀个失败就恢复到
    初始状态"
  • 不保证⼀致性: 不涉及 "约束". 也没有回滚. MySQL 的⼀致性体现的是运行事务前和运行后 , 结果都
    是合理有效的, 不会出现中间⾮法状态
  • 不需要隔离性: 也没有隔离级别, 因为不会并发执行事务 (redis 单线程处理请求)
  • 不需要持久性: 是保存在内存的. 是否开启持久化, 是redis-server 自己的事情, 和事务无关

Redis 事务本质上是在服务器上搞了⼀个 "事务队列". 每次客户端在事务中进行⼀个操作, 都会把命令先

发给服务器, 放到 "事务队列" 中(但是并不会立即执行)

而是会在真正收到 EXEC 命令之后, 才真正执行队列中的所有操作.

因此, Redis 的事务的功能相比于 MySQL 来说, 是弱化很多的. 只能保证事务中的这⼏个操作是 "连续

的", 不会被别的客户端 "加塞", 仅此而已.

事务操作

MULTI

开启⼀个事务. 执行成功返回 OK

EXEC

真正执行事务

每次添加⼀个操作, 都会提示 "QUEUED", 说明命令已经进入客户端的队列了. 真正执行 EXEC 的时候, 客户端才会真正把上述操作发送给服务器. 此时就可以获取到上述 key 的值了

DISCARD

放弃当前事务,此时直接清空事务队列.,之前的操作都不会真正执行到

WATCH

在执行事务的时候, 如果某个事务中修改的值, 被别的客户端修改了, 此时就容易出现数据不⼀致的问

例如

shell 复制代码
# 客户端1 先执行
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key 100
QUEUED

# 客户端2 再执行
127.0.0.1:6379> set key 200
OK

# 客户端1 最后执行
127.0.0.1:6379> EXEC
OK

此时, key 的值是多少呢??

客户端1

客户端2

从输⼊命令的时间看, 是客端1 先执行的 set key 100. 客户端2 后执行的 set key 200.

这个时候,就容易发生歧义,watch 命令就是⽤来解决这个问题的,watch 在该客户端上监控⼀组具体的 key

  • 当开启事务的时候, 如果对 watch 的 key 进行修改, 就会记录当前 key 的 "版本号". (版本号是个简单
    的整数, 每次修改都会使版本变大. 服务器来维护每个 key 的版本号情况)
  • 在真正提交事务的时候, 如果发现当前服务器上的 key 的版本号已经超过了事务开始时的版本号, 就
    会让事务执行失败. (事务中的所有操作都不执行).

客户端1先执行

然后客户端2执行

然后再回到客户端1执行

此时可以发现事务已经被取消了. 这次提交的所有命令都没有执行

shell 复制代码
# 客户端1
127.0.0.1:6379> watch key  # 开始监控 key
OK
127.0.0.1:6379> multi  
OK
127.0.0.1:6379> set key 100   #进行修改,从服务器获取key的版本号0,记录key的版本号
QUEUED
127.0.0.1:6379> set key2 200
QUEUED

# 客户端2 
127.0.0.1:6379> set key 200    # 修改成功, 使服务器端的 k1 的版本号 0 -> 1
OK

# 客户端1
127.0.0.1:6379> exec # 真正执⾏修改操作, 此时对⽐版本发现, 客⼾端的 k1 的版本号是 0, 服务器上的版本号是 1, 版本不⼀致! 说明有其他客⼾端在事务中间修改了k1
(nil)
127.0.0.1:6379> get key
"200"
127.0.0.1:6379> get key2  # 事务已经被取消
(nil)
UNWATCH

取消对 key 的监控,相当于 WATCH 的逆操作

watch的实现原理

watch的实现,类似于一个"乐观锁"

乐观锁,悲观锁不是指某个具体的锁,而是指的是某一类锁的特性

乐观锁:加锁之前,就有一个心理预期,预期接下来锁冲突的概率比较低

悲观锁:加锁之前,也有一个心理预期,接下来锁冲突的概率比较高

锁冲突概率高,和冲突概率低,接下来要做的工作是不一样的

  • 当执行watch key的时候,就会给这个key安排一个版本高,版本号可以理解成一个"整数",每次在修改的时候,版本号都会"变大"
  • 在执行exec时,就会做出判定,判定当前这个key的版本号,和最初watch的时候记录的版本号是否一致,如果一致,说明当前key在事务开启到最终执行的这个过程中,没有别的客户端修改,于是才能真正进行设置,如果不一致,说明key在其他客户端改过了,因此此处就直接丢弃事务中的操作,exec返回nil

总结

Redis的事务,要比mysql的事务简单的多

  1. 原子性:Redis的事务,并不支持回滚
  2. 一致性:Redis并不会保证事务执行前和执行后,内容统一
  3. 持久性:Redis主要通过内存来存储数据
  4. 隔离性:Redis自身作为一个单线程的服务器模型,上面的请求本质上都是串行执行的
相关推荐
建投数据39 分钟前
建投数据与腾讯云数据库TDSQL完成产品兼容性互认证
数据库·腾讯云
Hacker_LaoYi2 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
岁月变迁呀2 小时前
Redis梳理
数据库·redis·缓存
独行soc2 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
你的微笑,乱了夏天2 小时前
linux centos 7 安装 mongodb7
数据库·mongodb
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭2 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
工业甲酰苯胺2 小时前
分布式系统架构:服务容错
数据库·架构
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘
White_Mountain4 小时前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
Code apprenticeship4 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存