Redis的锁

2021/9/19 19:36:03

本文主要是介绍Redis的锁,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

redis的锁分为乐观锁和悲观锁。

一、悲观锁(分布式锁)

这里解释一下,普通锁和分布式锁的区别:
1.普通锁:synronize和lock,处理的是单节点(一个进程)多线程并发的问题,保证数据安全;
2.分布式锁:redis, zk, mysql的分布式锁,处理的是多个节点的多个进程并发的安全问题

1.为什么需要分布式锁

多个进程,需要同时操作一个共享资源,需要达到互斥的效果。

2.分布式锁如何实现

SET lock_key $unique_id EX $expire_time NX

通过一条指令设置锁过期时间,保证原子性.

锁过期时间不好评估,该如何?
加锁时,先设置一个过期时间,然后我们开启一个「守护线程」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就自动对锁进行「续期」,重新设置过期时间。(Redisson已封装好了,直接用)

小结:

  • 死锁:设置过期时间
  • 过期时间评估不好,锁提前过期:守护线程,自动续期
  • 锁被别人释放:锁写入唯一标识,释放锁先检查标识,再释放
3.集群模式下,是否依旧安全?

在使用 Redis 时,一般会采用主从集群 + 哨兵的模式部署,当主库异常宕机时,哨兵可以实现「故障自动切换」。

试想这样的场景:
客户端 1 在主库上执行 SET 命令,加锁成功
此时,主库异常宕机,SET 命令还未同步到从库上(主从复制是异步的)
从库被哨兵提升为新主库,这个锁在新的主库上,丢失了!
怎么办?。。。

解决方案:RedLock(红锁)

4.RedLock的操作过程

整体的流程是这样的,一共分为 5 步:
(1)客户端先获取「当前时间戳T1」
(2)客户端依次向这 5 个 Redis 实例发起加锁请求(用前面讲到的 SET 命令),且每个请求会设置超时时间(毫秒级,要远小于锁的有效时间),如果某一个实例加锁失败(包括网络超时、锁被其它人持有等各种异常情况),就立即向下一个 Redis 实例申请加锁
(3)如果客户端从 >=3 个(大多数)以上 Redis 实例加锁成功,则再次获取「当前时间戳T2」,如果 T2 - T1 < 锁的过期时间,此时,认为客户端加锁成功,否则认为加锁失败
(4)加锁成功,去操作共享资源(例如修改 MySQL 某一行,或发起一个 API 请求)
(5)加锁失败,向「全部节点」发起释放锁请求(前面讲到的 Lua 脚本释放锁)

总结,有 4 个重点:
(1)客户端在多个 Redis 实例上申请加锁
(2)必须保证大多数节点加锁成功
(3)大多数节点加锁的总耗时,要小于锁设置的过期时间
(4)释放锁,要向全部节点发起释放锁请求

关于NPT难题

  • 网络异常,网络延迟
  • 进程暂停
  • 时钟漂移

参考文章:https://mp.weixin.qq.com/s/ybiN5Q89wI0CnLURGUz4vw?_share_channel=wechat

二、乐观锁(Watch机制)

关键词:watch(盯住), multi(开始), exec(执行)

原理:

1.watch在事物开始之前盯住一个或多个变量
2.在执行(exec)事物队列时,会检查关键变量自watch之后是否被修改了
3.如果被修改了,则返回null,执行失败,客户端一般选择重试。



这篇关于Redis的锁的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程