mybatis-plus乐观锁

mybatis-plus乐观锁

乐观锁vs悲观锁

悲观锁

假设:数据冲突是常态

策略:先锁定,再操作

心态:防御性编程

成本:前期投入大(锁的开销)

场景:写多读少

sql 复制代码
-- 悲观锁的思维方式
BEGIN TRANSACTION;
-- "我不相信别人,先锁住再说"
SELECT * FROM account WHERE id = 1 FOR UPDATE; -- 立即加锁

-- 只有我能操作
UPDATE account SET balance = balance - 100 WHERE id = 1;

COMMIT; -- 最后才释放锁

乐观锁

假设:数据冲突是例外

策略:通过版本号/时间戳检测数据变化,读取时不加锁,更新时检查

心态:乐观协作

成本:失败时重试成本

场景:读多写少

sql 复制代码
UPDATE product 
SET stock = 90, version = version + 1 
WHERE id = 1 AND version = 5
-- 如果version=5的记录存在,更新并返回1
-- 如果version已变(别人改了),更新0行

mybatis-plus乐观锁

添加版本字段

java 复制代码
@Data
@TableName("product")
public class Product {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String name;
    private Integer stock;
    private BigDecimal price;
    
    @Version  // 核心注解!
    private Integer version;
}

配置乐观锁插件

java 复制代码
@Configuration
public class MybatisPlusConfig {
    
    /**
     * 乐观锁插件配置
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        
        return interceptor;
    }
}

使用乐观锁更新

java 复制代码
@Service
@Slf4j
public class ProductServiceV2 {
    
    private static final int MAX_RETRY = 3;
    
  
    @Transactional(rollbackFor = Exception.class)
    public boolean deductStockWithRetry(Long productId, Integer quantity) {
        int retryCount = 0;
        
        while (retryCount < MAX_RETRY) {
            try {
                // 每次重试都需要重新查询最新数据
                Product product = productMapper.selectById(productId);
                
                if (product.getStock() < quantity) {
                    log.warn("库存不足,productId: {}, stock: {}", 
                            productId, product.getStock());
                    return false;
                }
                
                product.setStock(product.getStock() - quantity);
                int rows = productMapper.updateById(product);
                
                if (rows > 0) {
                    log.info("扣减成功,productId: {}, 剩余库存: {}", 
                            productId, product.getStock());
                    return true;
                }
                
                // 更新失败,版本冲突,重试
                retryCount++;
                log.info("版本冲突,第{}次重试,productId: {}", 
                        retryCount, productId);
                
                // 指数退避:减少竞争
                Thread.sleep(50 * retryCount);
                
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new BusinessException("操作被中断");
            }
        }
        
        log.error("扣减失败,达到最大重试次数,productId: {}", productId);
        throw new BusinessException("系统繁忙,请稍后重试");
    }
}

秒杀系统

一般采用 乐观锁 + 分布式锁、

分布式锁控制流量

乐观锁控制数据的一致性

java 复制代码
@Slf4j
@Service
public class SeckillService {
    
    @Autowired
    private RedissonClient redissonClient;
    
    @Autowired
    private ProductMapper productMapper;
    
    public boolean seckillProduct(Long productId, Long userId) {
        String lockKey = "seckill:lock:" + productId;
        RLock lock = redissonClient.getLock(lockKey);
        
        try {
            // 分布式锁:控制并发入口
            if (!lock.tryLock(3, 10, TimeUnit.SECONDS)) {
                throw new BusinessException("排队人数过多,请稍后");
            }
            
            // 乐观锁:保证数据一致性
            return deductStockWithRetry(productId, 1);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new BusinessException("系统繁忙");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}
相关推荐
铁蛋AI编程实战17 小时前
通义千问 3.5 Turbo GGUF 量化版本地部署教程:4G 显存即可运行,数据永不泄露
java·人工智能·python
HyperAI超神经17 小时前
在线教程|DeepSeek-OCR 2公式/表格解析同步改善,以低视觉token成本实现近4%的性能跃迁
开发语言·人工智能·深度学习·神经网络·机器学习·ocr·创业创新
jiang_changsheng17 小时前
RTX 2080 Ti魔改22GB显卡的最优解ComfyUI教程
python·comfyui
R_.L17 小时前
【QT】常用控件(按钮类控件、显示类控件、输入类控件、多元素控件、容器类控件、布局管理器)
开发语言·qt
Zach_yuan17 小时前
自定义协议:实现网络计算器
linux·服务器·开发语言·网络
云姜.17 小时前
java多态
java·开发语言·c++
CoderCodingNo18 小时前
【GESP】C++五级练习题 luogu-P1865 A % B Problem
开发语言·c++·算法
陳103018 小时前
C++:红黑树
开发语言·c++
一切尽在,你来18 小时前
C++ 零基础教程 - 第 6 讲 常用运算符教程
开发语言·c++
泉-java18 小时前
第56条:为所有导出的API元素编写文档注释 《Effective Java》
java·开发语言