在我们的日常开发中,一个进程中当多线程的去竞争某一资源的时候,我们通常会用一把锁来保证只有一个线程获取到资源。如加上synchronize关键字或ReentrantLock锁等操作。
那么,如果是多个进程相互竞争一个资源,如何保证资源只会被一个操作者持有呢?
server1、server2、server3 这三个服务都要修改amount这个数据,每个服务更新的值不同,为了保证数据的正确性,三个服务都向lock server服务申请修改权限,最终server2拿到了修改权限,即server2将amount更新为2,其他服务由于没有获取到修改权限则返回更新失败。
特征核心实现 单实例我们这里直接使用Lua脚本来保证原子性,其中
NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。
EX seconds :设定key的过期时间,时间单位是秒。
PX milliseconds: 设定key的过期时间,单位为毫秒
加锁
使用 Setnx 命令保证互斥性。
需要设置锁的过期时间,避免死锁。
Setnx 和设置过期时间需要保持原子性,避免在设置 Setnx 成功之后在设置过期时间客户端崩溃导致死锁。
加锁的 Value 值为一个唯一标示。由于分布式任务随机数都不可靠,可以(MAC地址+JVM进程ID) 作为唯一标示。加锁成功后需要把唯一标示返回给 Client 来用进行解锁操作。
脚本如下:
redis.call('set',key,value,'NX','PX',30000)
解锁
需要拿加锁成功的唯一标示要进行解锁,从而保证加锁和解锁的是同一个客户端。
解锁操作需要比较唯一标识是否相等,相等再执行删除操作。这 2 个操作可以采用 Lua 脚本方式使 2 个命令的原子性
脚本如下:
redis.call('get',key == value then return tostring(redis.call('del', key)==1) else return 'false' end
Redlock实现
redlock由来上面琐大的缺点就是它加锁时只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况:
例如,在Redis的master节点上拿到了锁,但是这个加锁的key还没有同步到slave节点,此时master故障,发生故障转移,slave节点升级为master节点,导致锁丢失。
正因为如此,Redis作者antirez基于分布式环境下提出了一种更高级的分布式锁的实现方式:Redlock。
其核心思想为:
实现步骤搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。
参考链接: 一文搞懂 Redis 分布式锁
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