视频地址:https://ke.qq.com/course/3275764?_bid=167&_wv=3&taid=10872245856631796&term_id=103405163&from=iosapp
一、并发设计
1. 直接减库存
存在问题:多个线程会有超卖问题
2.加进程互斥锁
存在问题:单机单进程没问题,多机(负载均衡)会有问题
3.redis 分布式锁之setnx 命令
存在问题:没有处理异常,锁没有超时时间
4. 加上处理异常,加上超时时间
存在问题:加索和超时时间是两条命令,并非原子性
5.用原子性命令
存在问题:
高并发时,问题很大。假如高并发情况下,第一个线程执行需要15s(并发情况下服务器的执行速度会大幅降低),但当执行到了第10s的时候,锁因为超时被释放了,第二个线程就下来了。假设第二个线程执行需要8s,那么第一个线程执行到了第15s时,就会删掉锁,这时候,第二个线程还没执行完,但是锁已经被第一个线程delete掉了,这时候外面的线程又进来了,可能第二个线程又会释放掉第三个线程的锁,这样就全乱套了,这样会导致锁失效了,线程都进来减库存了,还是会有超卖的情况。
6. 将进程id当成锁的value,在执行完检查当前进程的id是否与redis的key的值相等,相等才能释放锁,相当于进程只能释放自己加的锁。
存在问题:
如果在第一个线程判断相等之后,程序卡顿,比如gc,此时刚好10s锁超时,那么第二个线程加锁成功,此时第一个进程再执行delete操作,依然会释放别的进程的锁。
7.锁续命
分线程里面检查超时时间之后,持有锁的进程是否还继续持有该锁,有则重设超时时间。
很复杂,要用成熟的中间件:redisson。
只需要三行:
原理:
利用了lua语言原子性。
存在问题:
1.主从架构锁失效问题;
线程1获取锁成功之后,redis master挂了,cluster 被推选为master,线程2又可以获取锁成功;此时又可能发生超卖问题;
解决方式:用zookeeper做分布式锁,key-value 树形结构
区别(集群架构):redis:满足AP ; zookeeper: 满足CAP
C 一致性,A 可用性,P分区容错性,zookeeper 会在返回客户端结果之前同步所有从节点,如果在写了某节点的时候master挂了,zookeeper的选举机制会保证写成功的节点变为新的leader,意味着key不会丢失。
还有一种解决方式:RedLock(存在一些琐碎的问题,不推荐。)
节点都是对等的,没有主从关系。
2.锁实现了串行,但带来了性能问题
解决方式:分段锁。
将库存分布到多个锁里面,相当于多个队列。
二、并发问题的解决问题
1.缓存数据库双写不一致(时间线非顺序,线程1更新缓存时卡顿,线程2更新了缓存后,线程1又改回去了)
加分布式锁,有性能问题。
解决方式:
一般:读写锁:读和读的线程互斥,写和写互斥。