Redis - 事务

时间:2024-11-09 16:53:19

一、什么是事务

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

但是注意体会Redis的事务和MySQL事务的区别:

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

Redis 事务本质上是在服务器上搞了⼀个"事务队列".每次客⼾端在事务中进⾏⼀个操作,都会把命令先 发给服务器,放到"事务队列"中(但是并不会⽴即执⾏)

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

因此,Redis的事务的功能相⽐于MySQL来说,是弱化很多的.只能保证事务中的这⼏个操作是"连续 的", 不会被别的客⼾端"加塞",仅此⽽已.

二、事务操作

2.1、MULTI

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

实例

127.0.0.1:6379> MULTI
 OK

2.2、EXEC

真正执行事务

实例

 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

每次添加⼀个操作,都会提⽰"QUEUED",说明命令已经进⼊客⼾端的队列了.

真正执⾏EXEC的时候,客⼾端才会真正把上述操作发送给服务器.

此时就可以获取到上述key的值了.

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

2.3、DISCARD

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

实例

 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)

2.4、WATCH

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

实例

 # 客⼾端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

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

从输⼊命令的时间看,是客⼾端1先执⾏的setkey100.客⼾端2后执⾏的setkey200. 但是从实际的执⾏时间看,是客⼾端2先执⾏的,客⼾端1后执⾏的

 127.0.0.1:6379> get key 
 "100"

这个时候,其实就容易引起歧义.

因此,即使不保证严格的隔离性,⾄少也要告诉⽤⼾,当前的操作可能存在⻛险.

watch命令就是⽤来解决这个问题的.watch在该客⼾端上监控⼀组具体的key.

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

实例

客⼾端1先执⾏

 127.0.0.1:6379> watch k1 # 开始监控 k1       
 OK
 127.0.0.1:6379> MULTI
 OK
 127.0.0.1:6379> set k1 100 # 进⾏修改, 从服务器获取 k1 的版本号是 0. 记录 k1 的版本号.  (还没真修改呢, 版本号不变)
 QUEUED
 127.0.0.1:6379> set k2 1000     
 QUEUED

只是⼊队列,但是不提交事务执⾏.

客⼾端2再执⾏

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

客⼾端1再执⾏

 127.0.0.1:6379> EXEC   # 真正执⾏修改操作, 此时对⽐版本发现, 客⼾端的 k1 的版本号是 0, 服务器上的版本号是 1, 版本不⼀致! 说明有其他客⼾端在事务中间修改了 k1 !!! 
 (nil)

 127.0.0.1:6379> get k1
 "200"

 127.0.0.1:6379> get k2
 (nil)

此时说明事务已经被取消了.这次提交的所有命令都没有执⾏.

2.5、UNWATCH

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

此处不做演⽰.