一:什么是分布式锁。
- 通俗来说的话,就是在分布式架构的redis中,使用锁。
二:分布式锁的使用选择。
- 当 Redis 的使用场景不多,而且也只是单个在用的时候,可以构建自己使用的 锁。
- 如果在公司里落地生产环境用分布式锁的时候,一般是会用开源类库。
- Redis分布式锁,一般就是用 Redisson 框架就好了,非常的简便易用。
三:Redisson 实现Redis分布式锁的原理。
- 整体流程图
-
- 详解
- 当某个请求的客户端需要加锁时候。
- 如果该客户端面对的是一个redis cluster集群,他首先会根据hash算法选择一台机器。(一致hash算法)
- 随后会向Redis发送加锁请求(原理为Lua脚本)
- Lua 脚本会保证业务执行的原子性,所以采用 Lua 的方式为加锁命令。
-
- 这个Lua 脚本的大致意思为(这个Key的锁是否存在,如果存在,则一直循环等待,如不存在,则上锁,并设置过期时间。)
- watch dog 提供的锁自动延期的能力
- 加锁的锁key默认生存时间才30秒,如果超过了30秒,客户端1还想一直持有这把锁,怎么办呢?
- RedisSon 只要加锁成功,就会启动一个watch dog看门狗, 他是一个后台线程,会每隔10秒检查一下 ,如果还持有锁key,那么就会不断的延长锁key的生存时间。
四:分布式锁锁带来的问题
- 在分布式架构中的问题,就是如果你对某个redis master实例,写入了key的锁,此时会异步复制给对应的master slave实例。
- 但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master。
- 接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。
- 此时就会导致多个客户端对一个分布式锁完成了加锁。
- 这时系统在业务语义上一定会出现问题, 导致各种脏数据的产生 。
- 这个就是redis cluster/redis master-slave架构的 主从异步复制 导致的redis分布式锁的问题:在redis master实例宕机的时候,可能导致多个客户端同时完成加锁。
五:简单实现的redis锁
- 原理
- 通过 redis 的 setnx 功能进行加锁
- 实现(PHP)
-
-
<?php /** * 创建 Redis 单例 * @return mixed */ class redisInstance { static $redis; private function __construct() { } public static function getInstance() { if (self::$redis) { return self::$redis; } self::$redis = new Redis(); return self::$redis; } } class redisLock { public $reids; public function __construct() { $this->reids = redisInstance::getInstance(); } /** * 键加锁,默认加锁时间为 1分钟 = 60 * 1000(毫秒) * @param $key * @param int $ttl * @return bool */ public function lock($key, $ttl = 60000) { $lockKey = $key . '_lock'; // 通过setNx命令拿到锁 $lock = $this->reids->set($lockKey, 1, ['NX', 'PX' => $ttl]); // 拿到锁则直接返回 if ($lock) { return true; } // 没有拿到锁,则一直循环等待锁资源释放 while (!$lock) { $lock = $this->reids->set($key, 1, ['NX', 'PX' => $ttl]); } return $lock; } /** * 释放锁 * @param $key * @return bool */ public function unLock($key) { $lockKey = $key . '_lock'; $lock = false; // 释放锁 while (!$lock) { $lock = $this->reids->del($lockKey); } return true; } } $redis = redisInstance::getInstance(); $redisLock = new redisLock(); /** * 例如,买商品,进行库存递减 * 1:对库存加锁 * 2:递减 * 3:释放锁 * 影响 * 加锁导致的并发度降低 */ $key = 'stock'; $redisLock->lock($key); $stock = $redis->get($key); if ($redis->get($key) <= 0) { $redisLock->unLock($key); return false; } $redis->decr($key); $redisLock->unLock($key);
-