mobile wallpaper 1
2401 字
6 分钟
第 9 章 数据库并发控制
2026-07-02

本章介绍数据库并发控制的基本概念,详细阐述事务的 ACID 特性、并发操作引发的数据不一致问题,以及通过 S 锁与 X 锁构建的三级封锁协议。

9.1 并发控制与事务基础#

9.1.1 什么是并发控制#

在多用户数据库系统中,多个用户或应用程序可能会同时(并发地)访问或修改同一个数据。并发控制 (Concurrency Control) 就是通过特定的技术和机制,协调这些并发操作的执行,保证数据库的 一致性 (Consistency)隔离性 (Isolation),防止因并发冲突导致数据被破坏。

9.1.2 什么是事务 (Transaction)#

数据库中的 事务 (Transaction) 是用户定义的一个数据库操作序列。这些操作要么全做,要么全不做,是一个不可分割的逻辑工作单位。 在部分基础介绍中,事务有时也会被称为“事件”,其核心和本质即数据库的事务。

9.1.3 事务的 ACID 特性#

事务是并发控制的基本单位。一个合法的事务必须满足 ACID 四大特性:

  1. 原子性 (Atomicity):事务中的操作是不可分割的。事务中包含的所有操作要么全部成功提交,要么全部失败回滚 (Rollback)。
  2. 一致性 (Consistency):事务必须使数据库从一个一致性状态过渡到另一个一致性状态。例如,转账操作中,双方的总金额在转账前后必须保持守恒。
  3. 隔离性 (Isolation):一个事务的执行不能被其他并发事务干扰。并发执行的事务之间必须相互隔离,事务的中间状态对其他事务不可见。
  4. 持久性 (Durability):一旦事务提交 (Commit),它对数据库中数据的改变应该是永久性的,即使数据库随后发生系统崩溃或停电,修改结果也不会丢失。

9.2 并发操作带来的数据不一致问题#

如果不对并发操作进行控制,当多个事务并发读写相同的数据对象时,可能会导致以下 3 种典型的数据不一致问题:

9.2.1 丢失修改 (Lost Update)#

当两个事务 T1T_1T2T_2 同时读入同一数据并进行修改时,T2T_2 的提交结果覆盖了 T1T_1 的提交结果,导致 T1T_1 的修改丢失了。

  • 示例流程
    1. 事务 T1T_1 读出机票余额 A=100A = 100
    2. 事务 T2T_2 也读出机票余额 A=100A = 100
    3. 事务 T1T_1 售出一张票,修改 A=99A = 99 并写入磁盘。
    4. 事务 T2T_2 售出一张票,修改 A=99A = 99 并写入磁盘。此时 T1T_1 的修改被覆盖,机票本应剩 98 张,实际却显示 99 张。

9.2.2 读脏数据 (Dirty Read)#

当事务 T1T_1 修改了某个数据并将其写回磁盘,但在事务结束前由于某种原因被撤销 (Rollback) 回滚。在此期间,另一个并发事务 T2T_2 读取了被修改后的值。此时 T2T_2 读取到的数据就是 脏数据 (即与磁盘实际状态不符的不正确数据)。

  • 示例流程
    1. 事务 T1T_1AA 的值修改为 200 并写回。
    2. 事务 T2T_2 读取了 A=200A = 200
    3. 事务 T1T_1 由于出错执行回滚,将 AA 的值恢复为原来的 100。
    4. 此时事务 T2T_2 持有的 A=200A = 200 就是脏数据。

9.2.3 不可重复读 (Unrepeatable Read)#

不可重复读是指事务 T1T_1 读取数据后,并发事务 T2T_2 对该数据进行了修改、删除或插入,当事务 T1T_1 再次尝试读取该数据或该范围的记录时,得到了与前一次不同的结果。

  • 具体表现形式
    1. 值修改T1T_1 读取 A=100A = 100T2T_2 将其修改为 200,T1T_1 再次读取得到 200,与第一次不同。
    2. 记录删除T1T_1 按条件读取了某些记录,随后 T2T_2 删除了其中部分记录,T1T_1 再次读取发现记录减少。
    3. 幻读 (Phantom Read)T1T_1 按条件读取了某些记录,随后 T2T_2 插入了符合条件的新记录,T1T_1 再次读取发现多了记录 (如产生“幻影”般的新数据)。

9.3 封锁机制 (Locking)#

