线程安全->锁,多线程并发执行的时候,执行先后的顺序,不确定,随机性->需要保证程序在任意执行顺序下,执行逻辑都是ok的,之前学过的锁,mutex,synchronized本质上只能在一个进程内部生效,分布式系统中,是有很多进程的(每个服务器,都是独立的进程)
因此,之前的锁,就难以对现在的分布式系统中的多个进程之间产生制约,因此,分布式系统中,多个进程之间的执行顺序也是不确定的,引入分布式锁解决上述问题。
买票服务器,再进行买票操作过程中,就需要先加锁(往redis上设置一个特殊的key-value
完成上述操作,再把这个key-value删除掉)
其他服务器也想买票,也去redis上尝试设置key-value,如果发现了key-value已经存在,就认为加锁失败(放弃/阻塞,就看具体的实现策略了)就可以保证第一个服务器执行"查询->更新"过程中,第二个服务器不会执行查询,也就解决了上述超卖的问题。)
当然你用mysql事务也可以,但是分布式系统中,要共享的不一定是mysql又可能是其他存储介质(数据库)可能没有事务这个操作。
setnx -不存在就设置,存在就出错
解锁,del命令来完成
极端情况,某个服务器加锁成功,然后后续逻辑的过程中,程序崩了,没有解锁(JAVA中是为了保证解锁能够执行到,把解锁放到finally里面)->分布式系统中无效,进程内部才有效,又可以服务器掉电,无法执行finally,或者进程异常终止,这样的情况,导致redis设置的key,无人删除,导致其他服务器无法获取到这个锁
由此我们引入了过期时间,可以给set的keys设置过期时间的,一旦时间到,key就会被自动删除掉,set ex nx 比如设置key的过期时间为1000ms,意味着即使出现极端情况,某个服务器挂了,没有正确释放锁,这个锁最多保存1000ms,也就会自动释放了
setnx
expire这样分着设置可以吗? 注意⚠️这样设置不可以,redis无法保证原子性,不如一条命令稳妥
所谓的加锁:就是给redis上设置一个key-value
所谓的解锁,就是给redis上这个key-value删除掉
是否会出现,服务器1执行加锁,服务器2执行了解锁?
为了解决上述问题-引入校验机制
1.给服务器编号,每个服务器有自己的身份标识
2.进行加锁的时候,设置key-value-key对应着要对哪个资源加锁(比如车次),value就可以存储刚才服务器的编号,标识出当前这个锁是哪个服务器加上的
后续解锁的时候,就可以进行校验了,先查询一下这个锁对应的服务器编号,然后判定一下这个编号是否就是当前执行解锁的服务器编号,如果是,才真正执行del,如果不是就失败,服务器这边要完成的逻辑,通过上述校验,就可以有效避免误解锁,
在解锁的时候,先查询判定,再进行del->此处是两个操作,可能会出现问题,一个服务器内部,也可能是多线程的,可能同一个服务器,两个线程都在执行上述解锁操作(他是不针对某某,指定某某解锁,而是单纯的解锁,一定解开一个)。
实现中,更好的方案:lua脚本
lua是一个编程语言,作为redis内嵌的脚本,lua语言特别轻量,占用的体积小,可以使用lu a编写一定逻辑,把这个脚本上传到redis服务器上然后就可以让客户端来执行redis上述脚本了,(redis明确说明lua就是事务的替代方案)
过期时间的续约问题:加锁时候给key设置过期时间,那么设置多少合适呢?
时间短:业务逻辑没有执行完,就释放锁了,设置的不长,锁释放不及时的问题
更好的方式"动态续约"->往往也需要服务器有一个专门的线程,负责续约这个事情(看门狗
标签:加锁,Redis,解锁,redis,key,服务器,执行,分布式 From: https://blog.csdn.net/weixin_72953218/article/details/138707199