Redisson分布式锁 和 乐观锁的使用场景

场景背景

在礼品卡管理系统中,激活礼品卡是一个关键业务操作。该操作需要确保数据一致性,防止因并发操作导致的数据异常。传统的做法是使用Redisson分布式锁来防止并发,但随着业务发展,特别是面对大量礼品卡批量激活的场景,传统分布式锁方案暴露出了一些问题。

为什么不使用Redisson锁

1. 性能问题

当需要激活大量礼品卡(如10万条记录)时,使用Redisson分布式锁会遇到以下性能问题:

  • 大量Redis连接开销:需要为每张礼品卡创建一个分布式锁,导致大量Redis操作
  • 内存占用过高:需要维护大量的锁对象引用
  • 网络延迟累积:每个锁操作都需要网络往返,大量操作会造成显著延迟
  • Redis服务器压力:大量锁操作会给Redis服务器带来巨大压力

Redisson锁适用场景

Redisson分布式锁更适合用于锁定一段"业务流程",而不是具体的数据记录修改。典型的适用场景包括:

1. 房间消费结算流程

在房间消费结算过程中,需要锁定整个结算流程,防止在结算期间添加新的消费项目:

java 复制代码
// 示例:房间消费结算时锁定业务流程
String lockKey = HealthRedisKeyUtil.getLockKey(HealthRedisKeyUtil.consumeRoom, roomId.toString());
RLock rlock = redissonClient.getLock(lockKey);
try{
        rlock.

lock();
// 执行结算流程
// 此时不允许再添加新的消费项目
}finally{
        if(rlock.

isHeldByCurrentThread()){
        rlock.

unlock();
    }
            }

2. 订单创建流程

在创建订单的过程中,需要确保订单创建的原子性,防止重复创建:

java 复制代码
// 示例:订单创建时锁定业务流程
String lockKey = HealthRedisKeyUtil.getLockKey(HealthRedisKeyUtil.order, orderId.toString());
RLock rlock = redissonClient.getLock(lockKey);
try{
        rlock.

lock();
// 执行订单创建流程
}finally{
        if(rlock.

isHeldByCurrentThread()){
        rlock.

unlock();
    }
            }

3. 会员充值、消费、权益扣减等复合操作

这些操作通常涉及多个表的修改,需要保证整个业务流程的原子性:

java 复制代码
// 示例:会员充值流程
String lockKey = HealthRedisKeyUtil.getLockKey(HealthRedisKeyUtil.memberRecharge, memberId.toString());
RLock rlock = redissonClient.getLock(lockKey);
try{
        rlock.

lock();
// 1. 更新会员账户余额
// 2. 创建充值记录
// 3. 发放相关权益
// 整个流程需要保持原子性
}finally{
        if(rlock.

isHeldByCurrentThread()){
        rlock.

unlock();
    }
            }

为什么礼品卡激活适合使用版本号

1. 操作特性匹配

礼品卡激活操作具有以下特点:

  • 单一数据修改:主要是更新gift_card_info表中的状态字段
  • 可独立处理:每张礼品卡的激活可以独立进行,不需要强关联性
  • 非高频操作:相对于实时交易,属于低频操作

2. 版本号机制优势

使用版本号(乐观锁)机制处理礼品卡激活有以下优势:

  • 无锁开销:不需要维护分布式锁,降低系统复杂性
  • 高并发性:允许多张不相关的礼品卡同时激活
  • 精确冲突检测:能准确识别并处理并发冲突
  • 良好的可重试性:失败的操作可以针对性重试
  • 优秀的扩展性:无论是10条还是10万条记录都可以统一处理

3. 实现方案

sql 复制代码
-- 数据库表结构修改
ALTER TABLE gift_card_info
    ADD COLUMN version INT DEFAULT 0;
java 复制代码
// GiftCardInfo实体类添加版本号字段
@TableField("version")
private Integer version = 0;

// 更新时使用版本号进行乐观锁控制,并结合状态机验证
UpdateWrapper<GiftCardInfo> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", giftCardInfo.getId());
updateWrapper.eq("version", giftCardInfo.getVersion()); // 版本号检查
updateWrapper.eq("giftUseStatus", GiftUseStatus.unactivated); // 状态机验证:只有未激活的才能激活
updateWrapper.eq("status", Status.enable); // 状态机验证:只有启用状态的才能激活
updateWrapper.set("giftUseStatus", GiftUseStatus.activated);
updateWrapper.set("effectiveTime", new Date()); // 激活时需要设置生效时间
updateWrapper.setSql("version = version + 1"); // 版本号递增

总结

在礼品卡激活这类场景中,使用版本号乐观锁替代Redisson分布式锁是更为合适的选择。这种方案既保证了数据一致性,又避免了分布式锁的各种性能和可靠性问题。而对于需要锁定整个业务流程的场景,如房间消费结算、订单创建等,Redisson分布式锁仍然是首选方案。

相关推荐
闪电悠米3 分钟前
黑马点评短信登录01_session_sms_login
java·spring boot·redis·git·spring·面试
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ3 分钟前
MySQL选择字符集和排序规则
数据库·mysql
努力努力再努力wz4 分钟前
【QT入门系列】QWidget 六大常用属性详解:windowOpacity、cursor、font、focus、toolTip 与 styleSheet
android·开发语言·数据结构·c++·qt·mysql·算法
Advancer-5 分钟前
黑马点评plus --异步秒杀重构升级
java·spring boot·重构·intellij-idea
Dicky-_-zhang8 分钟前
服务网格实战:Istio与Linkerd对比选型与落地实践
java·jvm
旺仔Sec9 分钟前
HBase 分布式集群部署实战:从解压到启动的完整指南
数据库·分布式·hbase
云烟成雨TD10 分钟前
Spring AI Alibaba 1.x 系列【56】SAA Admin 平台功能介绍
java·人工智能·spring
Gauss松鼠会11 分钟前
GaussDB(DWS) 资源监控Topsql
java·网络·数据库·算法·oracle·性能优化·gaussdb
夏日听雨眠11 分钟前
数据结构(快速排序)
java·数据结构·算法
周易宅13 分钟前
Docker MySQL 8.0.45 性能优化配置文档
mysql·docker·性能优化