封锁是实现并发控制最常用的技术。所谓封锁,就是事务在对某个数据对象 (如数据项、记录、关系表) 操作之前,向系统发出请求,对其加锁。在释放锁之前,其他事务不能对该数据对象进行不兼容的操作。

常用的基本封锁类型有两种:共享锁 (S 锁)排他锁 (X 锁)

9.3.1 共享锁 (S 锁 - Shared Lock)#

又称 读锁。如果事务 TT 对数据对象 AA 加上了 S 锁,则事务 TT 可以读取 AA 但不能修改 AA

  • 兼容性:其他事务可以对 AA 再次加 S 锁以并发读取,但不能加 X 锁,直到事务 TT 释放了 AA 上的 S 锁。这保证了多个事务可以并发读取同一数据,但读期间不允许任何写操作。

9.3.2 排他锁 (X 锁 - Exclusive Lock)#

又称 写锁。如果事务 TT 对数据对象 AA 加上了 X 锁,则事务 TT 既可以读取 AA 也可以修改 AA

  • 兼容性:其他任何事务都不能再对 AA 加任何类型的锁 (无论是 S 锁还是 X 锁),直到事务 TT 释放了 AA 上的 X 锁。这保证了在写操作期间,其他事务既不能读也不能写该数据。

9.3.3 锁的相容矩阵#

锁请求 \ 已持锁无锁S 锁 (读锁)X 锁 (写锁)
S 锁 (读锁)允许允许拒绝
X 锁 (写锁)允许拒绝拒绝

9.4 三种封锁协议 (Locking Protocols)#

在运用 X 锁与 S 锁对数据对象加锁时,需要约定一些规则 (如:何时申请、持锁多久、何时释放)。这些规则被称为 封锁协议 (Locking Protocols)。不同级别的封锁协议可以提供不同强度的并发控制保护。

9.4.1 一级封锁协议#

  • 规则:事务 TT 在修改数据 RR 之前必须先对其加 X 锁,直到事务结束 (COMMITROLLBACK) 才释放。如果是读取数据,则不需要加锁。
  • 保护效果:可以防止 丢失修改 (因为其他修改事务无法在同一时间对该数据加写锁)。
  • 缺陷:由于读操作不加锁,因此无法防范 读脏数据不可重复读

9.4.2 二级封锁协议#

  • 规则:在一级封锁协议的基础上,增加对读操作的加锁限制:事务 TT 在读取数据 RR 之前必须先对其加 S 锁读完后即可释放 S 锁
  • 保护效果:可以防止 丢失修改读脏数据 (因为读数据前要加 S 锁,而如果其他事务正在修改该数据并持有 X 锁,读事务必须等待写事务结束释放锁后才能加 S 锁,从而避免了读到未提交的脏数据)。
  • 缺陷:由于读完数据后立即释放了 S 锁,如果在同一个事务中后面再次读取,数据可能已被其他事务修改,因此无法防范 不可重复读

9.4.3 三级封锁协议#

  • 规则:在一级封锁协议的基础上,增加更严格的读锁持有期限制:事务 TT 在读取数据 RR 之前必须先对其加 S 锁,并且 必须持续到事务结束 才能释放。
  • 保护效果:可以防止 丢失修改读脏数据 以及 不可重复读 (因为 S 锁持续到事务结束,在这期间其他并发事务无法申请 X 锁来修改该数据,从而保证了可重复读)。

9.4.4 封锁协议对比与总结#

封锁协议级别写锁 (X 锁) 释放时机读锁 (S 锁) 释放时机防丢失修改防读脏数据防不可重复读
一级封锁协议事务结束时不加锁
二级封锁协议事务结束时读完即释放
三级封锁协议事务结束时事务结束时
两段锁协议 (2PL) 与三级协议的区别

需要特别区分 三级封锁协议两段锁协议 (Two-Phase Locking Protocol, 2PL)

  • 三级协议:侧重于解决特定的并发不一致问题(如脏读、不可重复读),它定义的是不同锁的持有生命周期。
  • 2PL 协议:规定所有事务的加锁操作必须在解锁操作之前进行(加锁阶段和解锁阶段)。它是保证并发事务 可串行化 (Serializability) 执行(即并发执行效果等同于某种串行执行效果)的充分条件。遵循 2PL 的事务并不一定是三级协议,三级协议也不一定完全遵循 2PL。

参考视频:数据库系统概论 陈红 老师主讲 第11章 并发控制#

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

第 9 章 数据库并发控制
https://blog.sopak.space/posts/study/economics-management/dp-a/9/
作者
Xxxhite
发布于
2026-07-02
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录