两个人同时操纵数据库,怎么处理并发问题?

两个人同时(或多个并发用户/线程/进程)操作同一个数据库,最核心的问题是可能导致数据不一致、丢失更新、脏读、幻读等异常。

数据库(以及应用层)主要通过并发控制(Concurrency Control) 来解决这个问题。下面从最常见、最实用的角度给你讲清楚怎么处理。

1. 数据库层面的主流解决方案(最推荐优先使用)

控制方式核心思路什么时候加锁/检查冲突典型实现方式适用场景优缺点对比
悲观锁 (Pessimistic)先假设会冲突,先锁住再说读/写时就加锁SELECT … FOR UPDATE
行锁、表锁
高冲突场景(如库存扣减、抢票、转账)并发低,但安全;可能死锁或等待时间长
乐观锁 (Optimistic)先假设不会冲突,最后提交时再检查只在提交时检查版本号(version)
或时间戳、CAS
读多写少、低冲突场景并发高,但冲突时需重试;适合大部分业务
MVCC 多版本并发控制每条记录保留多个历史版本,读不阻塞写读写都不锁(或轻锁)InnoDB 的 undo log + 隐藏列(trx_id, roll_ptr)几乎所有现代 OLTP 数据库(MySQL InnoDB、PostgreSQL、Oracle 等)读写并发最高;空间换时间,需定期清理旧版本

结论:2025–2026 年主流做法

  • 大部分业务首选 MVCC + 乐观锁 的组合(读不阻塞写,写冲突靠版本号解决)
  • 高冲突、强一致性场景(如秒杀、账户余额)再加 悲观锁分布式锁

2. 经典例子:库存扣减(两个人同时买最后一个商品)

场景:商品库存 = 1,A 和 B 同时下单。

方式1:悲观锁(数据库行锁)

-- 事务A 先执行
BEGIN;
SELECT stock FROM goods WHERE id = 1 FOR UPDATE;   -- 拿到行锁,stock=1
UPDATE goods SET stock = stock - 1 WHERE id = 1;
COMMIT;

-- 事务B 同时执行,会在 FOR UPDATE 这里阻塞等待,直到A提交后才继续
-- 如果超时或死锁,可能回滚

优点:简单、安全,不会超卖
缺点:并发上来锁等待严重,吞吐下降

方式2:乐观锁(版本号 / CAS)

-- 事务A
SELECT stock, version FROM goods WHERE id = 1;   -- 读到 stock=1, version=10
-- 业务逻辑...
UPDATE goods 
SET stock = stock - 1, version = version + 1 
WHERE id = 1 AND version = 10;   -- 如果 version 还是10才更新成功

-- 事务B 同时读到 version=10
-- 但A先提交,version变成11
-- B的 UPDATE 影响行数=0 → 说明冲突,重试或提示“库存不足”

优点:高并发时不阻塞读,性能好
缺点:冲突时需要业务重试(通常重试3–5次)

方式3:真正的库存安全写法(推荐组合)

-- 方式3:乐观锁 + 行级条件更新(最常用)
UPDATE goods 
SET stock = stock - 1 
WHERE id = 1 AND stock > 0;   -- 数据库原子判断

-- 然后检查 affected rows
-- 如果 == 1 → 扣减成功
-- 如果 == 0 → 库存不足,失败

或者用 SELECT … FOR UPDATE + 乐观锁 双保险(高并发下仍可能有性能瓶颈)。

3. 应用层辅助手段(数据库锁不够用时)

当单机数据库锁粒度不够、或分布式系统时,常用这些:

手段适用场景实现工具/方式备注
分布式锁分布式系统、微服务、多库Redis(Redlock)、ZooKeeper、etcd强一致,但引入外部依赖
消息队列削峰高并发场景先排队Kafka / RabbitMQ 顺序消费最终一致性,延迟稍高
幂等性 + 防重表防止重复扣款/重复下单唯一索引 / 业务唯一键 + 防重表必须配合幂等设计
事务补偿 / Saga分布式事务Seata、DTM、Saga 模式最终一致性,适合长事务

4. 快速决策表(根据你的业务选)

你的业务场景推荐优先级顺序
库存、余额、积分、抢票1. UPDATE + 条件判断
2. 乐观锁 version
3. 悲观锁 FOR UPDATE
普通表单修改(文章、配置)MVCC + 乐观锁(版本号)
读多写少(浏览量、点赞)纯乐观锁 或 延迟写(Redis + 定时同步)
分布式系统、高并发扣款分布式锁 + 防重 + 幂等
最终一致性可接受(日志、统计)消息队列 + 幂等消费

一句话总结:

大多数业务先用“数据库原生的 MVCC + 乐观锁/条件更新”就能解决 80% 的并发问题,性能和复杂度都可控。只有真正高冲突、强一致性场景才需要悲观锁或分布式锁。

你的具体场景是什么?是库存扣减、订单状态变更、还是其他?是单机 MySQL/PostgreSQL,还是分布式(TiDB、CockroachDB 等)?告诉我,我可以给你更精确的代码/方案。

文章已创建 4725

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部