【InnoDB】事务基础知识

本文归纳数据库事务的一些基础知识。

ACID

事务具有4个特征,分别是原子性、一致性、隔离性和持久性,简称事务的ACID特性。

  • 原子性(atomicity)

    一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作。

    innodb其实是通过WAL写redo log保证原子性的:

    • 如果buffer pool中脏页还未刷盘数据库就挂了,那么重启时可以通过redo log恢复;(保证成功提交)
    • buffer pool是有限的,如果一个事务内buffer pool用完了还需要读入新的页,那么就要刷回一些页,而刷回页的操作也需要记录到redo log,这些页可能是还未提交事务的,如果没有记录,那么回滚的时候就找不到对应的页。(保证回滚成功)
  • 一致性(consistency)*

    事务前后数据的完整性必须保持一致。事务的执行不能破坏数据库数据的完整性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。
    中间状态是不一致的,所以如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于一种不正确的状态,也就是不一致的状态。

  • 隔离性(isolation)

    事务的隔离性是指在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰。不同的事务并发操作相同的数据时,每个事务都有各自完成的数据空间,即一个事务内部的操作及使用的数据对其他并发事务时隔离的,并发执行的各个事务之间不能相互干扰。

  • 持久性(durability)

    一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中。即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束的状态。

总结: 只有满足原子性、一致性,事务执行才是正确的。没有并发时,隔离性一定能满足,有并发时需要考虑隔离性。持久性是为了应对系统崩溃情况。

innodb 是通过日志机制满足这些特性的,主要是redo log 和 undo log。

并发一致性问题

在并发环境下,若事务的隔离性不能保证,将会出现不一致的问题。基本可以分为三类问题:

  1. 丢失修改

    两个事务T1和T2同时读入同一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失。

    实际业务中不应该出现丢失修改,这种是出现了写-写并发,对数据写时应该加锁。

  2. 脏读

    事务T1修改了某个数据,还没提交时被事务T2读取了,然后T1却回滚了,导致T2读到的数据与数据库中的不一致。这就是脏读。

    一般来说都不会出现脏读,因为所有的数据库实现隔离级别都至少是提交读。

  3. 不可重复读

    在一次事务中多次读取的结果不一样。这违反了事务一致性。

    不可重复读又可以分为两种情况:
    1)事务T1多次读取同一数据,事务T2在事务T1还未结束时,对数据作了更新并提交,导致事务T1之后的读取与之前读取结果不一致。
    2)事务T1读取某个范围的数据,然后事务T2在这个范围增加了数据并提交,导致事务T1再次读该范围数据时与第一次读的不同,多了一些数据。

    注意:第二种不可重复读在Mysql的官方文档中被称为Phantom Problem,即幻读。在标准SQL规范中幻读单独自成一种问题,但在mysql中都可看成不可重复读。

    • 与脏读的区别:
      脏读读的是未提交的数据,而不可重复读读的是已提交的数据。
    • 大部分数据库厂商的做法:
      一般来说,不可重复读是允许出现的,因为读到的是已提交的数据,也不会有大问题。所以很多数据库将默认的隔离级别设置为 READ COMMITTED

产生并发不一致性问题的主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。

事务的隔离级别

如果所有用到的资源都需要自己去手动加锁解锁,那将会非常麻烦。在标准SQL规范中指明了四种隔离级别,用来协调并发问题,数据库产品在内部实现了不同的隔离级别,直接提供给用户使用。

  • 未提交读(Read Uncommited)

    事务中的修改,即使没有提交,对其它事务也是可见的。该隔离级别可能会出现所有的并发一致性问题。

  • 已提交读(Read Commited)RC

    事务只能读取已经提交的数据,即一个事务所做的修改在提交之前对其它事务是不可见的。这个级别只能解决脏读问题。是大部分关系型数据库的默认隔离级别。并发度高,但存在幻读问题。

  • 可重复读(Repeatable Read)RR

    在事务处理过程中,多次读取数据时,其结果都和事务开始时刻是一致的。也是InnoDB的默认隔离级别,innodb 采用间隙锁实现,容易引发死锁,且并发度低。

  • 串行化(SERIALIZABLE)

    是最严格的事务隔离级别,通过加锁强制所有事务被串行执行。

posted @ 2021-12-12 19:53  Glaci  阅读(72)  评论(0编辑  收藏  举报