Skip to content

tags: [并发控制, 并发] category: 架构设计 date: 2026-08-01 banner: /images/banner-database.webp title: 什么场景不需要并发控制? ​

什么场景不需要并发控制? ​

在系统设计中,我们往往过度关注“如何加锁”,却忽略了“何时不该加锁”。并发控制的本质是防止多个事务同时修改同一数据时产生不一致。但如果场景中根本不存在并发修改风险,或者业务对数据一致性的要求极低、可以容忍短暂的异常,那么强行引入乐观锁或悲观锁,反而会带来不必要的性能开销和代码复杂度。

以下 7 类场景,通常不需要显式的应用层并发控制:

1. 纯只读操作 ​

  • 报表查询与 BI 展示:仅执行 SELECT,读取历史快照或当前数据,自然没有写冲突。
  • API 只读接口:如商品详情页、文章内容展示。

注意:即使读取过程中有其他事务在写入,除非业务严格要求“可重复读”或“完全无幻读”,否则普通的读操作无需加锁。隔离级别的选择(如 Read Committed)通常已足够应对。

2. 一次性数据导入/初始化 ​

  • 系统初始化脚本:仅在部署时运行一次。
  • 离线数据迁移:在维护窗口期由单一任务执行,期间屏蔽其他写操作。

这类场景本质上是单线程串行执行,物理上不存在并发,自然无需并发控制。

3. 仅追加的日志/审计表 ​

  • 操作日志、点击流、消息存档:这类表的设计模式是 Append-Only(仅追加),从不更新或删除已有行。

由于每个事务都在插入新行,不存在对同一行的并发更新,行级冲突概率为零。无论是乐观锁还是悲观锁,在这里都是多余的。

4. 低一致性要求的统计字段 ​

  • 页面访问计数器、点赞数:允许少量丢失或短暂不精确。

关键区分:这里说的“不需要并发控制”,是指不需要应用层的乐观/悲观锁。如果你使用 UPDATE table SET views = views + 1,数据库底层的行锁已经保证了这条语句的原子性。只有当你采用“先 SELECT 再在内存计算后 UPDATE”的贫血模型时,才需要应用层锁。

5. 分布式最终一致性场景 ​

  • 异步数据同步:通过消息队列(MQ)异步更新数据,允许短暂不一致,且更新顺序可能乱序,但业务可接受。
  • 缓存更新:缓存与数据库之间不要求严格实时一致。

这类场景的解法通常是消息队列 + 重试机制,而非数据库层面的锁。用业务层的最终一致性换取系统的高吞吐。

6. 单用户/单线程环境 ​

  • 桌面单机应用:只有一个用户操作本地数据库。
  • 单实例后台任务:通过调度框架确保同一时刻只有一个任务实例运行。

既然从架构上已经杜绝了并发,代码中就可以完全省略并发控制逻辑。

7. 利用数据库原子操作替代应用层锁 ​

这是最容易被忽视的一点。很多开发者习惯在应用层加锁,其实数据库自身的能力往往更可靠:

  • 库存扣减:UPDATE stock SET num = num - 1 WHERE id = ? AND num > 0。这一条语句本身就是原子的,数据库行锁保证了不会超卖,无需应用层重试。
  • 幂等更新:INSERT ... ON DUPLICATE KEY UPDATE。
  • 条件更新:UPDATE ... SET version = version + 1 WHERE id = ? AND version = ?。这本质上是乐观锁,但如果你不关心是否更新成功(无需重试),它就只是一条普通的原子 SQL。

原则:如果单条 SQL 就能完成业务逻辑,并且结果正确性仅依赖数据库的原子性,那么不需要额外的并发控制。


场景决策速查表 ​

场景是否需要应用层并发控制核心原因
纯只读查询不需要无写冲突
一次性导入/初始化不需要单线程,物理无并发
仅追加的日志表不需要不同行无冲突 (Append-Only)
非关键计数器 (+=1)不需要数据库行锁已保证原子性
最终一致性异步更新不需要业务容忍短暂不一致
单用户/单线程应用不需要架构层面已规避并发
单条原子 SQL 更新不需要数据库自身已处理并发

总结 ​

不要为了并发而并发。

只要没有多个事务同时修改同一行数据,或者数据库的原子操作(Atomic Operation)已经足够保证业务正确性,就可以大胆地去掉乐观锁或悲观锁。

大多数简单的增减库存、计数器更新,一条 UPDATE 语句足矣。过度设计并发控制,往往是系统复杂度和性能瓶颈的万恶之源。

Released under the MIT License.