1、分布式锁
实现原理:分布式锁的核心思想是通过 Redis 这种分布式缓存系统来控制多台机器之间的锁竞争。当一个客户端(如用户的请求)获得锁时,其他客户端不能同时执行相同的操作,从而避免超卖问题。
核心要求:互斥性、可重入性、防死锁、高性能、高可用。
使用Redis来实现分布式锁,尤其在处理并发预约号源,能有效避免并发请求导致的超卖问题。
2、Redis分布锁实现,解决并发预约号源超卖问题
2.1 Redis分布锁
利用 Redis 单线程执行命令 + 原子操作的特性,在多实例、多进程环境下,实现共享资源的互斥访问。
特点:
- 加锁原子性:只有第一个执行 SETNX(SET if Not eXists)的线程获得锁。
- 可设置过期时间:防止死锁。
- 高性能:Redis 内存存储,单线程,响应快。
2.2 Redis分布锁实现基本流程
- 客户端请求获得分布式锁。
- Redis 设置锁的键值对,若存在则说明锁被占用。
- 客户端在执行操作(如预定号源)前,先检查是否获取到了锁。
- 执行操作后,客户端释放锁。
2.3 SETNX + EXPIRE(传统方式)
原理:先使用SETNX设置锁,成功后设置过期时间
public boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
// 1. 尝试获取锁
Long result = jedis.setnx(lockKey, requestId);
if (result == 1) {
// 2. 设置过期时间(非原子操作!)
jedis.expire(lockKey, expireTime);
return true;
}
return false;
}
问题:
- 非原子性:SETNX成功但EXPIRE失败时,锁将永久存在,导致死锁
- 锁误删:可能删除其他客户端的锁(未使用唯一标识)
- 无重入性:同一线程无法重入
2.4 SET NX PX
原理:使用Redis 2.6.12+的SET命令扩展,支持原子性设置锁和过期时间
public boolean tryLockWithSet(Jedis jedis, String lockKey, String requestId, int expireMillis) {
String result = jedis.set(lockKey, requestId, "NX", "PX", expireMillis);
return "OK".equals(result);
}
// 释放锁(需保证原子性)
public boolean unlock(Jedis jedis, String lockKey, String requestId) {
// 使用Lua脚本保证原子性
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";
Object result = jedis.eval(luaScript,
Collections.singletonList(lockKey),
Collections.singletonList(requestId));
return result.equals(1L);
}
优势:
- 原子性:单条命令完成锁设置和过期时间
- 避免死锁:确保过期时间一定被设置
- 简单可靠:生产环境常用方案
2.5 Redisson (企业级解决方案)
原理:基于Netty的Redis客户端,提供完整的分布式锁实现。
// Maven依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.23.5</version>
</dependency>
// 使用示例
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
try {
// 尝试加锁,最多等待100秒,上锁后30秒自动解锁
boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS);
if (isLocked) {
// 执行业务逻辑
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
优点
- 看门狗机制:自动续期锁,避免业务执行时间过长导致锁过期。
- 支持可重入锁(同一个线程可多次获取锁)、读写锁(读读共享,读写互斥)、信号量(控制并发数),适合复杂分布式场景。
- 封装原子性、超时处理、异常安全,极大降低死锁风险。