分布式锁可通过mysql、zk、redis实现,其中redis分布式锁的性能最好,zk的一致性做的最好。
京东上可以抢购茅台,然而10万人抢10瓶茅台,并发量很大,后台程序处理稍不注意就容易产生超卖问题。本篇旨在阐述其中产生的问题以及如何解决。
产生超卖问题后,很多同学都会想到使用synchronized锁,本地jvm进程中多个线程能被锁住,但在分布式环境中则会失效。
即本地锁在多节点下会失效。我们需要使用分布式锁,期望在分布式环境下能够提供锁服务,且达到本地锁的效果
1、为了效率:防止不同节点之间做相同的事情,浪费资源(定时任务)
2、为了安全:有些事情在同一时间只允许一个线程去做
分布式锁:互斥性、锁超时
锁超时:某线程获取锁后,设置超时时间,如果获取锁后执行超时,则释放锁—拒绝死锁的情况
可重入:方法获取锁后,该线程进行递归调用时,再去获取锁,也能获取到锁—避免死锁问题:已获取锁的线程,自己递归去获取锁,如果获取不到,就阻塞
if(setnx(key, value)==1){
expire(key, 30);
try{
//业务
}finally{
//释放锁
del key;
}
}
代码中涉及到从redis中获取锁,并设置超时时间两步操作。
这两步操作需要保持原子性,redis是弱事务,那怎么保证原子性?
解决方案
方法1、lua脚本
方法2、2.6以后用set命令
业务执行时间大于锁超时时间,则产生2个问题:
1、线程A和线程B并行执行;
2、错误解锁—A可能把B的锁解了
解决办法:业务没执行完,锁不应该释放
锁续期,看门狗:给锁续期—A的守护线程
介绍可重入—避免死锁
加锁和解锁一定是成对出现的