- 设计思路
- 基于配置中心:利用配置中心(如 Apollo、Nacos 等)来管理不同业务场景下分布式锁的过期时间配置。这样可以在不重启系统的情况下,动态修改过期时间。
- 锁服务分层:将锁服务分为两层,外层为通用的分布式锁获取与释放逻辑,内层根据不同业务场景处理过期时间。
- 事件驱动:通过事件机制来感知业务场景的切换,例如使用消息队列(如 Kafka)来传递场景切换事件。
- 具体实现
public boolean acquireLock(String lockKey, long acquireTimeout, TimeUnit unit) {
// 通用的分布式锁获取逻辑,如使用 Redis 实现
Jedis jedis = jedisPool.getResource();
String lockValue = UUID.randomUUID().toString();
try {
long result = jedis.setnx(lockKey, lockValue);
if (result == 1) {
return true;
} else {
long endTime = System.nanoTime() + unit.toNanos(acquireTimeout);
while (System.nanoTime() < endTime) {
result = jedis.setnx(lockKey, lockValue);
if (result == 1) {
return true;
}
Thread.sleep(100);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
jedis.close();
}
return false;
}
- **根据场景设置过期时间**:
public void setLockExpireTime(String lockKey, long expireTime, TimeUnit unit) {
Jedis jedis = jedisPool.getResource();
try {
jedis.expire(lockKey, (int) unit.toSeconds(expireTime));
} catch (Exception e) {
e.printStackTrace();
} finally {
jedis.close();
}
}
- **场景切换处理**:
- **事件监听**:通过消息队列监听业务场景切换事件,例如:
@KafkaListener(topics = "scene_switch_topic", groupId = "lock_group")
public void handleSceneSwitch(String scene) {
String lockKey = "product_sale_lock";
if ("pre_sale".equals(scene)) {
long expireTime = Long.parseLong(config.getProperty("pre_sale_lock_expire_time"));
setLockExpireTime(lockKey, expireTime, TimeUnit.SECONDS);
} else if ("sale_start".equals(scene)) {
long expireTime = Long.parseLong(config.getProperty("sale_start_lock_expire_time"));
setLockExpireTime(lockKey, expireTime, TimeUnit.SECONDS);
} else if ("sale_mid".equals(scene)) {
long expireTime = Long.parseLong(config.getProperty("sale_mid_lock_expire_time"));
setLockExpireTime(lockKey, expireTime, TimeUnit.SECONDS);
}
}
- 保证高并发性能和数据一致性
- 高并发性能:
- 锁优化:使用乐观锁、分段锁等技术减少锁竞争。例如,在商品库存扣减时,可根据商品 ID 进行分段加锁,不同段商品可并行处理。
- 缓存:在抢购前将商品库存等数据加载到缓存(如 Redis)中,减少数据库压力,提高响应速度。在锁获取成功后,先在缓存中进行库存扣减,再异步更新数据库。
- 数据一致性:
- 分布式事务:使用分布式事务框架(如 Seata)来保证在锁操作、库存扣减、订单生成等一系列操作的原子性。
- 日志记录:记录所有锁操作和业务操作日志,以便在出现问题时进行数据恢复和一致性校验。