当前位置: 首页 > 科技观察

Redis分布式锁解决多进程-多线程单进程-单线程操作

时间:2023-03-13 07:06:39 科技观察

1.简介在业务开发中,像写订单一样,一般需要单线程来保证订单写入数据库,防止多次插入数据。最近,有两个容器。程序运行时,会发送多个通知,所以需要保证同一时间只有一个进程(一个容器)在运行。这时候分布式锁就是用来解决这个问题的。这种方案业界也有很多解决方案,这里我们使用redis分布式锁来解决。简单的说,使用golang的redis库可以实现如下方案。2、redis的分布式锁实现2.1setnx+expiresetnxkeyvalue,设置key为value,只有当key不存在时,才成功,如果key存在,什么都不做,成功返回1,成功返回0失败。SETNX实际上是SETIFNOTExists的缩写。setnxkeyvalexpirekeyseconds但是上面两个操作不是原子的。如果执行完第一条指令,应用程序异常或重启,锁不会过期。2.2由于lua脚本的原子性无法保证,可以采用执行lua脚本的原子性,将上述两个操作封装到lua脚本中来实现。ifredis.call('setnx',KEYS[1],ARGV[1])==1thenredis.call('expire',KEYS[1],ARGV[2])elsereturn0end;2.3setcarryingTTLfromRedis2.6.12从version1开始,SET命令的行为可以通过一系列的参数来修改。SETkeyvalue[EXseconds][PXmilliseconds][NX|XX]将字符串值value关联到key。如果键已经包含另一个值,无论类型如何,SET都会覆盖旧值。对于一个原本就有生存时间(TTL)的key,当对该key执行SET命令成功后,该key原有的TTL将被清除。EXsecond:设置key的过期时间为second秒。SETkeyvalueEXsecond相当于SETEXkeysecondvalue。PXmillisecond:设置密钥过期时间为millisecond毫秒。SETkeyvaluePXmillisecond相当于PSETEXkeymillisecondvalue。NX:仅当密钥不存在时才设置密钥。SET键值NX等同于SETNX键值。XX:仅当密钥已存在时才设置密钥。直接使用可能会出现以下问题:超时解锁导致并发。例如:线程A成功获取到锁,设置了30秒的过期时间,但是线程A执行超过30秒,锁就过期了,自动释放。这时,线程B已经获得了锁。A和线程B并发执行。两个线程A和B并发出现显然是不允许的。一般有两种方法可以解决这个问题:解决方法:1)保证代码在过期时间之前发布。2)为获取锁的线程增加守护线程,为即将过期但还未释放的锁增加有效时间。锁被另一个线程意外删除。例如:如果线程A成功获取到锁,设置了30秒的过期时间,但是线程A执行超过30秒,锁过期自动释放,此时线程B获取到锁;然后A执行完毕,线程A使用DEL命令释放锁,但是线程B加的锁还没有执行完,线程A实际上释放了线程B加的锁。解决方法是:通过设置锁标志value中当前线程的锁,在删除前验证key对应的value,判断锁是否被当前线程持有。可以生成一个UUID来标识当前线程,可以使用lua脚本来验证标识和解锁操作。学习文章:https://xiaomi-info.github.io/2019/12/17/redis-distributed-lock/https://zhuanlan.zhihu.com/p/115848078