业务场景
- 防止用户重复下单
- MQ消息去重
- 订单操作变更
- 库存超卖
分析:
业务场景共性:
共享资源:用户id、订单id、商品id。。。
解决方案
- 共享资源互斥
- 共享资源串行化
问题转化
锁的问题 (将需求抽象后得到问题的本质)
锁应用
单应用中使用锁:(单进程多线程)synchronized、ReentrantLock
分布式应用中使用锁:(多进程多线程)分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
解决分布式下并发访问:
try{
if(getLock()){
扣减库存
}
}finally{
unlock();
}
分布式锁特性:
- 互斥性:我持有则别人不能持有
- 同一性:我加锁,别人不能解锁
- 可重入性:我加锁,锁超时,则锁失效,别人可以加锁;我加锁,还可再次加锁(多次持有)
Redis实现分布式锁
原理:利用Redis的单线程特性对共享资源进行串行化处理
获取锁:
方式一
/**
* 使用redis的set命令实现获取分布式锁 推荐使用
* @param lockKey 可以就是锁
* @param requestId 请求ID,保证同一性 uuid+threadID
* @param expireTime 过期时间,避免死锁
* @return
*/
public boolean getLock(String lockKey,String requestId,int expireTime) {
//NX:保证互斥性
// hset 原子性操作
String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
if("OK".equals(result)) {
return true;
}
return false;
}
方式二 (使用setnx命令实现) -- 并发会产生问题
public boolean getLock(String lockKey,String requestId,int expireTime) {
Long result = jedis.setnx(lockKey, requestId);
if(result == 1) {
//成功设置 失效时间
jedis.expire(lockKey, expireTime);
return true;
}
return false;
}
释放锁
方式1(del命令实现) -- 并发
/
**
* 释放分布式锁
* @param lockKey
* @param requestId
*/
public static void releaseLock(String lockKey,String requestId) {
if (requestId.equals(jedis.get(lockKey))) {
jedis.del(lockKey);
}
}
方式2(redis+lua脚本实现)--推荐
public static boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get&