Redis面试题之Redis事务,Redis分布式锁

更新时间:2023-06-30 10:50:06 阅读: 评论:0

Redis⾯试题之Redis事务,Redis分布式锁
1. Redis事务简介
为了避免Redis执⾏指令过程中, 多条连续执⾏的指令被⼲扰, 打断, 插队这种情况, 就需要开启事务.
Redis事务就是⼀个命令执⾏的队列, 将⼀系列预定义命令包装成⼀个整体(⼀个队列), 当执⾏时, ⼀次性按照添加顺序依次执⾏, 中间不会被打断或者⼲扰.
或者简单的理解, 就是⼀个队列中, ⼀次性,顺序性, 排他性的执⾏⼀系列命令.
2. Redis事务操作
2.1 Redis事务的基本操作
开启事务multi
作⽤: 设定事务的开启位置, 次指令后, 后续的所有指定均加⼊到事务中.
执⾏事务exec
作⽤: 设定事务的结束位置, 同时执⾏事务, 与multi成对出现, 成对使⽤.
注意: 加⼊事务的命令暂时进⼊到任务队列中, 并没有⽴即执⾏, 只有与执⾏exec命令才开始执⾏.
取消事务discard
作⽤: 终⽌当前事务的定义, 发⽣在multi之后, exec之前.
网络营销工程师培训
2.2 事务的⼯作流程
执⾏指令
服务器判断指令是否是事务指令
rolling stones普通指令就普通执⾏
如果是multi就创建队列, 并返回ok
继续执⾏指令
thm
服务器继续判断指令是否是事务指令
如果是exec, 则执⾏事务, 并返回队列中所有结果
如果是普通指令, 则加⼊队列中, 返回queued
如果是discard, 则销毁队列, 返回ok
2.3 使⽤事务的注意事项
如果开启事务后, 命令中出现了语法错误, 则导致该事务队列失效(之前书写正确的也失效了), 这就是都会整体失败.
如果开始事务后, 命令执⾏时出现了错误, 则提交事务后, 正确的被执⾏了, 错误的不被执⾏, 也就是说不会整体回滚, 那只能⼿动回滚数据.
如何⼿动进⾏事务回滚呢?
1. 记录操作过程中被影响的数据之前的状态
单数据: String
多数据:hash, list, t, zt
2. 设置指令恢复所有的被修改的项
单数据: 直接t回去,注意该值是否有时效, 如有也有设置回去
多数据: 修改对应值或者整体克隆复制.
考虑英语
2.4 基于特定条件的事务执⾏-锁
实际开发过程中, 为了⾼可⽤, 会有多个客户端有操作Redis的权限. 为了保证同⼀个key只能被⼀个的客户端增删改操作, 需要对同⼀数据加锁.
场景分析
天猫双11热卖过程中, 对已经售罄的货物追加补货, 4个业务员都有权限补货, 只能被⼀个业务员连续操作, 不能被多个业务员重复补货,如何解决呢?
1. 多个客户端有可能同时操作同⼀个key, 且该key只能被修改⼀次.
2. 如果修改后, 后⾯再被修改则要终⽌当前的操作.
3. 肯定也要添加事务, 涉及增删改问题肯定是要添加事务的.
解决⽅案
1. 对同⼀个要修改的key添加监听锁
2. 开启事务
3. ⼀旦该key发⽣改变, 当前客户端操作的事务则失败
lazy是什么意思watch key1 [key2, ]# 监听指定的key
multi # 开启事务
... # 进⾏⼀些列操作
exec# 提交事务
4. 如何取消监视呢? 可以取消对所有key的监视
unwatch
因此, 这也是Redis的⼀个应⽤场景, Redis应⽤基于状态控制的批量任务执⾏.
3. 分布式锁
3.1 分布式锁是什么
实际开发过程中, 也会遇到如下场景, 不但要保证同⼀个key只能被⼀个的客户端增删改操作, 还要监控该key对应的value值, 这时就需要设置分布式锁了.
场景分析
还是天猫双11热卖过程中, 怎么避免最后⼀件商品不被多⼈同时购买(超卖问题)
1. watch监听能监听特定的key是否被修改, 但是⽆法监听被修改的值, 此处要监控的是具体的数据.
2. 虽然Redis是单线程的, 但是多个客户端对同⼀数据同时进⾏操作时, 如何避免不被同时修改呢?
解决⽅案
1. 使⽤tnx设置⼀个公共锁 tnx lock-key value, value可以为随机任意值.
2. tnx命令能返回value值.只有第⼀次执⾏的才会成功并返回1,其它情况返回0:
如果返回是1, 说明没有⼈持有锁, 当前客户端设置锁成功,可以进⾏下⼀步的具体业务操作.
如果返回是0, 说明有⼈持有了锁, 当前客户端设置锁失败, 那么需要排队或等待锁的释放.
3. 操作完毕通过del操作释放锁.
这就是Redis分布式锁的雏形, 当然实际开发中要考虑锁时效性避免死锁问题,还要避免锁误删问题, 因此有接下来⼏种版本的分布式锁3.2 分布式锁版本1
3.2.1 业务场景
场景分析
依赖分布式锁的机制, 某个⽤户操作Redis时对应的客户端宕机了, 且此时已经获取到锁, 导致锁⼀直被持有, 其他客户端拿不到锁, 这就是死锁问题, 如何解决呢?
1. 由于锁操作由⽤户控制加锁解锁, 必定会存在加锁未解锁的风险
2. 需要解锁操作不能仅依赖⽤户控制, 系统级别要给出对应的保底处理⽅案.
解决⽅案
1. 使⽤expire为锁key添加时间限定, 到时不释放锁, 则放弃锁.
tnx lock-key 001 # 设置分布式锁
expire lock-key cond # 设置单位为秒
pexpire lock-key milliconds # 设置单位为毫秒
2. 或者直接设置的时候添加时间限制
t lock-key value NX PX 毫秒数
# ⽐如为key为name设置分布式锁
t lock-name 001 NX PX 5000
value可以是任意值
NX代码只有lock-key不存在时才设置值
实际开发中如何知道设置多少时间合适.
由于操作通常都是微秒或毫秒级,因此该锁定时间不宜设置过⼤。具体时间需要业务测试后确认。
例如:持有锁的操作最长执⾏时间127ms,最短执⾏时间7ms。
1. 测试百万次最长执⾏时间对应命令的最⼤耗时,测试百万次⽹络延迟平均耗时
丝袜英文
2. 时间设定推荐:最⼤耗时*120%+平均⽹络延迟*110%
3. 如果⼆者相差2个数量级,取其中单个耗时较长即可.
spsp3.2.2 流程图
1、通过t命令设置锁
2、判断返回结果是否是OK
1)Nil,获取失败,结束或重试(⾃旋锁)
2)OK,获取锁成功
执⾏业务
释放锁,DEL 删除key即可
3、异常情况,服务宕机。超时时间EX结束,会⾃动释放锁
3.3 分布式锁版本2
3.3.1 业务场景责任感英文
场景分析
1. 三个进程:A和B和C,在执⾏任务,并争抢锁,此时A获取了锁,并设置⾃动过期时间为20ms
2. A开始执⾏业务,因为某种原因,业务阻塞,耗时超过了20ms,此时锁⾃动释放了.
3. B恰好此时开始尝试获取锁,因为锁已经⾃动释放,成功获取锁.
4. A此时业务执⾏完毕,执⾏释放锁逻辑(删除key),于是B的锁被释放了,⽽B其实还在执⾏业务.
5. 此时进程C尝试获取锁,也成功了,因为A把B的锁删除了。
问题(1):使⽤版本1的分布式锁, 有可能B和C同时获取到锁,违反了锁只能被⼀个客户端持有的特性.
如何解决这个问题呢?我们应该在删除锁之前, 判断这个锁是否是⾃⼰设置的锁, 如果不是(例如⾃⼰的锁已经超时释放), 那么就不要删除了.
问题(2):如何得知当前获取锁的是不是⾃⼰呢?
解决⽅案
1. 我们可以在t 锁时,存⼊⾃⼰的信息!删除锁前, 判断下⾥⾯的值是不是与⾃⼰相等. 如果不等,就不要删除了.
2. 这⾥⾃⼰的信息通常是⼀个随机值+当前线程的id, 通过UUID.randomUUID().toString()+Thread.curr
entThread().getId()获取到.
3.3.2 流程图
1、通过t命令设置锁
id通常是⼀个随机值+当前线程的id.
2、判断返回结果是否是OK
1)Nil,获取失败,结束或重试(⾃旋锁)
2)OK,获取锁成功
执⾏业务
get lock判断返回的id是否⼀致
⼀致则释放锁,DEL 删除key即可
不⼀致则不释放锁.
3、异常情况,服务宕机。超时时间EX结束,会⾃动释放锁
3.4 分布式锁版本3
3.4.1 业务场景fito
场景分析
就是实际开发过程中, ⼀段代码内部会有嵌套⽅法, 外层⽅法获取到锁后, 内层再去获取时由于锁已经存在了就⽆法获取了, 但内层代码不执⾏完外层也释放不了锁啊, 这就是⽅法嵌套导致的死锁问题, 怎么解决呢?
解决⽅案
1. 让锁成为可重⼊锁, 也就是外层代码获取到这把锁, 内层代码可以获取到该锁.
2. 获取时判断是不是⾃⼰的锁, 是则继续使⽤, ⽽且要记录重⼊的次数
3. 这⾥的锁不能使⽤之前的String类型作为lock-key的值了, 锁的value要使⽤hash结构
ht lock-key 线程信息, 重⼊次数(默认1) NX PX 毫秒数
key: lock-key
渭南师范学院学报value-key:线程信息
value-value:重⼊次数

本文发布于:2023-06-30 10:50:06,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/162529.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:事务   操作   设置   时间   客户端
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图