InnoDB 存储引擎的事务学习

本文主要是针对《Mysql技术内幕:InnoDB 存储引擎》一书中第七章关于 InnoDB 存储引擎中事务的学习总结。mysql

事务是访问并更新数据库各类数据项的一个程序执行单元,是数据库区别于文件系统的重要特性之一。算法

事务的 ACID 特性

理论上,事务必须知足 ACID 特性,才算严格的定义,其中对于 InnoDB 存储引擎来讲,其默认事务隔离级别为 READ REPEATABLE,彻底遵循和知足事务的 ACID 特性。sql

  • A(Atomicity) 原子性
一个事务必须被视为一个不可分割的最小工做单元,要么所有提交成功,要不所有失败回滚
复制代码
  • C(Consistency) 一致性
一致性是指数据库从一个执行性状态转换为另一个一致性状态
事务的一致性是但愿全部操做是符合现实当中的指望的,好比事务的结束后,完整性约束有没有被破坏,是否发生丢失更新,脏读,不可重复读等问题
因此事务的一致性是一个综合的概念,要靠事务的原子性,隔离性和持久性来保证
复制代码
  • I(Isolation) 隔离性
隔离性是指事务所作的修改在最终提交前,对其它事务是不可见的,一般是靠锁机制来实现的
复制代码
  • D(Durability) 持久性
事务一旦提交,,其所作的修改是永久保存在数据库里的,即便系统奔溃数据页不会丢失
持久性保证事务系统的高可靠性,而高可用性事务自己没法保证,须要一些系统共同配合完成
复制代码

事务的分类

事务分为扁平事务,带有保存点的扁平事务,链事务,嵌套事务,分布式事务。对于 InnoDB 存储引擎来讲,其支持扁平事务,带保存点的事务,链事务,分布式事务。对于嵌套事务,其原生不支持数据库

  • 扁平事务
- 扁平事务由 BEGIN WORK 开始,ROLLBACK 或者 COMMIT 结束,其间是原子的,要么执行,要么回滚
复制代码
  • 带有保存点的扁平事务
- 除了支持扁平事务支持的操做外,容许在事务执行过程当中回滚同一事务中较早的一个状态
- 由于某些事务可能在执行过程当中出现的错误并不会致使全部的操做都无效,放弃整个事务不合乎要求,开销太大
- 保存点用来通知事务系统应该记住事务当前的状态,以便当以后发生错误时,事务能回到保存点当时的状态
复制代码
  • 链事务
- 链事务的思想是:在提交一个事务时,释放不须要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务
- 提交事务操做和开始下一个事务操做 将合并为一个原子操做,这意味着下一个事务将看到上一个事务的结果,就好像一个事务中进行的同样
- 链事务与带有保存点的扁平事务不一样的是,带有保存点的扁平事务能回滚到任意正确的保存点,而链事务中的回滚仅限当前事务,即只能恢复到最近的一个保存点
- 对于锁的处理,二者也不相同,锁事务在执行 COMMIT 后即释放了当前所持有的锁,而带有保存点的扁平事务不影响迄今为止所持有的锁
复制代码
  • 嵌套事务
- 嵌套事务是一个层次结构框架,由一个顶层事务控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务
- 嵌套事务是由若干事务组成的一棵树,子树既能够是嵌套事务也能够是扁平事务
- 处在叶节点的事务是扁平事务,可是每一个事务从根到叶节点的距离能够是不一样的
- 子事务既能够提交也能够回滚。可是它的提交操做并不立刻生效。除非其父事务已经提交。所以能够推论出,任何子事务都在顶层事务提交后才真正的提交
- 树中的任意事务回滚会引发它的全部子事务一同回滚,故子事务仅保留 ACI 特性而不具备 D 特性
- 实际的工做是交由叶子节点完成,即只有叶子节点的事务才能才能访问数据库、发送信息、获取其余类型的资源,而高层的事务仅负责逻辑控制
复制代码
  • 分布式事务
