在日常开发中,像常见的秒杀扣库存这种需要访问同一资源的情况,并发读写时如果没有任何防护,很容易出现数据错乱的问题,比如超卖,库里的数据可能会出现负数。为解决此问题,我们引入了分布式锁,通常通过 Redis 来实现。它能让同一时刻只允许一个线程访问资源,超卖问题也就迎刃而解。
然而,分布式锁又带来了不少新麻烦,比如死锁问题,这出现的频率还挺高。常见场景有:加锁后没有程序去释放锁,一直被占用就产生了死锁;虽然写了释放锁的程序,但加锁后程序挂了,也会产生死锁;还有加锁时的原子性问题,没加上过期时间,程序一挂同样产生死锁等等。总的来说,死锁大多是因为加了锁却没释放,导致其他线程拿不到锁,造成大面积请求失败。
所以,解决问题的关键在于保障分布式锁能够正常释放。释放锁主要有两种方式,一是直接删掉锁的 key,二是设置过期时间。删掉锁的 key 时要注意,首先得判断锁是否被占用,占用了才执行删除操作,还要判断是否是当前线程加的锁,不能误删别的线程加的锁,同时要保证操作的原子性,这可以通过 lua 脚本来实现。设置过期时间的话,大家应该都不陌生,一般先 set nx,然后设置过期时间,到期就直接释放。不过这个步骤不是原子性的,好在从 Redis 2.6 开始,引入了新的 set 命令,可以直接设置锁的同时设置过期时间,方便了许多,或者也可以通过 lua 脚本来执行 set 和 expire 操作。
好了,以上就是关于分布式锁死锁问题的相关内容。