从零开发短视频电商 分布式锁-基于Redis实现


书接上文 — > 从零开发短视频电商 分布式锁-基于数据库实现

实现

相关文档https://redis.io/docs/manual/patterns/distributed-locks/

加锁

在Redis中加锁非常简便,直接使用SET命令即可。示例及关键选项说明如下:

SET resource_1 random_value NX EX 5
参数/选项说明
resource_1分布式锁的key,只要这个key存在,相应的资源就处于加锁状态,无法被其它客户端访问。
random_value一个随机字符串,不同客户端设置的值不能相同。 uuid:线程id 参考 redisson
EX设置过期时间,单位为秒。您也可以使用PX选项设置单位为毫秒的过期时间。
NX如果需要设置的key在Redis中已存在,则取消设置。

示例代码为resource_1这个key设置了5秒的过期时间,如果客户端不释放这个key,5秒后key将过期,锁就会被系统回收,此时其它客户端就能够再次为资源加锁并访问资源了。

Spring可以直接使用 redisTemplate.opsForValue().setIfAbsent(K key, V value, Duration timeout)

解锁

解锁一般使用DEL命令,一个客户端设置的锁,必须由自己解开。因此客户端需要先使用GET命令确认锁是不是自己设置的,然后再使用DEL解锁。在Redis中通常需要用Lua脚本来实现自锁自解:

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

续租

当客户端发现在锁的租期内无法完成操作时,就需要延长锁的持有时间,进行续租(renew)。同解锁一样,客户端应该只能续租自己持有的锁。在Redis中可使用如下Lua脚本来实现续租:

续租 需要每个锁后台定时任务 时间建议是锁的1/3 去续租

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("expire",KEYS[1], ARGV[2])
else
    return 0
end

集群或主从一致性问题

Redis的主从同步(replication)是异步进行的,如果向master发送请求修改了数据后master突然出现异常,发生高可用切换,缓冲区的数据可能无法同步到新的master(原replica)上,导致数据不一致。如果丢失的数据跟分布式锁有关,则会导致锁的机制出现问题,从而引起业务异常。下文介绍两种保障一致性的方法。

使用红锁(RedLock)

红锁是Redis作者提出的一致性解决方案。红锁的本质是一个概率问题:如果一个主从架构的Redis在高可用切换期间丢失锁的概率是k%,那么相互独立的N个Redis同时丢失锁的概率是多少?如果用红锁来实现分布式锁,那么丢锁的概率是(k%)^N。Redis节点越多则一致性越强,鉴于Redis极高的稳定性,此时的概率已经完全能满足产品的需求。

说明 红锁的实现并非这样严格,一般保证M(1<M<=N)个同时锁上即可,但通常仍旧可以满足需求。

红锁的问题在于:

  • 加锁和解锁的延迟较大。
  • 难以在集群版或者标准版(主从架构)的Redis实例中实现。
  • 占用的资源过多,为了实现红锁,需要创建多个互不相关的云Redis实例或者自建Redis。

使用WAIT命令

Redis的WAIT命令会阻塞当前客户端,直到这条命令之前的所有写入命令都成功从master同步到指定数量的replica,命令中可以设置单位为毫秒的等待超时时间,实现成本低。在云Redis版中使用WAIT命令提高分布式锁一致性的示例如下:

SET resource_1 random_value NX EX 5
WAIT 1 5000

使用以上代码,客户端在加锁后会等待数据成功同步到replica才继续进行其它操作,最大等待时间为5000毫秒。执行WAIT命令后如果返回结果是1则表示同步成功,无需担心数据不一致。相比红锁,这种实现方法极大地降低了成本。

需要注意的是:

  • WAIT只会阻塞发送它的客户端,不影响其它客户端。
  • WAIT返回正确的值表示设置的锁成功同步到了replica,但如果在正常返回前发生高可用切换,数据还是可能丢失,此时WAIT只能用来提示同步可能失败,无法保证数据不丢失。您可以在WAIT返回异常值后重新加锁或者进行数据校验。
  • 解锁不一定需要使用WAIT,因为锁只要存在就能保持互斥,延迟删除不会导致逻辑问题。

最后

说了这么多,我觉得不要造轮子的话,可以使用redisson的分布式锁,这个开源项目内置很多分布式对象,很多开源的分布式锁都是基于他来做的。它也内置了看门狗,但是如果加锁的时候指定了过期时间,那么 Redission 不会给你开启看门狗的机制

参考:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
基于Redis可以使用以下步骤实现分布式锁: 1. 获取锁: - 使用Redis的SETNX命令(SET if Not eXists)尝试获取锁。将锁的名称作为Redis的key,唯一标识符(如UUID)作为value。 - 如果SETNX返回1,表示成功获取到锁,执行业务逻辑。 - 如果SETNX返回0,表示锁已被其他客户端占用,等待一段时间后重试或进行其他处理。 2. 设置锁的过期时间: - 为了避免锁的持有者发生异常或崩溃而无法释放锁,需要为锁设置一个过期时间。可以使用Redis的EXPIRE命令设置锁的过期时间。 - 保证在获取锁后,业务逻辑执行成功并释放锁之前,锁不会过期。 3. 释放锁: - 使用Redis的DEL命令删除锁的key,以释放锁。 - 在执行业务逻辑完成后,通过判断锁的value是否为当前客户端的唯一标识符来确保只有锁的持有者才能释放锁。 需要注意的是,分布式锁实现还需要考虑以下情况: - 死锁检测:如果持有锁的客户端发生异常或崩溃,导致无法正常释放锁,需要设置一个合理的过期时间,以确保锁能够被自动释放。 - 重试机制:在获取锁时,如果锁已被其他客户端占用,可以根据业务需求进行等待一段时间后重试,或者使用指数退避等策略来避免竞争激烈时的频繁重试。 - 高可用性:当Redis服务器发生故障或网络分区时,需要确保分布式锁的可用性。可以使用Redis的主从复制、哨兵模式或集群模式等来提高系统的可用性。 另外,为了简化分布式锁实现,也可以考虑使用已经实现好的分布式锁框架,如Redlock、Curator等。这些框架提供了更完善和可靠的分布式锁解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lakernote

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值