MySQL 中的乐观锁和悲观锁是两种不同的并发控制策略,它们各有特点和适用场景。
一、悲观锁(Pessimistic Locking)
概念
悲观锁假设数据冲突经常发生,因此在操作数据之前先获取锁,确保在事务期间数据不会被其他事务修改。
实现方式
使用数据库内置的锁机制:
-- 1. 开始事务 START TRANSACTION; -- 2. 加排他锁(悲观锁) SELECT * FROM products WHERE id = 1 FOR UPDATE; -- 3. 执行业务逻辑 UPDATE products SET stock = stock - 1 WHERE id = 1; -- 4. 提交事务 COMMIT;
特点
思想:先加锁,后操作实现:依赖数据库锁机制(行锁、表锁等)适用场景:写操作频繁的场景数据竞争激烈的环境需要强一致性的业务优缺点
优点:
保证强一致性实现简单,直接使用数据库功能适合高并发写场景缺点:
性能开销大(加锁、释放锁)可能产生死锁降低系统并发度二、乐观锁(Optimistic Locking)
概念
乐观锁假设数据冲突很少发生,因此不加锁直接操作,只在提交时检查数据是否被修改过。
实现方式
通常使用版本号或时间戳:
方法1:版本号机制
-- 表结构增加版本字段 CREATE TABLE products ( id INT PRIMARY KEY, name VARCHAR(100), stock INT, version INT DEFAULT 0 ); -- 更新时检查版本号 UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 1; -- 这里的1是读取时的版本号 -- 检查影响行数,如果为0表示版本不一致,需要重试
方法2:时间戳机制
-- 表结构增加时间戳字段 CREATE TABLE products ( id INT PRIMARY KEY, name VARCHAR(100), stock INT, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 更新时检查时间戳 UPDATE products SET stock = stock - 1 WHERE id = 1 AND update_time = '2024-01-01 10:00:00';
方法3:条件判断(基于业务字段)
-- 基于库存数量判断 UPDATE products SET stock = stock - 1 WHERE id = 1 AND stock > 0;
重试机制
由于乐观锁可能失败,通常需要配合重试逻辑:
// Java 示例代码 public boolean deductStockWithRetry(int productId, int quantity) { for (int i = 0; i < MAX_RETRY; i++) { Product product = productDao.getById(productId); if (product.getStock() < quantity) { return false; // 库存不足 } int affectedRows = productDao.updateStock(productId, quantity, product.getVersion()); if (affectedRows > 0) { return true; // 更新成功 } // 版本冲突,重试 Thread.sleep(100); // 短暂等待 } throw new RuntimeException("更新失败,超过最大重试次数"); }
特点
思想:先操作,提交时检查实现:基于应用层逻辑(版本号、时间戳等)适用场景:读多写少的场景数据冲突较少的业务对性能要求高的系统优缺点
优点:
提高系统并发性能避免死锁问题减少数据库锁开销缺点:
实现相对复杂需要处理更新失败的情况不适合高并发写场景三、对比总结
四、实际应用建议
选择悲观锁的场景
银行转账、库存扣减等金融业务需要强一致性的核心业务写操作非常频繁的系统选择乐观锁的场景
商品浏览、社交媒体的点赞功能读多写少的业务系统对性能要求较高的互联网应用混合使用策略
在实际项目中,可以根据不同业务场景混合使用:
-- 核心业务使用悲观锁 START TRANSACTION; SELECT * FROM account WHERE user_id = 123 FOR UPDATE; -- 执行资金操作 COMMIT; -- 非核心业务使用乐观锁 UPDATE article SET like_count = like_count + 1 WHERE id = 456 AND version = current_version;
五、注意事项
- 悲观锁要合理设置事务隔离级别和锁等待超时时间乐观锁要设计合理的重试策略和重试次数上限在高并发场景下,乐观锁的重试可能造成"惊群效应"根据业务特点选择合适的锁策略,不要一概而论
这两种锁策略各有优劣,关键在于根据具体的业务场景和性能要求来选择最合适的方案。
到此这篇关于MySQL乐观锁和悲观锁的使用的文章就介绍到这了,
