【Java面试】高并发下,如何做到安全的修改同一行数据
安全修改同一行数据的核心是。
·

🔒 一、悲观锁(Pessimistic Locking)
原理:先加锁再操作,确保独占访问。
实现方式:
BEGIN TRANSACTION;
SELECT * FROM table WHERE id = 1 FOR UPDATE; -- 加行级锁
UPDATE table SET column = value WHERE id = 1;
COMMIT;
适用场景:写操作频繁、冲突概率高的场景(如秒杀扣库存)。
优缺点:
- ✅ 强一致性,避免并发冲突;
- ❌ 锁竞争可能导致线程阻塞,降低吞吐量;
- ❌ 需控制事务粒度,避免长事务引发死锁。
🔄 二、乐观锁(Optimistic Locking)
原理:无锁检查,冲突重试,基于版本号/时间戳校验。
实现方式:
- 表中增加
version字段; - 更新时校验版本号:
UPDATE table SET column = new_value, version = version + 1
WHERE id = 1 AND version = old_version;
若返回影响行数为0,则重试或放弃。
适用场景:读多写少(如配置更新、轻度并发扣减)。
优化:
- 结合重试机制(如指数退避算法);
- 业务层设计幂等操作,避免重复提交副作用。
⏱️ 三、MVCC(多版本并发控制)
原理:读旧写新,版本隔离,通过快照读避免阻塞。
实现方式:
- 数据库默认支持(如MySQL InnoDB的
REPEATABLE READ隔离级别); - 写操作创建新版本,读操作访问旧快照。
适用场景:读写混合的高并发场景(如余额查询与转账)。
注意: - 需合理设置隔离级别(如避免
READ UNCOMMITTED); - 可能引发幻读问题,需配合间隙锁(Gap Lock)。
🌐 四、分布式锁(Distributed Lock)
原理:全局互斥,跨节点协调,适用于分布式系统。
实现方式:
| 方案 | 实现要点 | 适用场景 |
|---|---|---|
| Redis锁 | SET lock_key unique_val NX EX 30 + Lua脚本原子释放 |
高吞吐要求(如活动库存) |
| ZooKeeper锁 | 创建临时有序节点,监听前序节点删除事件 | 强一致性场景(如金融交易) |
| 数据库锁 | 基于唯一约束插入锁记录(INSERT INTO lock_table(...)) |
无中间件的简单系统 |
关键设计:
- 锁自动续期(看门狗机制),避免业务未完成锁过期;
- 锁标识(如UUID)防误删,确保释放者为持有者;
- 容灾设计(如Redis集群模式、ZooKeeper多数存活)。
📊 五、方案选型建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 单机写冲突高 | 悲观锁 | 强一致性,实现简单 |
| 读多写少,冲突概率低 | 乐观锁 | 无锁竞争,吞吐量高 |
| 读写混合,事务隔离要求高 | MVCC | 读写互不阻塞,兼顾性能与一致性 |
| 分布式系统,跨节点协调 | Redis/ZooKeeper锁 | 全局互斥,高可用 |
⚠️ 六、通用优化原则
- 减小锁粒度:锁单行而非全表(如
WHERE id=1); - 缩短持有时间:事务内仅保留必要操作,避免远程调用;
- 异步化处理:写操作入队列(如Kafka),消费端串行处理;
- 监控与兜底:
- 数据库锁等待监控(
SHOW ENGINE INNODB STATUS); - 重试次数限制,防雪崩;
- 数据校对任务,修复不一致。
- 数据库锁等待监控(
🌰 七、实战案例:电商库存扣减
// 方案1:悲观锁(强一致)
try (Connection conn = dataSource.getConnection()) {
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareStatement("SELECT stock FROM sku WHERE sku_id=? FOR UPDATE");
ps.setString(1, "SKU_001");
ResultSet rs = ps.executeQuery();
if (rs.next() && rs.getInt("stock") > 0) {
// 扣减库存
executeUpdate("UPDATE sku SET stock=stock-1 WHERE sku_id='SKU_001'");
}
conn.commit();
}
// 方案2:乐观锁(高并发)
int retry = 0;
while (retry++ < 3) {
Sku sku = dao.selectById("SKU_001");
if (sku.getStock() > 0) {
int rows = dao.update("UPDATE sku SET stock=stock-1, version=version+1
WHERE sku_id='SKU_001' AND version=" + sku.getVersion());
if (rows > 0) break; // 更新成功退出
}
}
💡 总结:安全修改同一行数据的核心是 权衡一致性与性能。单机场景优先用数据库锁(悲观/乐观),分布式环境需引入全局协调(Redis/ZK),最终通过异步队列和监控兜底保障稳定性。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)