- 分布式事务一般是一个分布式环境下运行的扁平事务,所以须要根据数据所在位置访问网络中的不一样节点
- 分布式事务通常不能经过一台数据库就完成任务,其须要访问网络中两个节点或者多个的数据库,而在每一个节点的数据库执行的实务操做有都是扁平的
- 对于分布式事务,其一样须要知足ACID特性,要么都发生,要么都失败
复制代码

事务的实现

事务的隔离性是经过锁来实现的,而原子性,一致性,持久性是经过数据库中的 redo log 和 undo log 来实现的。缓存

redo log
  • 重作日志由两部分组成:内存中中的重作日志缓存和磁盘上的重作日志文件
  • InnoDB 存储引擎的重作日志是在事务进行中不断的写入,并非在事务提交时才写入,先写入重作日志缓存,而后根据必定规则刷新到重作日志文件
  • 重作日志文件记录了 InnoDB 存储引擎的事务日志,主要用来在发生宕机的状况下,对事务操做进行恢复操做,历来保证的原子性和持久性
  • 重作日志缓存:
- 重作日志缓存不放在缓存池中,是另一份单独的缓存
- 重作日志缓存不须要特别大,由于 Master Thread 每秒都会将数据刷新到磁盘
- 经过参数 innodb_log_buffer_size 来修改重作日志缓存的大小,默认为 8MB
- 重作日志缓存都会被刷新到磁盘的三种条件:1. Master Thread 会每秒进行刷新 2. 每一个事务提交时会刷新 3.重作日志缓存空间小于 1/2 时会强制刷新
复制代码
  • 重作日志文件:
- 重作日志文件至少有一个重作日志文件组,每一个重作日志文件组至少有 2 个重作日志文件
- 为了保证高可用性,能够设置镜像日志文件组,将日志组存储在不一样的磁盘上
- 重作日志的写入不须要 doublewrite,由于写入是按照一个扇区的大小进行写入的,是写入的最小单位,因此能够保证写入的可靠性
复制代码
undo log
  • undo log 主要用于用户在执行 rollback 语句时进行回滚操做
  • redo 存放在日志文件里,而 undo存放在数据库共享表空间的一个是特殊段中(undo segment)
  • undo 是逻辑日志,在回滚时全部修改逻辑的被取消了,可是数据结构和页自己在回滚后可能大不相同
  • undo 其实就是作与先前相反的操做,对于每一个 Insert 执行 delete,对每个 update 执行相反的 update
  • undo 另一个做用是实现 MVCC(多版本控制),实现非锁定读,经过 undo 日志找到以前的行版本信息
  • uodo log 也会产生 redo log,由于 undo log 也须要持久性的保护
  • undo log 的回收和删除
- 并非当事务提交时,undo log 就会被删除,颇有可能还有其余事务在使用该 undo log 来记录以前的版本
- 因此事务提交时 undo log 会被放在待删除列表里,以后经过 purge 线程来根据是否有其它事务在使用来决定删除
- 对于 insert 操做的记录,只对本事务可见,因此在事务提交后能够直接删除,不须要进行 purge 操做
- 经过全局参数 innodb_purge_batch_size 来设置每次 purge 操做能够清理的 undo 页的数量
复制代码

事务控制语句

  • Mysql 命令行的默认设置下,事务是自动提交的,能够经过执行命令 SET AUTOCOMMIT = 0 来禁止自动提交
  • 关于事务的控制语句总结以下:
- START TRANSACTION | BEGIN:显式地开启一个事务
- COMMIT:会提交事务,并使得已对数据库作的全部修改为为永久性的
- COMMIT WORK:COMMIT WORK 和 COMMIT 语句基本是一致的,不一样之处在于 COMMIT WORK 用来控制事务结束后的行为是 CHAN 仍是 RELEASE 的
            用户能够经过参数 completion_type 来进行控制 COMMIT WORK 的行为,该参数默认为 0,在这种设置下 COMMIT和 COMMIT WORK是彻底等价的。
            当参数 completion_type的 值为 1 时, COMMIT WORK 等同于 COMMIT AND CHAIN,表示立刻自动开启个相同隔离级别的事务
