redis事务【面试必看】

什么是事务

Redis 的事务和 MySQL 的事务概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执
⾏。
但是注意体会 Redis 的事务和 MySQL 事务的区别:

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

Redis 事务本质上是在服务器上搞了⼀个 “事务队列”。每次客⼾端在事务中进⾏⼀个操作,都会把命令先发给服务器,放到 “事务队列” 中(但是并不会⽴即执⾏),⽽是会在真正收到 EXEC 命令之后, 才真正执⾏队列中的所有操作。

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

为了更好理解redis事务的概念,举一个例子:

典型写法:

获取仓库中剩余商品的个数
if(个数>0)
{
   下单成功
   商品个数--;
}

这种写法有一个问题就是多线程的时候会有并发的问题,需要加锁

在redis中直接使用事务即可

开启事务
get count
if(count>0)
decr count
执行事务 //redis服务器收到执行事务的时候才会真正执行事务

redis语句并不可以进行条件判断,但是redis支持lua脚本,通过lua脚本就可以实现条件判断

事务操作

MULTI

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

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"

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)

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 先执⾏的 set key 100。客户端2 后执⾏的 set key 200。但是从实际的执⾏时间看,是客户端2 先执⾏的,客户端1 后执⾏的

127.0.0.1:6379> get key 
"100"

这个时候, 其实就容易引起歧义。
因此, 即使不保证严格的隔离性,⾄少也要告诉⽤户,当前的操作可能存在⻛险。
watch 命令就是⽤来解决这个问题的。watch 在该客户端上监控⼀组具体的 key。(watch的实现有点像乐观锁)

  • 当开启事务的时候,如果对 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 的版本
(nil)
127.0.0.1:6379> get k1
"200"
127.0.0.1:6379> get k2
(nil)

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

UNWATCH

取消对 key 的监控。
相当于 WATCH 的逆操作。此处不做演示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值