MyBatis-Plus + Lock4j 分布式锁教程

发布时间:2026/6/8 15:23:39

MyBatis-Plus + Lock4j 分布式锁教程 一、Lock4j 简介Lock4j 是阿里巴巴开源的分布式锁组件支持 Redis、Zookeeper 等多种实现与 Spring Boot 无缝集成。二、快速开始1. 添加依赖dependency groupIdcom.baomidou/groupId artifactIdlock4j-redis-template-spring-boot-starter/artifactId version2.2.0/version /dependency !-- 如果使用 Redis需要同时引入 Redis 依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency2. 配置文件spring: redis: host: localhost port: 6379 lock4j: # 锁的默认过期时间(ms)超时自动释放 acquire-timeout: 3000 # 获取锁的超时时间(ms)超时则获取失败 expire: 30000 # 主线程等待锁的时间超时则抛出异常 primary-timeout: 30003. 基础使用Service public class UserService { Autowired private UserMapper userMapper; // 基本用法锁的key为 user:lock: id Lock4j(keys {#id}, expire 10000, acquireTimeout 3000) public void updateUser(Long id, String name) { User user userMapper.selectById(id); user.setName(name); userMapper.updateById(user); } }三、核心注解详解Lock4j 注解参数参数说明默认值keys锁的key支持SpEL表达式空expire锁的过期时间(ms)配置中的值acquireTimeout获取锁超时时间(ms)配置中的值name锁的名称前缀空四、实战示例1. 防重复提交RestController public class OrderController { PostMapping(/order/create) Lock4j(keys {#userId}, expire 5000, acquireTimeout 1000) public Result createOrder(RequestBody OrderVO orderVO, RequestParam Long userId) { // 业务逻辑 return Result.success(); } }2. 库存扣减MyBatis-Plus 操作Service public class ProductService { Autowired private ProductMapper productMapper; Lock4j(keys {#productId}, expire 5000, acquireTimeout 3000) Transactional(rollbackFor Exception.class) public boolean deductStock(Long productId, Integer quantity) { // 1. 查询商品 Product product productMapper.selectById(productId); // 2. 检查库存 if (product.getStock() quantity) { throw new RuntimeException(库存不足); } // 3. 扣减库存 product.setStock(product.getStock() - quantity); int result productMapper.updateById(product); return result 0; } }3. 复杂SpEL表达式Service public class BizService { // 使用多个参数组成key Lock4j(keys {#userId, #orderId}) public void processOrder(Long userId, String orderId) { // 业务逻辑 } // 使用对象属性 Lock4j(keys {#user.id, #user.name}) public void updateUser(Param(user) User user) { // 业务逻辑 } // 自定义key前缀 Lock4j(name order:lock, keys {#orderId}) public void handleOrder(String orderId) { // 最终key: order:lock:orderId值 } }4. 手动获取锁Service public class ManualLockService { Autowired private LockTemplate lockTemplate; public void doWithLock(String resourceId) { // 手动获取锁 LockInfo lockInfo lockTemplate.lock(resourceId, 10000L, 3000L); if (lockInfo null) { throw new RuntimeException(获取锁失败); } try { // 业务逻辑 System.out.println(执行业务逻辑); } finally { // 释放锁 lockTemplate.releaseLock(lockInfo); } } }5. 配合 LambdaQueryWrapperService public class AccountService { Autowired private AccountMapper accountMapper; Lock4j(keys {#accountId}, expire 10000) Transactional public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) { // 使用 LambdaQueryWrapper 查询 LambdaQueryWrapperAccount fromWrapper Wrappers.AccountlambdaQuery() .eq(Account::getId, fromAccountId); Account fromAccount accountMapper.selectOne(fromWrapper); LambdaQueryWrapperAccount toWrapper Wrappers.AccountlambdaQuery() .eq(Account::getId, toAccountId); Account toAccount accountMapper.selectOne(toWrapper); // 扣款 fromAccount.setBalance(fromAccount.getBalance().subtract(amount)); accountMapper.updateById(fromAccount); // 加款 toAccount.setBalance(toAccount.getBalance().add(amount)); accountMapper.updateById(toAccount); } }五、高级配置1. 自定义Key生成器Component public class CustomKeyGenerator implements LockKeyGenerator { Override public String generateKey(LockInfo lockInfo, Method method, Object[] args) { // 自定义key生成逻辑 return custom: method.getName() : Arrays.hashCode(args); } } // 使用方式 Lock4j(keys {#id}, keyGenerator CustomKeyGenerator.class) public void customKeyMethod(Long id) { // ... }2. 自定义锁失败处理Component public class CustomLockFailureHandler implements LockFailureHandler { Override public void onLockFailure(String key, Method method, Object[] args) { // 锁获取失败时的处理 throw new BusinessException(系统繁忙请稍后重试); } }3. 执行器配置Configuration public class Lock4jConfig { Bean public ExecutorService lockExecutor() { return Executors.newFixedThreadPool(10); } }六、注意事项1. 事务与锁的顺序// ✅ 正确锁在外事务在内 Lock4j(keys {#id}) Transactional public void correctWay(Long id) { // 业务逻辑 } // ❌ 错误事务在外锁在内可能导致事务未提交锁已释放 Transactional Lock4j(keys {#id}) public void wrongWay(Long id) { // 业务逻辑 }2. 避免死锁// 多个锁时注意顺序 Lock4j(keys {#id1}) public void method1(Long id1, Long id2) { // 调用需要锁id2的方法 - 可能导致死锁 method2(id2); } Lock4j(keys {#id2}) public void method2(Long id2) { // ... }3. 合理设置超时时间lock4j: # 根据业务执行时间设置建议比业务执行时间长30% expire: 5000 # 业务平均执行3秒设置5秒 # 获取锁超时时间不宜过长 acquire-timeout: 2000七、常见问题解决1. 锁未释放问题// 使用 try-finally 确保释放 LockInfo lockInfo lockTemplate.lock(key, 10000, 3000); try { // 业务操作 // 注意不要在业务代码中提前return } finally { if (lockInfo ! null) { lockTemplate.releaseLock(lockInfo); } }2. 重试机制Component public class RetryLockService { Autowired private LockTemplate lockTemplate; public void executeWithRetry(String key, Runnable task, int maxRetries) { for (int i 0; i maxRetries; i) { LockInfo lockInfo lockTemplate.lock(key, 10000, 3000); if (lockInfo ! null) { try { task.run(); return; } finally { lockTemplate.releaseLock(lockInfo); } } // 等待后重试 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } throw new RuntimeException(获取锁失败重试 maxRetries 次); } }八、性能优化建议锁粒度尽量使用细粒度锁如按订单ID、用户ID超时设置根据业务耗时合理设置避免过长或过短监控告警添加锁等待时间、失败率的监控降级方案锁服务不可用时考虑本地锁或限流九、最佳实践总结Service Slf4j public class BestPracticeService { /** * 推荐的最佳实践模板 */ Lock4j( keys {#bizId}, // 精确的锁key expire 10000, // 比业务执行时间长30-50% acquireTimeout 2000 // 合理等待时间 ) Transactional(rollbackFor Exception.class) public Result doBusiness(String bizId, BusinessData data) { // 1. 参数校验 if (StringUtils.isEmpty(bizId)) { throw new IllegalArgumentException(业务ID不能为空); } // 2. 业务处理 try { // MyBatis-Plus 数据库操作 return Result.success(); } catch (Exception e) { log.error(业务处理失败, bizId: {}, bizId, e); throw new BusinessException(处理失败); } } }

相关新闻