- ROLLBACK:回滚并结束用户的事务,并撤销正在进行的全部未提交的修改
- ROLLBACK WORK:ROLLBACK WORK 和 ROLLBACK 的关系与 COMMIT WORK 和 COMMIT 关系相似
- SAVEPOINT identifier: SAVEPOINT 容许在事务中建立一个保存点,一个事务中能够有多个 SAVEPOINT
- RELEASE SAVEPOINT identifier:删除一个事务的保存点,当没有一个保存点执行这句语句时,会抛出一个异常
- ROLLBACK TO [SAVEPOINT] identifier:这个语句与 SAVEPOINT 命令一块儿使用,能够把事务回滚到标记点,而不回滚在此标记点以前的任何工做
- SET TRANSACTION:这个语句用来设置事务的隔离级别。 InnoDB存储引擎提供的事务隔离级别有: READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、 SERIALIZABLE。

复制代码
  • ROLLBACK TO SAVEPOINT 来回滚到某个保存点,可是若是回滚到一个不存在的保存点,会抛出异常
  • ROLLBACK TO SAVEPOINT 虽然有 ROLLBACK,但其并非真正地结束一个事务,所以即便执行了 ROLLBACK TO SAVEPOINT ,以后也须要显式地运行 COMMIT 或 ROLLBACK 命令
  • 经过变量 com_commit 和 com_rollback 能够查看每秒钟事务的提交量和回滚量,这个统计是全部显式提交的事务

事务的隔离级别

在文章 InnoDB 存储引擎的锁学习 中就屡次提到事务的隔离级别,不一样粒度的锁在不一样隔离级别下的表现。隔离级别越低,数据库所须要的锁越少,性能越好,可是安全性越低。接下来是对事务的四种隔离级别(由低到高)的总结:安全

  • Read Uncommitted(读取未提交内容)
- 在该隔离级别,全部事务均可以看到其余未提交事务的执行结果
- 本隔离级别不多用于实际应用,由于它的性能也不比其余级别好多少
- 读取未提交的数据,也被称之为脏读问题
- 该隔离级别违背了事务的隔离性原则
复制代码
  • Read Committed(读取提交内容)
- 一个事务只能看见已经提交事务所作的改变,知足隔离性的定义
- 该隔离级别下会出现同一个事务两次读同一份数据结果不一致现象,也就是不可重复读问题
复制代码
  • Repeatable Read(可重读)
- 这 是MySQL 的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到一样的数据行
- Repeatable Read 这会致使幻读 (Phantom Read)问题。幻读指当用户读取某一范围的数据行时,另外一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。
- InnoDB 利用 Gap Lock 算法在  Repeatable Read 隔离级别下解决了 幻读现象
复制代码
  • Serializable(可串行化)
- 这是最高的隔离级别,它经过强制事务排序,使之不可能相互冲突,从而解决幻读问题。
- 它是在每一个读的数据行上加上共享锁,在这个级别,可能致使大量的超时现象和锁竞争。
复制代码

很差的事务习惯

  • 在循环中提交事务
- 当插入发生错误时,数据库会停留在一个未知的位置,没法回滚
- 每一次提交都要写一次重作日志,性能会变的不好
CREATE PROCEDURE load1(count int unsigned)
begin

  declare s int unsigned default 1;

  declare c char(80) default repeat('a',80);

  while s<=count do

    insert into t1 select NULL,c;

    commit;

    set s=s+1;

  end while;

end;
复制代码
  • 使用自动提交事务
- 自动提交并非好习惯,会让一些开发人员可能产生错误的理解
- 另外,在不一样的语言API时,自动提交是不一样的,用不一样的语言来编写数据库应用程序前,应该对链接MySQL的API作好研究
- 在编写应用程序开发时,最好把事务的控制权限交给开发人员,即在程序端进行事务的开始和结束
复制代码
  • 使用自动回滚
- 自动回滚的状况下,开发人员没法定位发生了什么样的错误
- 对于事务的 BEGIN,COMMIT,ROLLBACK 操做应该交给程序端来控制,当发生错误时,咱们能够把异常抛出去再进行回滚
复制代码
相关文章
相关标签/搜索