1)锁的概念
同步方式:
- 锁
- 原子变量
- volatile
锁的性能优化
- 缩小锁的范围
- 减小锁的粒度
- 锁分段
锁的分类
- 公平锁:synchronized, ReentrantLock
- 非公平锁: ReentrantLock, CAS(自旋锁)
- 独享锁: synchronized, ReentrantLock
- 共享锁:Semaphore
2)分布式锁
- 互斥性:在任意时刻只有一个客户端可以获取锁
- 防死锁:即使有一个客户端持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁
- 持锁人解锁:加锁与解锁必须为同一客户端,客户端自己不能把别人的加的锁给解开了
- 可重入:当一个客户端获取对象锁后,这个客户端可以再次获取本对象上的锁
A. Redis分布式锁
1)加锁流程
2)Redis分布式锁算法
a. 加锁:
- setnx命令,set if not exists
- 设置锁的有效时间防止死锁
- 设置一个随机值用来标识锁的持有人
b. 解锁:
- 检查是否持有锁
- 删除锁
c. 使用lua脚本保证解锁操作的原子性
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end
B. 基于数据库的分布式锁
1)实现方式
- 新建一张锁表
- 获取锁时插入一条数据
- 解锁时删除数据
2)主要问题:
- 可用性差,数据库挂掉会导致业务系统不可用,连接数量有限
- 锁的失效时间难以控制,容易造成死锁
- 非阻塞锁,未获得锁的线程需要需次出发获得锁操作
- 非重入锁,同一个线程在没有释放锁之前无法再次获取该锁
C. 基于zk的分布式锁
1)实现方式:
每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的临时有序节点。判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。
当释放锁的时候,只需将这个临时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。
2)主要问题:
性能一般,加减锁的时候需要通过Leader创建或删除临时节点
D. 分布式锁比较
1)理解成本
数据库 > 缓存 > Zookeeper
2)复杂性
Zookeeper > 缓存 > 数据库
3)性能
缓存 > Zookeeper > 数据库
4)可靠性
Zookeeper > 缓存 > 数据库
3)幂等性
处理幂等性常用的方法
- A. 生成全局唯一id(uuid),借助redis存储
- B. 乐观锁机制(cas)
- C. 悲观锁机制(for update)?
- D. Token机制(类全局id)
- E. 防重表(支付订单号,唯一索引)
- F. 异步处理