简单了解分布式锁
对于分布式锁,我们希望有如下特点
资源互斥:能在分布式条件下对资源占用进行限制
死锁:可重入,可超时释放,避免死锁
高性能、高可用:加锁、解锁效率高
安全性:锁只能被锁持有的客户端删除
分布式锁实现方式
数据库分布式锁
创建一个锁表,其中一个唯一字段存储需要锁的方法或者资源,比如卖票接口,锁住接口或者票的库存
通过唯一字段的insert、delete操作,实现加锁、释放锁
缺点:
- 锁没有失效时间,可能导致死锁(定时任务扫库)
- 锁无法重入,同一个线程释放锁之前无法再次获得锁(参考synchronized的偏向锁,加个字段判断是否是本线程)
- 访问数据库涉及I/O操作,效率较低(考虑使用缓存)
- 非阻塞操作失败后,需要轮询重试,占用cpu资源
ZooKeeper分布式锁
暂时不了解,以后找时间再补充,性能没有Redis分布式锁好
Redis分布式锁
主要关注超时释放的死锁问题、以及锁误删的安全性问题
1 | 加锁 |
Redis单机环境
使用redis原生
第一版:setnx
锁没有超时时间,可能会死锁
- 线程崩溃,可以在finally块释放锁
- 服务宕机,锁无法释放
第二版:setnx+ expire
无法保证加锁和添加锁过期时间的原子性,可能会死锁
第三版:set key value ex nx
存在误删,比如线程一执行业务时间太久,此时是线程二持有锁,释放了线程二的锁
第四版:set key value ex nx + 创建当前线程的唯一标识UUID,在执行完业务之后,对照是否是本线程再删除锁
判断锁和删除锁并不是原子性的,所以可能还是会存在误删
第五版:set key value ex nx + 创建当前线程的唯一标识UUID,在执行完业务之后,对照是否是本线程再删除锁 + lua
- 减少网络开销:原本我们需要向 Redis 服务请求多次命令,可以将命令写在 Lua 脚本中,这样执行只会发起一次网络请求。
- 原子操作:Redis 会将 Lua 脚本中的命令当作一个整体执行,中间不会插入其它命令。
- 复用:客户端发送的脚步会存储 Redis 中,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。
缺点:
- 过期时间不好控制,需要略大于业务时间,否则业务还未结束锁就被释放了
- 非阻塞操作失败后,需要轮询重试,占用cpu资源
使用Redisson框架
第六版:Redisson分布式锁
底层使用了看门狗机制,每10s检查一次锁,自动续期,保证业务完成锁才释放
缺点:主从复制不及时没有同步锁信息的同时,主节点宕机,导致锁被重复获取
Redis集群环境
第七版:RedLock + Redisson
Redis集群的处理方式,获取到半数以上主节点的锁才能算上锁成功
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 白兰!