目录
分布式锁
1、jvm层面的加锁,单机锁
2、分布式锁
分布式微服务架构,拆分后各个服务之间为了避免冲突和数据故障而加入的一种锁
问题
以下这种架构会带来的问题就是:如果redis存储数据,多台springboot修改Redis,也就是多台springboot各有一个线程对这个数据进行访问,而数据时不能同时被两个线程同时修改的,就会出现问题
分布式锁原理
jvm锁原理:为了防止两个线程对同一堆内存数据访问,增加一个第三方标记这个数据是否正在被一个线程访问,synchronized中第三方就是自己的对象头,对象头中标记有锁还是无锁,lock就是lock对象里的信号量;
而在分布式,数据存储在所有springboot共享的区域,jvm锁只能在一个服务内起作用,这时就需要分布式锁,而分布式锁也是通过第三方信号来标记这个数据是否正在被访问,这个第三方可以使redis也可以是其他信号量;
有分布式锁还需要jvm锁吗?
分布式锁可以保证所有进程只有一个线程对该数据访问,不需要jvm锁也可以保证数据安全;但是会增加网络的IO,所以会在每个进程内使用jvm锁达到类似过滤效果,提高性能
两大类分布式锁
类cas自旋式
mysql redis
一个线程访问到数据然后加上了锁,其他访问该数据的线程不断以询问的方式自旋获取锁;自旋询问时不是依靠cpu了而是网络IO,性能大大下降
event事件通知锁状态
zookeeper etcd
一个线程访问到数据然后加上了锁,其他线程看见数据被锁就类似阻塞,等到这条数据释放锁,通知一个正在等待的线程,告知它可以访问
Redis分布式锁
出现问题与解决
案例
一个基础的分布式业务逻辑
单机超卖问题
给整个方法加锁,两种锁看状态
- sync:下死命令,进来抢锁,必须抢完完成业务
- lock:有trylock(),尝试抢锁,可是设定时间,抢不到可以释放
分布式问题
使用反向代理负载均衡访问多个服务
JMeter做压测,输入线程数和http请求
高并发单机锁还是能出现超卖,一个商品买了两次
单机锁没办法解决分布式问题
- 声明锁
- 加锁释放锁
保证释放锁
如果出现异常的话,可能无法释放锁
必须在 finally代码块里释放锁
redis宕机如何保证释放锁
给锁设置过期时间,到时间后自动释放锁
设置时间不保证原子性
写在一起保证原子性
业务代码过多,锁超过时间自动释放
- A线程进来拿到锁,处理自己的业务超过10秒,锁自动释放
- B线程进来发现没有锁,自己加了一把锁
- A线程完成任务,释放了过期的锁,删了别人的锁
- B线程锁被释放,其他线程都能进来
判断,只能删自己的锁
finally块里的判断和解锁不是原子的
使用 lua 脚本:Jedis.eval(script)
用其他方法
set两条数据保证原子
watch:乐观锁,监控期间有人把数据改了,就取消提交,释放这次操作,不覆盖
使用Redis事务保证原子性删除锁
锁过期时间续期
业务逻辑没执行完,锁续期,缓存续命
主从结构锁丢失
异步复制导致锁丢失
主节点没来得及把刚set的数据给从节点,就挂了
Redisson
直接用Redisson获得锁、加锁lock()、释放锁unlock()
直接释放锁在超高并发也会出现问题,所以要判断一下
1.是否持有锁
2.是否当前线程所持有