六、事务

90 阅读6分钟

一、认识事务

  事务 是数据库区别于文件系统的重要特性之一。数据库系统引入事务的主要目的是:事务会把数据库从一种一致状态转换为另一种一致状态。在数据库提交工作时,可以确保要么所有修改都已经保存了,要么所有修改都不保存。

  InnoDB 存储引擎中的事务完全符合ACID的特性。ACID:

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

A(Atomicity)原子性

  原子性指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功,才算这个事务成功。事务中任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。 即要么都做,要么都不做。

  例如用户在ATM机取款,所有操作要么都成功,要么都失败,取款流程为:
  1) 登录 ATM 机平台,验证密码。
  2) 从远程银行的数据库中,取得账户信息
  3) 用户在ATM机上输入欲提取的金额
  4) 从远程银行的数据库中,更新账户信息。
  5) ATM 机出款。
  6) 用户取钱。

C(consistency)一致性

  一致性指事务将从数据库从一种状态转变为下一种一致的状态。在事务开始前和事务结束后,数据库的完整性约束没有被破坏。
  例如,在表中有一个字段为姓名,为唯一约束。如果一个事务对姓名字段进行了修改,但是在事务提交或事务操作发生回滚后,表中的姓名变得非唯一了,这就破坏了事务的一致性要求,即事务将数据库从一种状态变为了一种不一致的状态。因此,事务是一致性的单位,如果事务中某个动作失败了,系统可以自动撤销事务——返回初始化状态。

I(isolation)隔离性

  隔离性也叫并发控制、可串行化、锁等。事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离,即该事务提交前对其他事务都不可见,通常这使用锁来实现。

D(durability)持久性

  事务一旦提交,其结果就是永久性的。即使发生宕机等故障,数据库也能将数据恢复

二、事务的实现

  原子性、一致性、持久性通过事务的redo log和 undo log 来完成。redo log 称为重做日志,用来保证事务的原子性和持久性。undo log 用来保证事务的一致性。
  redo 恢复提交事务修改的页操作,而 undo 回滚行记录到某个特定版本。redo 通常是物理日志,记录的是页的物理修改操作。 undo 是逻辑日志,根据每行记录进行记录。

2.1 redo

  重做日志用来实现事务的持久性,即事务ACID中的D。由两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file),是持久的。
  当事务提交(COMMIT)时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的COMMIT操作完成才算完成。
  redo log 用来保证事务的持久性,undo log 用来帮助事务回滚及 MVCC 功能。redo log 基本上都是顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而undo log 是需要进行随机读写的。
  为了确保每次日志都写入重做日志文件,在每次将日志缓冲写入重做日志文件后,InnoDB 存储引擎都需要调用一次fsync操作。由于 fsync 的效率取决于磁盘的性能,因此磁盘的性能决定了事务提交的性能,也就是数据库的性能。

redo log 与 binlog 的区别?

  1. redo log 是在 InnoDB 存储引擎层产生,而二进制日志实在MySQL 数据库的上层产生的,并且二进制不仅仅针对于 InnoDB 存储引擎, MySQL 数据库中的任何存储引擎对于数据库的更改都会产生二进制日志。
  2. 两种日志记录的内容形式不同。MySQL 数据库上层的二进制日志使一种逻辑日志,记录的是对应的SQL语句。而InnoDB 存储引擎层面的重做日志是物理格式日志,记录的是对于每个页的修改。
  3. 写入磁盘时间点不同,二进制日志只在事务提交完成后进行一次写入。而 InnoDB 存储引擎的重做日志在事务进行中不断地被写入,这表现为日志并不是随事务提交的顺序进行写入的。

  二进制日志仅在事务提交时记录,并且对于每一个事务,仅包含对应事务的一个日志。对于重做日志,由于其记录的是物理操作日志,因此每个事务对应多个日志条目,并且事务的重做日志写入是并发的,并非在事务提交时写入。

2.2 undo

  undo 用于事务的回滚操作。因此在对数据库进行修改时,InnoDB 存储引擎不但会产生 redo,还会产生一定量的 undo。这样如果用户执行的事务或语句由于某种原因失败了,又或者用户用一条ROLLBACK语句请求回滚,就可以利用这些 undo信息将数据回滚到修改前的样子。
  undo 存放在数据库内部的一个特殊段中,称为undo 段(undo segment)。undo段位于共享表空间内。

  undo 是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。
  例如,用户执行了一个INSERT 10W条记录的事务,这个事务会导致分配一个新的段,即表空间会增大。在用户执行ROLLBACK时,会将插入的事务进行回滚,但是表空间的大小不会因此而收缩。因此,当InnoDB 回滚时,实际上做的是与先前相反的工作。对于每个INSERT,它会完成一个DELETE;对于每个DELETE,它会执行一个INSERT;对于每个UPDATE,它会执行一个相反的UPDATE,将修改前的行放回去。
  undo 的另一个作用是 MVCC,当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。
  undo log 会产生 redo log,这是因为 undo log 也需要持久性的保护。