[Redis][事务]详细讲解

目录


0.什么是事务?

  • Redis的事务和MySQL的事务概念上是类似的,都是把一系列操作绑定成一组,让这一组能够批量执行
  • Redis事务和MySQL的区别
    • 弱化的原子性 :Redis没有"回滚机制",只能做到这些操作"批量执行",不能做到"一个失败就恢复到初始状态"
      • 如果事务中若干个操作,存在有失败的,那就失败吧,不会有回滚操作
    • 不保证一致性 :不涉及"约束",也没有回滚,事务执行过程中如果某个修改操作出现失败,就可能引起不一致的情况
      • MySQL的一致性体现的是运行事务前后运行后,结果都是合理有效的,不会出现中间非法状态
    • 不需要隔离性:也没有隔离级别,因为不会并发执行事务(Redis单线程处理请求)
    • 不需要持久型 :是保存在内存的,是否开启持久化,是redis-server自己的事情,和事务无关
  • Redis如果按照集群模式部署,就不支持事务

1.Redis 事务本质

  • Redis事务**本质**上是在服务器上搞了一个"事务队列",每次客户端在事务中进行一个操作,都会把命令先发给服务器,放到"事务队列"中(但是不会立即执行),而是会在真正收到EXEC命令之后,才真正执行队列中的所有操作
    • 因此,Redis事务的功能相比于MySQL来说,是弱化很多的,只能保证事务中的这几个操作是"连续的",不会被别的客户端"加塞",仅此而已
  • 综上
    • Redis事务的意义,就是为了"打包",避免其他客户端的命令,插队到中间
    • 此处的不被插队,不是先抢占位置,而是先让出位置

2.Redis 事务意义

  • Redis的事务为啥就搞的这么简单,为啥不设计成和MySQL一样强大呢?

    • MySQL的事务,在背后付出了很大的代价
      • 空间上,要花费更多的空间来存储更多的数据
      • 时间上,也要有更大的开销
    • 正是因为MySQL有上述的问题,才有了Redis的用武之地
  • 什么时候需要使用到Redis事务呢?

    • 需要把多个操作打包进行,使用Redis事务比较合适
  • 示例 :商品抢购

    • 典型写法如果不加上任何限制,就可能会出现线程安全问题

      • 在多线程中,通过加锁的方式,来避免"插队"
      cpp 复制代码
      if(count > 0)
      {
      	// 下单成功
      	count--
      }
    • 在Redis中直接使用事务即可

      bash 复制代码
      # 开启事务
      get count
      if count > 0
      	decr count
      # 执行事务
      # Redis服务器收到执行事务操作的时候,才会真正执行
  • 说明

    • Redis原生命令中不支持条件判定,但是Redis支持lua脚本,可以通过lua实现
    • 可以认为lua脚本的实现方式是Redis事务的进阶版本

3.事务操作

1.MULTI

  • 功能:开启一个事务,执行成功返回OK
  • 当开启事务,并且向服务器发送若干个命令之后,此时服务器重启,此时的这个事务怎么办?
    • 此时的效果相当于DISCARD

2.EXEC

  • 功能 :真正执行事务
    • 每次添加一个操作,都会提示"QUEUED",说明命令已经进入服务端的队列了
    • 真正执行EXEC的时候,服务器才会真正执行命令
  • 示例
bash 复制代码
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 1
QUEUED
127.0.0.1:6379> set k2 2
QUEUED
127.0.0.1:6379> set k3 3
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK

127.0.0.1:6379> get k1
"1"
127.0.0.1:6379> get k2
"2"
127.0.0.1:6379> get k3
"3

3.DISCARD

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

  • 示例

    bash 复制代码
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set k1 1
    QUEUED
    127.0.0.1:6379> set k2 2
    QUEUED
    127.0.0.1:6379> DISCARD
    OK
    
    127.0.0.1:6379> get k1
    (nil)
    127.0.0.1:6379> get k2
    (nil)

4.WATCH

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

  • 此时key的值是多少呢?

    • 从输入命令的时候看,是客户端1先执行的set key 100,客户端2后执行的set key 200,但是从实际的执行时间看,是客户端2先执行的,客户端1后执行的

      bash 复制代码
      # 客⼾端 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
      1) OK
      
      # 结果查询
      127.0.0.1:6379> get key
      "100"
    • 这个时候,其实很容易引起歧义

    • 因此,即使不保证严格的隔离性,至少也要告诉用户,当前的操作可能存在风险

  • watch命令就是用来解决这个问题的,watch在该客户端上监控一组具体的key

    • 当开启事务的时候,如果对watchkey进行修改,就会记录当前key的"版本号"
    • 在真正提交事务的时候,如果发现当前服务器上的key的版本号已经超过了事务开始时的版本号,就会让事务执行失败
  • watch本质上是给exec加了个判定条件,属于"乐观锁"

  • 示例

    bash 复制代码
    # 客户端1先执行
    127.0.0.1:6379> watch k1        
    OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set k1 100      
    QUEUED
    127.0.0.1:6379> set k2 1000     
    QUEUED
    # 只是入队列,但是不提交事务执行
    
    # 客户端2后执行
    127.0.0.1:6379> set k1 200     
    OK
    
    # 客户端1再执行
    127.0.0.1:6379> EXEC          
    (nil)
    127.0.0.1:6379> get k1
    "200"
    127.0.0.1:6379> get k2
    (nil)
    # 此时说明事务已经被取消了,这次提交的所有命令都没有执行

5.UNWATCH

  • 功能 :取消对key的监控,相当于WATCH的逆操作
相关推荐
秋意钟23 分钟前
MySQL日期类型选择建议
数据库·mysql
Dxy12393102161 小时前
python下载pdf
数据库·python·pdf
桀桀桀桀桀桀2 小时前
数据库中的用户管理和权限管理
数据库·mysql
superman超哥3 小时前
04 深入 Oracle 并发世界:MVCC、锁、闩锁、事务隔离与并发性能优化的探索
数据库·oracle·性能优化·dba
用户8007165452003 小时前
HTAP数据库国产化改造技术可行性方案分析
数据库
minihuabei3 小时前
linux centos 安装redis
linux·redis·centos
engchina4 小时前
Neo4j 和 Python 初学者指南:如何使用可选关系匹配优化 Cypher 查询
数据库·python·neo4j
engchina4 小时前
使用 Cypher 查询语言在 Neo4j 中查找最短路径
数据库·neo4j
尘浮生4 小时前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
威哥爱编程4 小时前
SQL Server 数据太多如何优化
数据库·sql·sqlserver