从哪里开始说呢,直接开题吧,在高并发写库的情况下,如何和避免“超卖”现象,参考(https://www.cnblogs.com/gxyandwmm/p/9588383.html)
超卖现象,意思是,卖出了超过一开始设定的阈值
举个场景例子:我想卖10本书,结果,因为没处理好并发问题,卖出了大于10本。
1,单机的话,可以用jdk锁,synchronized或者lock等等都可以,但现在很少有单机的了,都是集群,所以,我们主要介绍分布式锁
2,分布式锁
下面是同学们能写出的常见的代码:
伪码1: if(setnx(key,1) == 1){ expire(key,30) try { do something ...... }catch() { } finally { del(key) } }
但上面的代码存在的问题如下:
1,当执行完if后挂了,那锁那长期持有,无法释放,但redis2.0版本后,,修复了这个bug,其实也不算bug吧,总之插入key值与设置过期时间变成原子操作了
2,继续往下走,设置超时时间,举个极端反例
线程A得到锁,过期时间30秒,但30秒内没有完成,锁自动释放
这时候,线程B当然可以得到锁了
这时候,线程A正好执行完了,执行finally中的del操作来释放锁,可想而知,这时候释放的是线程B正在持有的锁
接触锁与加的锁不一致
如何解决:锁名称加uuid或者线程id
3,超大并发情况下,哪怕线程A释放的就是自己的锁,但依旧也有两个线程同时在执行同一段代码
解决:续费过期时间,思想可以参考redisson,q其实也可以直接用redisson一行代码搞定。
4,主从redis,设置锁后,主挂了,主还未将锁信息同步到从,则从没有锁的信息,会有另一个线程也获取到锁,锁便失去了意义
咋办:好像redis没办法,只能用zookeeper了
简答说一下zookeeper的原理吧,其实我也不是很清楚,主要区别是zookeeper有一种机制叫临时节点,当第一个请求来了后,或建立一个临时节点,后面的线程便会依次排队,等待锁的释放。当zookeeper检测到连接断了时,会将临时节点删掉,这样,下一个线程便能获取锁。