• 数据库事务的超级详细讲解,包括事务特性、事务隔离级别、MVCC(多版本并发控制)


    数据库事务:

    主要有事务特性,事务的隔离级别,MVCC。

    事务特性:

    事务(Transaction)是指作为单个逻辑工作单元执行的一系列操作,这些操作要么全部成功执行,要么全部不执行,不能出现部分执行的情况。事务具有以下四个特性,通常缩写为ACID:

    1、原子性(Atomicity):事务是一个不可分割的工作单位,要么全部执行,要么全部不执行。如果事务中的任何一步操作失败,整个事务都会被回滚(Rollback),回到操作之前的状态,不会造成数据的损坏或不一致。

    2、一致性(Consistency):事务在执行前后,数据库从一个一致性状态转移到另一个一致性状态。即使在事务执行过程中出现异常,数据库也会通过回滚操作,将数据恢复到一致性状态。

    3、隔离性(Isolation):事务的执行不会受到其他事务的影响,每个事务在执行过程中都应该感觉不到其他事务的存在。即使多个事务同时操作同一数据,也不会互相干扰。隔离性能够防止并发事务导致的数据异常和不一致问题。

    4、持久性(Durability):一旦事务被提交(Commit),其所做的修改将会永久保存在数据库中,并且即使系统发生故障,这些修改也不会丢失。数据库系统会将事务的修改持久化到磁盘中,以保证数据的持久性。

    案例说明事务的特性:

    假设有一个银行系统,用户 A 和用户 B 分别有 1000 元和 2000 元存款。用户 A 想要向用户 B 转账 500 元。这个转账操作可以通过以下 SQL 语句来实现:

    sqlCopy Code-- 开启事务
    START TRANSACTION;
    
    -- 扣除用户 A 的账户余额
    UPDATE accounts SET balance = balance - 500 WHERE user_id = 'A';
    
    -- 增加用户 B 的账户余额
    UPDATE accounts SET balance = balance + 500 WHERE user_id = 'B';
    
    -- 提交事务
    COMMIT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    以上 SQL 语句实现了转账操作的原子性。如果在转账过程中发生了异常,比如数据库连接断开或者用户 A 的余额不足,那么整个事务都会被回滚,用户 A 和用户 B 的账户余额都会恢复到转账前的状态,确保了事务的一致性和持久性。

    此外,隔离性的特点可以保证即使有其他用户同时查询用户 A 和用户 B 的账户余额,也不会受到转账操作的影响,保证了数据的隔离性。

    隔离级别:

    数据库事务的隔离级别定义了事务在并发执行时,系统如何控制事务之间的交互,即一个事务所做的更改在何时对其它事务可见。不同的隔离级别可以在并发性和一致性之间提供不同程度的平衡。SQL 标准定义了四种隔离级别,每种级别都可以减少一种或多种并发问题,但隔离级别越高,性能可能会越低,因为事务之间的并发能力减弱。

    1. 读未提交(Read Uncommitted)
    • 定义:最低的隔离级别,允许事务读取尚未提交的数据变更,可能会导致脏读、不可重复读和幻读。
    • 问题:脏读。
    • 案例:事务A修改了一条数据但尚未提交,事务B在此时读取了这条未提交的数据。如果A回滚,B读到的数据就是错误的。
    2. 读已提交(Read Committed)
    • 定义:允许事务读取并且仅读取已经提交的数据变更,可以避免脏读,但不可重复读和幻读仍可能发生。
    • 问题:不可重复读。
    • 案例:事务A读取了一条数据,事务B更新了这条数据并提交,当事务A再次读取同一数据时,数据已经变更。
    3. 可重复读(Repeatable Read)
    • 定义:确保如果一个事务读取了一条记录,则在这个事务剩余的时间里,无论其他事务如何更新,都不会影响到这条记录的读取结果。可以避免脏读和不可重复读,但幻读仍可能发生。
    • 问题:幻读。
    • 案例:事务A读取了几行数据,事务B插入了一行新数据并提交,当事务A再次查询同一范围的数据时,会看到之前未出现的新行。
    4. 串行化(Serializable)
    • 定义:最高的隔离级别,通过强制事务串行执行,避免脏读、不可重复读和幻读,但并发性能最差。
    • 问题:性能问题。
    • 案例:事务A和事务B需要操作同一数据集,B必须等待A完成并提交后,才能开始执行,从而保证了数据的一致性,但大大降低了并发性能。

    并发问题解释:

    • 脏读(Dirty Reads):事务读取了另一个事务未提交的数据。
    • 不可重复读(Non-Repeatable Reads):事务在同一查询中多次读取同一数据集,结果却不同,因为其他事务在两次读取之间更新了数据。
    • 幻读(Phantom Reads):事务在重新执行针对某个范围的查询时,发现其他事务插入了满足查询条件的新数据。

    在选择隔离级别时,需要在数据一致性和系统性能之间做出权衡。高隔离级别能更好地保证数据的一致性,但可能会降低系统的并发性能;低隔离级别虽然并发性能较好,但在数据一致性上可能存在缺陷。

    MVCC:

    MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种数据库事务并发控制的方法,主要用于解决读-写冲突,实现高并发访问数据库而不会出现数据不一致的情况。MVCC通过在数据库中保存数据的不同版本来实现并发控制,每个事务在执行时看到的数据版本都是一致的,但不同事务之间可以并发执行而不会相互影响。

    MVCC实现原理:
    1. 数据版本控制:

    MVCC的核心思想是为每个数据项维护多个版本,并且每个事务只能看到适合自己版本的数据。数据库中的每个数据项都会有一个版本号或者时间戳与之关联。

    • 版本号:可以是整数或者时间戳,用于标识数据版本的顺序。
    • 时间戳:记录数据版本的生成时间,通常精确到毫秒或更高精度。
    2. 版本链:

    数据库中的数据通常以行(Row)为单位存储,每一行数据可能会有多个版本,这些版本按照时间顺序形成一个版本链。版本链可以用一个双向链表或者类似的数据结构来实现。

    • 每个数据行的版本链:包含了该数据行的所有版本,按照时间顺序排列。
    • 链表节点:存储了数据的具体内容以及指向下一个版本的指针。
    3. 读操作实现:

    当一个事务要执行读操作时,它会根据自己的事务ID或者时间戳确定要读取的数据版本,然后遍历该数据行的版本链,找到最接近但不超过事务版本的数据版本。

    • 快照读(Snapshot Read):事务读取数据时不会阻塞其他事务对同一数据的写操作,而是读取数据版本链中对应版本的数据。
    • 读取过程:事务通过版本链中的版本号或者时间戳来定位数据,确保读取到的数据是在该事务开始之前已经提交的。
    4. 写操作实现:

    当一个事务要执行写操作时,它会为数据生成一个新版本,并将新版本插入到数据行的版本链中。通常会使用写时复制(Copy-On-Write)或者类似的机制来实现写操作。

    • 生成新版本:事务会生成一个新的版本号或者时间戳,并将新版本写入到数据库中。
    • 写时复制:旧版本的数据不会立即被覆盖,而是在新版本生成后才被修改,这样可以保留旧版本的数据用于事务的隔离和回滚。
    5. 提交操作实现:

    当一个事务提交时,它会释放对数据库的锁,并清理与该事务相关的数据版本。这通常包括将该事务生成的新版本标记为已提交,并删除回滚段中与该事务相关的数据。

    • 释放锁:事务提交后,其他事务可以访问该数据,并且可以读取该事务生成的新版本。
    • 清理数据:删除回滚段中与该事务相关的数据,确保数据库的存储空间得到有效利用。
    6. 事务隔离级别

    MVCC可以实现不同的事务隔离级别,包括读未提交、读已提交、可重复读和串行化。不同的隔离级别决定了事务对数据的读取和写入行为,从而影响了并发控制的实现方式。

    • 隔离级别:用于控制事务之间的相互影响程度,包括读取到的数据版本以及事务的可见性等方面。
    总结:

    MVCC通过多版本控制和版本链来实现数据库的并发控制,保证了事务之间的隔离性,从而确保了数据的一致性和完整性。MVCC是许多现代数据库系统的核心技术之一,如MySQL、PostgreSQL等都采用了MVCC来实现事务的并发控制。

    MVCC实现流程:

    当一个事务开始时,数据库系统会为其分配一个唯一的事务ID或者时间戳,并根据该事务ID或时间戳来执行读取和写入操作。

    1. 读操作流程:
    1. 事务开始:事务T开始时,获取一个唯一的事务ID或时间戳。
    2. 确定读取版本:根据事务T的隔离级别,确定要读取的数据版本:
      • 如果隔离级别是读未提交,则可以读取到其他未提交事务的数据。
      • 如果隔离级别是读已提交或更高级别,则只能读取已提交事务生成的数据版本。
    3. 读取数据:根据确定的数据版本,从数据库中读取数据。通常是通过版本链中的版本号或时间戳来定位数据。
    4. 提交或回滚:如果读取数据时发现该数据已被删除或不可见,则根据事务的隔离级别决定是提交还是回滚该事务。
    2. 写操作流程:
    1. 事务开始:事务T开始时,获取一个唯一的事务ID或时间戳。
    2. 生成新版本:为要写入的数据生成一个新版本,并将新版本插入到数据行的版本链中。通常采用写时复制或类似的机制来实现。
    3. 写入数据:将新版本的数据写入到数据库中。此时,其他事务仍然可以读取旧版本的数据,保持了数据的一致性。
    4. 提交或回滚:事务T完成所有写操作后,根据事务的提交或回滚指令来决定是否将写入的数据版本提交或回滚。
      • 如果提交事务,则将新版本标记为已提交,其他事务可以读取该版本。
      • 如果回滚事务,则删除新版本,并保留旧版本的数据。
    3. 并发控制流程:
    1. 读-写冲突检测:当一个事务T1要读取数据时,如果发现有其他事务T2正在对该数据进行写操作,则需要进行冲突检测。
      • 如果T2的写操作已提交,则T1可以读取T2提交的新版本。
      • 如果T2的写操作未提交,则根据事务隔离级别决定是否允许T1读取T2的未提交数据。
    2. 写-写冲突检测:当一个事务T1要对数据进行写操作时,如果发现有其他事务T2正在对同一数据进行写操作,则需要进行冲突检测。
      • 如果T2的写操作已提交,则T1需要等待T2完成后再进行写操作。
      • 如果T2的写操作未提交,则根据事务隔离级别决定是否允许T1覆盖T2的未提交数据。
    3. 事务隔离级别的控制:根据事务的隔离级别,决定事务对数据的读取和写入行为,从而控制事务之间的相互影响程度。
    4. 并发度控制:根据系统的并发度限制,控制同时执行的事务数量,避免系统过载和性能下降。
    4. 事务提交流程:
    1. 提交指令:当一个事务T完成所有操作后,执行提交指令。
    2. 标记提交:将事务T生成的新版本标记为已提交状态,其他事务可以读取该版本。
    3. 释放锁:释放事务T持有的锁资源,其他事务可以对相应数据进行读写操作。
    4. 清理数据:删除回滚段中与事务T相关的数据版本,释放存储空间。
    5. 提交完成:事务T提交完成,其他事务可以读取其写入的数据版本。

    MVCC通过以上流程实现了数据库的并发控制,保证了事务之间的隔离性和数据的一致性。

    MVCC案例说明:

    假设有一个简单的数据库表,记录了用户的姓名和年龄,初始数据如下:

    IDNameAge
    1Alice25
    2Bob30

    现在有两个事务,T1 和 T2,分别对数据库进行操作:

    • 事务 T1
      1. T1 开始,读取 ID 为 1 的用户信息,姓名为 Alice,年龄为 25。
      2. T1 更新 ID 为 1 的用户信息,将年龄增加 1,变为 26。
      3. T1 提交事务。
    • 事务 T2
      1. T2 开始,读取 ID 为 1 的用户信息,姓名为 Alice,年龄为 25。此时由于T1未提交,T2读取到的是旧版本的数据。
      2. T2 提交事务。

    通过MVCC,事务T2读取到了ID为1的用户信息时,虽然T1已经对该数据进行了更新,但T2读取到的是旧版本的数据,这样就避免了T2读取到脏数据的情况。

    总的来说,MVCC通过版本控制实现了数据库的高并发访问,保证了事务之间的隔离性,从而确保了数据的一致性。

  • 相关阅读:
    Rust 从入门到精通08-字符串
    【ARM AMBA AXI 入门 16 - AXI 写响应通道 BVALID | BREADY | BRESP 详细介绍】
    电容笔好还是触屏笔好?便宜又好用的电容笔推荐
    (三)什么是Vite——Vite 主体流程(运行npm run dev后发生了什么?)
    K8S部署相关
    Vue3.3指北(一)
    多线程与线程池
    数据库课件= =
    力扣-234-回文链表
    第二章:25+ Python 数据操作教程(第二十二节如何从 R 调用或运行 python)持续更新
  • 原文地址:https://blog.csdn.net/m0_56615376/article/details/137224167