lectrue15 并发控制理论

引子:在多用户同时访问数据库的环境下,我们需要解决以下两个核心问题:

  • 更新丢失问题(并发控制 - Concurrency Control): 当多个用户同时更新同一条记录时,我们如何避免出现"竞态条件"(Race Conditions)?即如何确保一个用户的修改不会被另一个用户无意中覆盖。

  • 持久性问题(故障恢复 - Recovery): 在发生停电或系统崩溃的情况下,我们如何确保数据库能够恢复到正确的状态?即如何保证已经提交的数据不会丢失。


事务:事务是指在共享数据库上执行的一个或多个操作序列(例如SQL查询),用以完成某种高层功能。它们是数据库管理系统(DBMS)中数据变更的基本单位。事务必须具有原子性,要么全无,要么全有。

草案系统:处理事务的一种简单方法是使用单个工作线程(例如单线程)一次仅执行一个事务。因此,同一时间只能有一个事务在运行。

为了执行事务,DBMS会复制整个数据库文件,并在新文件上进行事务修改。

  • 如果事务成功,新文件将成为当前的数据库文件。

  • 如果事务失败,DBMS 将丢弃新文件,事务的所有更改都不会被保存。

这种方法非常慢,因为它不允许并发事务,且每次事务都需要复制整个数据库文件。

并发执行的需求:一个(潜在的)更好的方法是允许独立事务的并发执行,同时保持正确性和公平性(即所有事务被平等对待,不会因为永远无法执行而陷入"饥饿"状态)。但在 DBMS 中执行并发事务极具挑战。既要保证正确性,又要保证执行效率。

任意交错的操作序列可能导致:

  • 临时不一致性:这是不可避免的,但不是问题。

  • 永久不一致性:这是不可接受的,会导致数据的正确性和完整性出现问题。

事务的界限:事务的作用范围仅限于数据库内部。它不能对外部世界做出更改,因为无法回滚这些外部操作。


定义:从形式上来说,数据库可以表示为一组固定的命名数据对象。这些对象可以是属性,元组,页,表甚至是整个数据库。我们讨论的算法适用于任何类型的对象,但前提是所有对象必须属于同一类型。

事务是对这些对象进行的一系列读 (Read) 和 写 (Write) 操作(例如 )。为了简化讨论,此定义假设数据库大小是固定的,因此操作仅限于读取和更新,不涉及插入或删除。

事务的边界由客户端定义。在 SQL 中,事务以 BEGIN 命令开始。事务的结果要么是 提交 (COMMIT),要么是 中止 (ABORT):

  • COMMIT:事务的所有修改要么全部保存到数据库中,要么被 DBMS 否决并强制中止。

  • ABORT:事务的所有更改都会被撤销,就像该事务从未发生过一样。中止可以是事务自发的,也可以是由 DBMS 触发的。

确保数据库正确性的标准由缩写词ACID给出:

  • 原子性 (Atomicity):确保事务中的所有操作要么全部发生,要么一个都不发生。

  • 一致性 (Consistency):如果每个事务本身是一致的,且数据库在事务开始时处于一致状态,那么在事务完成时,数据库保证仍处于一致状态。如果数据满足所有验证规则(如约束、级联和触发器),则称其为一致。

  • 隔离性 (Isolation):意味着当一个事务执行时,它应该产生一种"自己正与其他事务隔离"的错觉。隔离性确保并发执行事务的结果与按某种顺序逐个执行事务得到的数据库状态相同。

  • 持久性 (Durability):如果一个事务提交了,那么它对数据库产生的影响应该是永久性的。


ACID:原子性

DBMS 保证事务是原子性的。事务中的所有操作要么全部执行,要么一个也不执行。实现这一点有两种主要方法:

方法1:日志记录DBMS 记录所有的操作(Actions),以便在事务中止(Abort)时能够撤销(Undo)这些操作。它在内存和磁盘中同时维护 Undo 记录。出于审计和效率的考虑,几乎所有现代系统都使用日志记录。

方法2:影子分页DBMS 会对事务修改的页制作副本,事务在这些副本上进行更改。只有当事务提交时,这些页才会变得对外界可见。

  • 特点:这种方法在运行时的速度通常比基于日志的系统慢。

  • 优点:如果你是单线程运行,则无需记录日志,事务修改数据库时写入磁盘的次数更少。

  • 恢复简单:恢复过程非常简单,只需删除未提交事务的所有页即可。

  • 现状:通常情况下,人们更追求运行时性能而非恢复性能,因此影子分页在实践中很少使用。


ACID:一致性

从宏观上看,一致性意味着数据库所代表的世界在逻辑上正确,应用程序对数据的所有查询都将返回逻辑正确的结果,一致性有两个维度的概念。

