中小型网站建设如何,中天建设集团坑人吗,北京专业网站优化,wordpress如何换主题分布式锁其实就是#xff0c;控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源#xff0c;往往需要互斥来防止彼此干扰#xff0c;以保证一致性。 本篇内容包括#xff1a;关于 Redis 与 分布式锁… 分布式锁其实就是控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源往往需要互斥来防止彼此干扰以保证一致性。 本篇内容包括关于 Redis 与 分布式锁Redis 分布式锁的问题及解决方式Redis 中的 Lua 脚本 以及 Redis 中的 RedLock 算法 文章目录一、关于 Redis 与 分布式锁1、关于分布式锁2、关于 Redis 实现分布式锁二、Redis 分布式锁的问题及解决方式三、Redis 中的 Lua 脚本四、Redis 中的 RedLock 算法1、Redis 中的 RedLock 算法2、 Redlock 算法的客户端的执行步骤一、关于 Redis 与 分布式锁
1、关于分布式锁
在一个分布式系统中当一个线程去读取数据并修改的时候因为读取和更新保存不是一个原子操作在并发时就很容易遇到并发问题进而导致数据的不正确。这种场景很常见比如电商秒杀活动库存数量的更新就会遇到。如果是单机应用直接使用本地锁就可以避免。如果是分布式应用本地锁派不上用场这时就需要引入分布式锁来解决。
一般来说实现分布式锁的方式有以下几种
使用 MySQL基于唯一索引。使用 ZooKeeper基于临时有序节点。使用 Redis基于 setnx 命令。
2、关于 Redis 实现分布式锁
Redis 实现分布式锁主要利用 Redis 的setnx 命令。setnx 是 SET if not exists如果不存在则 SET的简写。
加锁使用setnx key value命令如果 key 不存在设置 value加锁成功。如果已经存在 lock也就是有客户端持有锁了则设置失败加锁失败
解锁使用 del 命令通过删除键值释放锁。释放锁之后其他客户端可以通过 setnx 命令进行加锁。
Key 的值可以根据业务设置比如是用户中心使用的可以命令为USER_REDIS_LOCKvalue 可以使用 uuid 保证唯一用于标识加锁的客户端。保证加锁和解锁都是同一个客户端。 二、Redis 分布式锁的问题及解决方式
首先有一个致命问题就是某个线程在获取锁之后由于某些异常因素比如宕机而不能正常的执行解锁操作那么这个锁就永远释放不掉了。为此我们可以为这个锁加上一个超时时间为此我们可以为这个锁加上一个超时时间
执行 SET key value EX seconds 的效果等同于执行 SETEX key seconds value执行 SET key value PX milliseconds 的效果等同于执行 PSETEX key milliseconds value
然后此时依然会有问题某线程 A 获取了锁并且设置了过期时间为 10s然后在执行业务逻辑的时候耗费了 15s此时线程 A 获取的锁早已被 Redis 的过期机制自动释放了在线程A获取锁并经过 10s 之后改锁可能已经被其它线程获取到了。当线程 A 执行完业务逻辑准备解锁DEL key的时候有可能删除掉的是其它线程已经获取到的锁。当解锁时也就是删除 key 的时候先判断一下 key 对应的 value 是否等于先前设置的值如果相等才能删除 key。
最后这里我们还是一眼就可以看出问题来GET和DEL是两个分开的操作在 GET 执行之后且在 DEL 执行之前的间隙是可能会发生异常的我们引入了一种新的方式就是 Lua 脚本解决原子性的问题。Redis 会将整个 Lua 脚本作为一个整体执行中间不会被其他请求插入。
另外为了防止多个线程同时执行业务代码需要确保过期时间大于业务执行时间可以在代码增加一个线程用于刷新定时过期时间并增加一个 bool 类型的值表示是否开启定时刷新过期时间在线程获取锁的时候将其设置为 true解锁前设置回 false。比如Redisson 实现获取锁成功就会开启一个定时任务定时任务会定期检查去续期。
此外还有一个问题在集群中主节点挂掉时从节点会取而代之客户端上却并没有明显感知。原先第一个客户端在主节点中申请成功了一把锁但是这把锁还没有来得及同步到从节点主节点突然挂掉了。然后从节点变成了主节点这个新的节点内部没有这个锁所以当另一个客户端过来请求加锁时立即就批准了。这样就会导致系统中同样一把锁被两个客户端同时持有不安全性由此产生。此处可以用 RedLock 算法解决。 三、Redis 中的 Lua 脚本
Lua 是一种轻量小巧的脚本语言用标准 C 语言编写并以源代码形式开放。其设计目的就是为了嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能
Redis 在 2.6 版本推出了 lua 脚本功能允许开发者使用 Lua 语言编写脚本传到 Redis 中执行。
使用 Lua 脚本的好处
原子操作。Redis 会将整个脚本作为一个整体执行中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件无需使用事务减少网络开销。可以将多个请求通过脚本的形式一次发送减少网络时延复用。客户端发送的脚本会永久存在 Redis 中这样其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。 四、Redis 中的 RedLock 算法
1、Redis 中的 RedLock 算法
在集群中主节点挂掉时从节点会取而代之客户端上却并没有明显感知。原先第一个客户端在主节点中申请成功了一把锁但是这把锁还没有来得及同步到从节点主节点突然挂掉了。然后从节点变成了主节点这个新的节点内部没有这个锁所以当另一个客户端过来请求加锁时立即就批准了。这样就会导致系统中同样一把锁被两个客户端同时持有不安全性由此产生
Redlock 算法就是为了解决这个问题
使用 Redlock需要提供多个 Redis 实例这些实例之前相互独立没有主从关系。同很多分布式算法一样Redlock 也使用大多数机制
加锁时它会向过半节点发送 set 指令只要过半节点 set 成功那就认为加锁成功。释放锁时需要向所有节点发送 del 指令。不过 Redlock 算法还需要考虑出错重试、时钟漂移等很多细节问题同时因为 Redlock 需要向多个节点进行读写意味着相比单实例 Redis 性能会下降一些
Redlock 算法是在单 Redis 节点基础上引入的高可用模式Redlock 基于 N 个完全独立的 Redis 节点一般是大于 3 的奇数个通常情况下 N 可以设置为 5可以基本保证集群内各个节点不会同时宕机。
2、 Redlock 算法的客户端的执行步骤
当 Redis 集群有 5 个节点运行 Redlock 算法的客户端的执行步骤
客户端记录当前系统时间以毫秒为单位依次尝试从 5 个 Redis 实例中使用相同的 key 获取锁当向 Redis 请求获取锁时客户端应该设置一个网络连接和响应超时时间超时时间应该小于锁的失效时间避免因为网络故障出现的问题客户端使用当前时间减去开始获取锁时间就得到了获取锁使用的时间当且仅当从半数以上的 Redis 节点获取到锁并且当使用的时间小于锁失效时间时锁才算获取成功如果获取到了锁key 的真正有效时间等于有效时间减去获取锁所使用的时间减少超时的几率如果获取锁失败客户端应该在所有的 Redis 实例上进行解锁即使是上一步操作请求失败的节点防止因为服务端响应消息丢失但是实际数据添加成功导致的不一致。