这里解释一下,普通锁和分布式锁的区别:
1.普通锁:synronize和lock,处理的是单节点(一个进程)多线程并发的问题,保证数据安全;
2.分布式锁:redis, zk, mysql的分布式锁,处理的是多个节点的多个进程并发的安全问题
多个进程,需要同时操作一个共享资源,需要达到互斥的效果。
SET lock_key $unique_id EX $expire_time NX
通过一条指令设置锁过期时间,保证原子性.
锁过期时间不好评估,该如何?
加锁时,先设置一个过期时间,然后我们开启一个「守护线程」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就自动对锁进行「续期」,重新设置过期时间。(Redisson已封装好了,直接用)
小结:
在使用 Redis 时,一般会采用主从集群 + 哨兵的模式部署,当主库异常宕机时,哨兵可以实现「故障自动切换」。
试想这样的场景:
客户端 1 在主库上执行 SET 命令,加锁成功
此时,主库异常宕机,SET 命令还未同步到从库上(主从复制是异步的)
从库被哨兵提升为新主库,这个锁在新的主库上,丢失了!
怎么办?。。。
解决方案:RedLock(红锁)
整体的流程是这样的,一共分为 5 步:
(1)客户端先获取「当前时间戳T1」
(2)客户端依次向这 5 个 Redis 实例发起加锁请求(用前面讲到的 SET 命令),且每个请求会设置超时时间(毫秒级,要远小于锁的有效时间),如果某一个实例加锁失败(包括网络超时、锁被其它人持有等各种异常情况),就立即向下一个 Redis 实例申请加锁
(3)如果客户端从 >=3 个(大多数)以上 Redis 实例加锁成功,则再次获取「当前时间戳T2」,如果 T2 - T1 < 锁的过期时间,此时,认为客户端加锁成功,否则认为加锁失败
(4)加锁成功,去操作共享资源(例如修改 MySQL 某一行,或发起一个 API 请求)
(5)加锁失败,向「全部节点」发起释放锁请求(前面讲到的 Lua 脚本释放锁)
关于NPT难题
参考文章:https://mp.weixin.qq.com/s/ybiN5Q89wI0CnLURGUz4vw?_share_channel=wechat
关键词:watch(盯住), multi(开始), exec(执行)
1.watch在事物开始之前盯住一个或多个变量
2.在执行(exec)事物队列时,会检查关键变量自watch之后是否被修改了
3.如果被修改了,则返回null,执行失败,客户端一般选择重试。