数据库一致性:数据库准确地反映了它所建模地真实世界实体,并遵循完整性约束,例如人的年龄不能为负数,此外,未来的事务应该能看到数据库中过去已提交事务所产生的影响。

事务一致性:如果数据库在事务开始前是一致的,那么在事务完成后也必须是一致的。确保事务一致性是应用程序(即开发者)的责任。


ACID:隔离性

DBMS为事务提供了一种它们正独自在系统中运行的错觉。事务不会看到并发事务产生的影响,这等同于一个个事务按串行顺序执行系统。但为了获得更好的性能,DBMS 必须在保持隔离性错觉的同时,交错执行多个并发事务的操作。

并发控制协议:并发控制协议是 DBMS 在运行时决定来自多个事务的操作如何正确交错的机制。

并发控制协议分为两类:

  1. 悲观并发控制:DBMS 假设事务会发生冲突,因此从一开始就阻止问题的发生。

  2. 乐观并发控制:DBMS 假设事务间的冲突很少见,因此选择在事务提交后处理发生的冲突。

DBMS执行操作的顺序被称为执行调度,我们的目标是交错执行事务以最大化并发量,同时确保输出是正确的。并发控制协议的目标是生成一个等同于串行执行的执行调度。

  • 串行调度:不交错执行不同事务动作的调度。

  • 等价调度:对于任何数据库状态,如果执行第一个调度的效果与执行第二个调度的效果完全相同,则这两个调度是等价的。

  • 可串行化调度:如果一个调度等价于事务的任何某种串行执行,则该调度是可串行化的。不同的串行执行可能产生不同的结果,但都被认为是"正确"的。

冲突:如果两个操作满足以下条件,则称它们发生了冲突:它们属于不同事务、作用于同一对象,且其中至少一个是写操作。冲突有三种变体:

  • 读-写冲突 (Read-Write Conflicts / "不可重复读"):一个事务多次读取同一对象时无法获得相同的值。

  • 写-读冲突 (Write-Read Conflicts / "脏读"):一个事务在另一个事务提交更改之前,就看到了该事务的写入结果。

  • 写-写冲突 (Write-Write Conflicts / "更新丢失"):一个事务覆盖了另一个并发事务尚未提交的数据。

冲突可串行化:在一个交错执行的调度中,把那些不冲突的操作互相交换位置会得到一个新的调度。对于数据库而言,这个新调度和老调度冲突等价。也就是说,无论怎么交换这些不冲突的操作,最终对数据产生的结果是一样的。

如果你不断地交换这些不冲突地操作,最终变为一个纯粹地串行调度,则认定那个一开始的调度是冲突可串行化的。它代表这个乱序的调度在逻辑上是绝对正确,绝对安全的。

可以通过交换非冲突操作直到形成串行调度来验证。但对于多事务调度,这太昂贵了。更好的方法是使用依赖图。

依赖图规则如下:

  • 画节点:每一个参与的事务画一个圆圈(节点 T1, T2...)。

  • 找冲突:在时间线上从上往下扫,寻找那三种冲突(R-W, W-R, W-W)。

  • 连箭头:如果发现冲突,且 T1 的操作在时间上先于 T2 发生,就画一条从 T1 指向 T2 的单向箭头 T1 -> T2

画完所有冲突箭头后,只要这张图里没有环(即无法从一个节点顺着箭头走回自己),这个调度就是冲突可串行化的。

各调度集合的关系如下: 串行调度 ⊂ 冲突可串行化调度 ⊂ 所有调度


ACID:持久性

所有已提交事务的更改在系统崩溃或重启后必须是持久的。DBMS可以使用日志记录或影子分页来确保所有更改都是持久的,这通常要求已提交的事务必须存储在非易失性存储器中。

相关推荐
小吴编程之路5 小时前
MySQL 索引核心特性深度解析:从底层原理到实操应用
数据库·mysql
~莫子5 小时前
MySQL集群技术
数据库·mysql
凤山老林6 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
就不掉头发6 小时前
Linux与数据库进阶
数据库
与衫6 小时前
Gudu SQL Omni 技术深度解析
数据库·sql
咖啡の猫6 小时前
Redis桌面客户端
数据库·redis·缓存
oradh6 小时前
Oracle 11g数据库软件和数据库静默安装
数据库·oracle
what丶k6 小时前
如何保证 Redis 与 MySQL 数据一致性?后端必备实践指南
数据库·redis·mysql
_半夏曲7 小时前
PostgreSQL 13、14、15 区别
数据库·postgresql
把你毕设抢过来7 小时前
基于Spring Boot的社区智慧养老监护管理平台(源码+文档)
数据库·spring boot·后端