当前位置: 首页 > news >正文

基于MySQL的分布式锁实现(Spring Boot + MyBatis)

基于MySQL的分布式锁实现(Spring Boot + MyBatis)

实现原理

基于数据库的唯一索引特性实现分布式锁,通过插入唯一索引记录表示获取锁,删除记录表示释放锁。

1. 创建锁表

首先需要在MySQL中创建一个锁表,用于存储锁信息:

CREATE TABLE `distributed_lock` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`lock_key` varchar(64) NOT NULL COMMENT '锁标识',`request_id` varchar(128) NOT NULL COMMENT '请求唯一标识',`expire_time` datetime NOT NULL COMMENT '过期时间',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_lock_key` (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式锁表';
2. 定义数据访问层

创建Lock实体类和Mapper接口:

// Lock.java
@Data
public class Lock {private Long id;private String lockKey;private String requestId;private LocalDateTime expireTime;private LocalDateTime createTime;
}// LockMapper.java
public interface LockMapper {/*** 获取锁(插入记录)*/int insertLock(Lock lock);/*** 释放锁(删除记录)*/int deleteLock(@Param("lockKey") String lockKey, @Param("requestId") String requestId);/*** 检查锁是否存在*/int checkLockExists(String lockKey);/*** 清除过期的锁*/int clearExpiredLocks();
}// LockMapper.xml
<mapper namespace="com.example.mapper.LockMapper"><insert id="insertLock">INSERT INTO distributed_lock (lock_key, request_id, expire_time)VALUES (#{lockKey}, #{requestId}, #{expireTime})</insert><delete id="deleteLock">DELETE FROM distributed_lock WHERE lock_key = #{lockKey} AND request_id = #{requestId}</delete><select id="checkLockExists" resultType="int">SELECT COUNT(1) FROM distributed_lock WHERE lock_key = #{lockKey}</select><delete id="clearExpiredLocks">DELETE FROM distributed_lock WHERE expire_time < NOW()</delete>
</mapper>
3. 实现分布式锁服务

创建分布式锁服务类,实现锁的获取和释放逻辑:

// DistributedLockService.java
@Service
public class DistributedLockService {private static final Logger logger = LoggerFactory.getLogger(DistributedLockService.class);@Autowiredprivate LockMapper lockMapper;@Autowiredprivate TransactionTemplate transactionTemplate;/*** 获取锁* @param lockKey 锁标识* @param expireSeconds 过期时间(秒)* @return 是否获取成功*/public boolean acquireLock(String lockKey, int expireSeconds) {String requestId = UUID.randomUUID().toString();Lock lock = new Lock();lock.setLockKey(lockKey);lock.setRequestId(requestId);lock.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));try {// 利用唯一索引特性,插入成功则获取锁成功int result = lockMapper.insertLock(lock);if (result > 0) {// 将requestId存入ThreadLocal,用于释放锁时验证ThreadLocalUtil.set("requestId", requestId);return true;}} catch (DuplicateKeyException e) {// 锁已被其他线程持有logger.debug("获取锁失败,锁已存在: {}", lockKey);} catch (Exception e) {logger.error("获取锁异常", e);}// 清除过期锁(可优化为定时任务)clearExpiredLocks();return false;}/*** 释放锁* @param lockKey 锁标识* @return 是否释放成功*/public boolean releaseLock(String lockKey) {String requestId = (String) ThreadLocalUtil.get("requestId");if (requestId == null) {logger.warn("未找到requestId,可能未获取锁或已释放锁");return false;}try {// 在事务中删除锁,确保原子性return transactionTemplate.execute(status -> {int result = lockMapper.deleteLock(lockKey, requestId);if (result > 0) {ThreadLocalUtil.remove("requestId");return true;}return false;});} catch (Exception e) {logger.error("释放锁异常", e);return false;}}/*** 清除过期的锁*/private void clearExpiredLocks() {try {lockMapper.clearExpiredLocks();} catch (Exception e) {logger.error("清除过期锁异常", e);}}
}
4. 使用分布式锁

在需要使用分布式锁的业务方法中调用锁服务:

// OrderService.java
@Service
public class OrderService {@Autowiredprivate DistributedLockService lockService;@Autowiredprivate StockService stockService;/*** 下单扣库存(使用分布式锁)*/public void placeOrder(String productId, int quantity) {String lockKey = "product_lock:" + productId;boolean lockAcquired = false;try {// 获取锁,设置超时时间为10秒lockAcquired = lockService.acquireLock(lockKey, 10);if (lockAcquired) {// 获取锁成功,执行扣库存操作stockService.reduceStock(productId, quantity);// 其他业务逻辑...} else {// 获取锁失败,处理重试或返回失败throw new BusinessException("系统繁忙,请稍后重试");}} finally {// 释放锁if (lockAcquired) {lockService.releaseLock(lockKey);}}}
}
5. 定时任务清理过期锁

为避免数据库中积累过多过期锁记录,添加定时任务定期清理:

// LockCleanupTask.java
@Component
public class LockCleanupTask {@Autowiredprivate LockMapper lockMapper;@Scheduled(fixedDelay = 60 * 1000) // 每分钟执行一次public void cleanupExpiredLocks() {try {int count = lockMapper.clearExpiredLocks();logger.info("清理过期锁完成,共清理: {}", count);} catch (Exception e) {logger.error("清理过期锁异常", e);}}
}
实现说明
  1. 获取锁:通过向数据库插入带有唯一索引的记录实现,插入成功则获取锁成功
  2. 释放锁:通过删除对应记录实现,需验证requestId确保安全性
  3. 锁超时:通过设置expire_time字段实现,配合定时任务清理过期锁
  4. 防误释放:使用ThreadLocal存储requestId,确保锁只能被持有者释放
http://www.lqws.cn/news/546283.html

相关文章:

  • 【数据分析,相关性分析】Matlab代码#数学建模#创新算法
  • 【C语言】知识总结·指针篇
  • 关于SAP产品名称变更通知 SAP云认证实施商工博科技
  • 动态控制click事件绑定
  • H.264中片数据分割(Slice Data Partitioning)介绍
  • Decoder-only PLM GPT1
  • c++异常
  • LINUX625 DNS反向解析
  • gemini-cli 踩坑实录
  • Windows VMWare Centos环境下安装Docker并配置MySql
  • PART 7 视频
  • web布局25
  • iOS打包流程中的安全处理实践:集成IPA混淆保护的自动化方案
  • 消息队列的网络模型详解:IO多路复用、Reactor模型、零拷贝
  • 一键获取服务器硬件脚本:CPU/内存/磁盘/RAID检测脚本详解
  • 电子行业 MES 系统:生产管理的智能引擎
  • Minio的扩容
  • 【docker】docker run参数说明
  • Imbalanced-learn 5. Ensemble of samplers
  • 【水印论文阅读1】将水印规则的定义域从离散的符号空间转移到连续的语义空间
  • 【大模型水印论文阅读2】前缀文本编码、均匀性约束
  • 【linux】程序地址空间
  • 信息抽取领域关键Benchmark方法:分类体系
  • 不同类型的微型导轨精度降低速度有何差异?
  • 专注搜索引擎优化的专业模板平台
  • 【MySQL进阶】服务器配置与管理——系统变量,选项,状态变量
  • CVE-2015-5531源码分析与漏洞复现(Elasticsearch目录遍历漏洞)
  • C语言高级编程
  • 【日志】Unity游戏实习该怎么准备
  • Unity知识点-Renderer常用材质变量