zoukankan      html  css  js  c++  java
  • 《MySQL技术内幕-InnoDB存储引擎》整理6-事务

    一、认识事务

    1、概述

    • A(Atomicity)原子性:原子性是指数据库事务是不可分割的工作单位,只有使事务中所有的数据库操作都执行成功,才算整个事务成功,事务中任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库状态应该回退到执行事务前的状态。
    • C(Consistency)一致性:一致性指事务将数据库从一种状态转变为下一种一致性状态,在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏。
    • I(Isolation)隔离性:事务的隔离性要求每个读写事务的对象对其他事务的操作对象能够相互分离,即该事务提交前对其他事务都不可见,通常由锁来实现。
    • D(Durability)持久性:事务一旦提交,其结果就是永久性的,即使发生宕机等故障,数据库也能将数据恢复

    2、分类

    从事务理论的角度,可以将事务分为以下几种类型:

    • 扁平事务:所有事务中最简单的一种,也是使用最为频繁的一种,所有操作都处于同一层级,由Begin Work开始,Commit Work或者RollBack Work结束
    • 带有保存点的扁平事务:除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态。保存点用来通知系统应该记住事务当前的状态,以便发生错误时,事务能回到保存点当时的状态,保存点通过Save Work函数来建立
    • 链事务:保存点模式的变种,其思想是在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务,即将提交事务操作和开始下一个事务操作合并为一个原子操作。链式事务只能恢复到最近一个保存点,在执行Commit后即释放了当前事务所持有的锁
    • 嵌套事务:是一个层次结构框架,由一个顶层事务控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务,控制着每一个局部的变换
    • 分布式事务:通常是一个在分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。

    InnoDB存储引擎支持扁平事务,带有保存点的事务,链事务和分布式事务,不支持嵌套事务

    二、事务的实现

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

    1、redo

    1、基本概念

    重做日志用来实现事务的持久性,其由两部分组成:内存中的重做日志缓冲,其是易失的;重做日志文件,其是持久的。InnoDB是事务的存储引擎,当事务提交时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的Commit操作完成才算完成,这里的日志为redo log和undo log。

    为了确保每次日志都写入重做日志文件,在每次将重做日志缓冲写入重做日志文件后,InnoDB存储引擎都需要调用一次fsync操作,fsync的效率取决于磁盘的性能。参数innodb_flush_log_at_trx_commit用来控制重做日志刷新到磁盘的策略

    • 默认值为1表示事务提交时必须调用一次fsync操作;
    • 0表示事务提交时不进行写入重做日志操作,重做日志操作由master thread完成,其会每1秒进行一次重做日志操作;
    • 2表示事务提交时将重做日志写入重做日志文件,但仅写入文件系统缓冲,不进行fsync操作

    Mysql数据库中还有一种二进制日志,其用来进行Point-In-Time的恢复以及主从复制环境的建立。它是一种逻辑日志,记录的是SQL语句。二进制日志仅在事务提交时记录,对于每一个事务仅包含对应事务的一个日志。

    2、log block

    在InnoDb存储引擎中,重做日志都是以512字节进行存储的,即重做日志缓存和重做日志文件都是以block进行保存的,称之为重做日志块,每块大小为512字节。重做日志块除了日志本身,还包括日志块头和日志块尾两部分组成。日志块头中的信息可以用来标记一些块的信息

    3、log group

    log group称为重做日志组,其中有多个重做日志文件,它只是一个逻辑上的概念,并没有一个实际存储的物理文件来表示。它由多个重做日志文件组成,每个log group中的重做日志文件大小是相同的,且其总大小只能小于4GB,后提高为512GB。log group中的第一个redo log file,其前2KB的部分保存4个512字节大小的块,其中会包含一些检查点信息,对于恢复操作来说至关重要

    4、重做日志格式

    InnoDB存储引擎的存储是基于页的,因此重做日志格式也是基于页的,虽然有着不同的重做日志格式,但是它们有着通用的头部格式:

    • redo_log_type:重做日志的类型
    • space:表空间ID
    • page_no:页的偏移量

    5、LSN

    LSN是Log Sequence Number的缩写,其代表的是日志序列号,在InnoDB存储引擎中共占8个字节,并且单调递增,其代表的含义有:①重做日志写入的总量;②CheckPoint的位置;③页的版本

    6、恢复

    InnoDB存储引擎在启动时不管上次数据库运行时是否正常关闭,都会尝试进行恢复操作。由于CheckPoint表示已经刷新到磁盘页上的LSN,因此在恢复过程中仅需恢复CheckPoint开始的日志部分。InnoDB存储引擎的重做日志是物理日志,因此恢复速度较二进制日志快得多。

    2、undo

    1、基本概念

    undo信息可以用来进行信息的回滚操作,它存放在数据库内部的一个特殊段中,这个段称为undo段。undo段位于共享表空间中。undo并不是将数据库物理的恢复到执行语句或事务之前的样子,而是将数据库逻辑的恢复到原来的样子。

    undo可以实现多版本并发控制,即用户读取一行记录时,若该纪录被其他事务占用,当前事务可以读取undo读取之前的行版本信息,以此实现非锁定读取。此外,undo log的产生会伴随着redo log的产生,这也是undo log需要持久性保护的原因

    2、undo存储管理

    InnoDB存储引擎对undo的管理同样采用段的方式,InnoDB存储引擎有rollback segment,每个回滚段种记录了1024个undo log segment,而在每个undo log segment段种进行undo页的申请。从InnoDB1.1版本开始,InnoDB最大支持128个rollback segment,这些rollback segment都存储于共享表空间中,从InnoDB1.2版本开始,又可以通过以下设置对rollback segment进行进一步设置:

    • innodb_undo_directory:用于设置rollback segment文件所在的路径,默认值为.表示InnoDB存储引擎的目录
    • innodb_undo_logs:用来设置rollback segment的个数,默认值为128
    • innodb_undo_tablespaces:用来设置构成rollback segment文件的数量

    3、undo log格式

    InnoDB存储引擎中,undo log分为insert undo log和update undo log。Insert undo log开始的前两个字节next记录的是下一个undo log的位置,尾部的两个字节记录的是undo log的开始位置,type_no记录事务的ID,table_id记录undo log所对应的表对象,其余部分记录了所有主键的列和值,rollback时可以根据这些值找到具体的记录,在进行删除操作。update undo log记录的是对delete和update操作产生的undo log,由于该undo log可能需要提供MVCC机制,所以不能在事务提交时删除,它会在事务提交时放入undo log链表,等待purge线程进行最后的删除。

    4、查看undo信息

    Oracle和MSSQL可以通过内部的数据字典来查看当前的undo信息,而InnoDB则不行,只能通过原理和个人进行进行判断

    3、purge

    delete和update操作不能直接删除原有的数据,真正的删除操作被延时了,并最终在purgr操作中完成。当某行数据不被任何其他事务引用时,则可以进行真正的删除操作。InnoDB存储引擎有一个history表,它根据事务提交的顺序,将undo log进行链接。当清理发生时,InnoDB存储引擎首先从history list中找到第一个需要被清理的记录,清理后再继续寻找可清理的对象,直至最尾端,这也是undo page可以被重用的原因

    innodb_purge_batch_size用来设置每次purge操作时需要清理的undo page数量,1.2版本之后默认值为300,其值越大回收的undo页越多,CPU和磁盘IO的操作也会越多,性能也会随之下降。innodb_max_purge_lag用来控制history list的长度,其长度越大,InnoDB存储引擎的压力也会越高,其默认值为0表示不对history list做任何限制,当大于0时,就会延迟DML操作。延迟算结果为(length(history_list)-innodo_max_purge_lag)*10-5,单位为毫秒,且作用对象是行,即更新5行数据时,总延迟时间为5个单位,每次purge操作后会重新计算延迟值。innodb_max_purge_lag_delay用来控制delay的最大毫秒数,以避免延迟过大导致的无限制等待

    4、group commit

    为了提高磁盘fsync的效率,当前数据库提供了group commit的供,即一次fsync可以刷新确保多个事务日志被写入文件。在开启二进制日志后,InnoDB存储引擎的group commit功能会失效,从而导致性能的下降,这是由于开启二进制日志后,为了保证存储引擎层中的事务与二进制日志的一致性,二者使用了二阶段事务,导致每个步骤都需要进行一次fsync。

    Mysql5.6以后,Binary Log Group Commit(BLGC)的实现方式解决了上述问题,其实现方式是将事务提交的过程分为几个步骤来完成。在Mysql数据库上层进行提交时首先按顺序将其放入一个队列中,队列的第一个事务称为leader,其他事务称为follower,leader控制着follower的行为,BLGC的步骤分为如下三个阶段:

    • Flush阶段,将每个事务的二进制日志写入内存中;
    • Sync阶段,将内存中的二进制日志刷新到磁盘,若队列中有多个事务,那么仅以此fsync操作就完成了二进制日志的写入;
    • Commit阶段,leader根据顺序调用存储引擎层事务的提交,而InnoDB存储引擎是支持group commit的

    当有一组事务在进行Commit阶段时,其他新事物可以进行Flush阶段,从而使group commit不断生效,且事务越多,group commit的效果越明显,数据库性能的提升也就越大。

    三、事务控制语句

    在Mysql命令行的默认设置下,事务都是自动提交的,因此要显式地开启一个事务需要使用命令Begin、Start Transaction,或者执行命令Set AutoCommit=0,禁用当前的自动提交。部分控制语句如下:

    • Start Transaction|Begin:显式地开启一个事务
    • Commit:提交事务
    • RollBack:结束用户的事务,并撤销正在进行的所有未提交的修改
    • SavePoint identifier:在事务中创建一个保存点,一个事务中可以有多个SavePoint
    • Release SavePoint identifier:删除一个事务的保存点
    • RollBack To [SavePoint] identifier:把事务回滚到标记点
    • Set Transaction:设置事务的隔离级别

    需要注意的是RollBack To SavePoint并不是真正地结束一个事务,因此需要显式地运行Commit或者RollBack命令

    四、隐式提交的SQL语句

    以下语句执行后会有一个隐式地Commit操作:①DDL语句;②修改Mysql架构的操作;③管理语句

    需要注意在MSSQL中,DDL操作是可以回滚的,而InnoDB存储引擎不行

    五、对于事务操作的统计

    InnoDB存储引擎的应用在考虑QPS的同时,应该关注每秒事务处理的能力(TPS)。计算TPS的方法是(com_commit+com_rollback)/time,其计算前提是所有事务都是显式提交的,隐式提交及回滚不会计算到com_commit和com_rollback变量中

    六、事务隔离级别

    SQL标准定义的四个隔离级别为:Read UnCommitted、Read Committed、Repeatable Read、Serializable。隔离级别越低,事务请求的锁越少或保持锁的时间越短。InnoDB存储引擎默认支持的隔离级别是Repeatable Read,且使用Netx-Key Lock算法避免幻读,这与其他数据库不同,也就是说InnoDB存储引擎在默认的Repeatable Read隔离级别下,已经能够完全保证事务隔离性的要求,达到SQL标准的Serializable隔离级别

    在Serialiable的事务隔离级别,InnoDB存储引擎会对每个Select语句自动加上Lock In Share Mode,即为每个读取操作加一个共享锁,因此该隔离级别下,对一致性的非锁定读不再予以支持。Serialiable的事务隔离级别主要用于InnoDB存储引擎的分布式事务。

    在Read Committed隔离级别下,除了唯一性的约束检查以及外键约束使用gap lock,InnoDB存储引擎不会使用gap lock锁算法,在Mysql5.1中,该事务隔离级别只能工作在replication二进制日志为Row的格式下,Mysql5.1版本后,因为支持了Row格式的二进制日志记录格式,因而避免了一些不同步现象的产生。此外,即使不使用Read Committed隔离级别,也应当考虑将二进制日志的格式更换为Row,以进一步保证数据的同步

    七、分布式事务

    1、Mysql数据库分布式事务

    InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现。分布式事务是指允许多个独立的事务资源参与到一个全局事务中。在使用分布式事务时,InnoDB存储引擎的事务隔离级别必须设置为Seaializable

    XA事务允许不同数据库之间的分布式事务,XA事务由一个或多个资源管理器、一个事务管理器以及一个应用程序组成。资源管理器提供访问事务资源的方法,通常一个数据库就是一个资源管理器,事务管理器协调参与全局事务的各个事务,应用程序定义事务的边界,指定全局事务中的操作。

    分布式事务使用两段式提交的方法,在第一阶段,所有参与全局事务的节点都开始准备,告诉事务管理器它们准备好提交,第二阶段事务管理器告诉资源管理器是执行Commit还是RollBack。与本地事务不同的是,分布式事务需要多一次的准备操作,所有节点同意后,再进行Commit或者RollBack操作。

    2、内部XA事务

    在Mysql数据库中还存在另一种分布式事务,其存在于存储引擎与插件,或者是存储引擎与存储引擎之间,称为内部XA事务。最常见的内部XA事务存在于binlog与InnoDB存储引擎之间,由于复制的需要,大多数数据库都开启了binlog功能,在事务提交时,先写二进制日志,再写InnoDB存储引擎的重做日志,两者必须保持同步。事务提交时,InnoDB存储引擎会先做一个Prepare操作,将事务的xid写入,接着进行二进制日志的写入,如在提交前宕机,重启后数据库会检查准备的UXID事务是否已经提交,如没有则存储引擎会再进行一次提交操作

    八、不好的事务习惯

    避免在循环中提交;避免使用自动提交;避免使用自动回滚

    九、长事务

    对于较长的事务,有时可以通过转换为小批量的事务来进行处理,当事务发生错误时,只需要回滚一部分数据

    作者:Jscroop
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    【leetcode】1534. 统计好三元组
    【leetcode】1351. 统计有序矩阵中的负数
    【leetcode】1523. 在区间范围内统计奇数数目
    【leetcode】204. 计数质数
    【leetcode】993. 二叉树的堂兄弟节点
    【leetcode】1598. 文件夹操作日志搜集器
    【leetcode】1389. 按既定顺序创建目标数组
    【leetcode】增减字符串匹配
    【leetcode】1185.一周中的第几天
    052-158
  • 原文地址:https://www.cnblogs.com/Jscroop/p/14672071.html
Copyright © 2011-2022 走看看