MySQL~认识事务、事务的分类、事务的实现原理、事务注意事项

认识事务

  • 谈到事务,那肯定少不了ACID的特性,ACID是以下几个单词的缩写,下面一一对其进行介绍

原子性(atomicity)
一致性(consistency)
隔离性(isolation)
持久性(durability)

原子性

  • 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 例如我们去ATM机取钱,流程如下
  1. 插入银行卡,验证密码是否正确
  2. 从远程银行的数据库中获取当前账户的信息
  3. 用户在ATM机上输入想要提取的金额
  4. 从远程银行的数据库中更行账户信息
  5. ATM出款
  6. 用户取款
  • 上述的过程就应该视为一个原子操作,要么全都发生,要么就不发生。因为不可能扣账户中的钱,ATM却不出钞票,又或者是出了钞票,我的账户却没发生变化,这种行为是不容许发生的。

一致性

  • 一致性即事务操作前与操作后的状态始终一致。

  • 如何理解呢?就好比我们此时有用户A和用户B,他们的余额分别为300元和700元,此时两人总金额为1000元。此时若是用户B向用户A转账200元,则两者的此时都有500元,总金额还是1000元。

  • 也就是说,无论我们两个怎么转账,总金额它只会是1000,既不会多,也不会少。这就是事务操作前后的状态始终一致。倘若钱多了或者少了,都代表着事务将数据库从一种状态变为了另外一种状态,此时就不再符合一致性了。

持久性

  • 持久性指的是事务一旦提交,其结果就是永久性的。即使发生了服务器宕机的事故,数据库也能成功的将数据给恢复。

  • 但是需要注意的是,只能从事务本身的角度来保证结果的永久性,即事务提交后所有变化都是永久的,但是这仅仅局限于数据库本身发生的问题,倘若是由于外部原因如RAID卡损坏、天灾人祸导致数据库发生问题,那么即使事务提交了,也可能会丢失。

  • 基于上述原因,持久性只能保证事务系统的高可靠性,而无法保证其高可用性。

隔离性

  • 隔离性指的是每个读写事务的对象之间相互隔离,即该事务提交前对其他事务都不可见。
  • 隔离性的详细内容和实现原理在我上一篇文章中有讲到

事务的使用

  • 开启事务 PS:(由于MySQL的数据分析器会自动将BEGIN识别为BEGIN…END,所以在存储过程中只能使用START TRANSACTION来开启事务)
START TRANSACTION
或者
BEGIN
(由于MySQL的数据分析器会自动将BEGIN识别为BEGIN...END,所以在存储过程中只能使用START TRANSACTION来开启事务)

  • 提交事务
COMMIT
  • 设置保存点
SAVEPOINT 保存点ID

  • 删除保存点
RELEASE SAVEPOINT 保存点ID

  • 回滚事务
//回滚整个事务
ROLLBACK

//回滚至某个保存点
ROLLBACK TO SAVEPOINT 保存点ID

  • 设置隔离级别
SET TRANSACTION

事务的分类

扁平事务

  • 事务类型中最常用的,使用最频繁的, 在扁平事务中所有操作都是同一层次, 开启事务到结束事务其间所有的操作都是原子的, 要么都执行要么都回滚,因此扁平事务是程序中原子操作的基本组成模块
  • 但是如果事务量巨大, 并且事务中的操作不受前后影响, 使用扁平事务的消耗会很大, 比如下面案例:

操作1:开启事务
操作2:在学校买公交车票去西安
操作3:买飞机票去北京
操作4: 买高铁票去天津

  • 但是在实际操作过程中, 如果在执行操作4的时候出错了, 使用扁平事务一旦回滚, 就要回到学校, 其实我们可以设置保存点, 在这个事务中操作4一旦出错就一个回滚到操作3,然后执行别的操作, 比如执行操作5:买大巴票去天津

带有保存点的扁平事务

  • 使用带有保存点扁平的事务就是为了避免回滚的时候开销太大,使用保存点来通知吸入记住当前事务的状态,以便后续出错就回滚到这里即可
  • 在一个事务中 可以设置多个保存点, 这些保存点是在该事务内部递增的, 比如一个操作执行失败了回滚到保存点2, 那么记下来的保存点还是应该以3来开始设置

链事务

  • 链事务可以视为保存点模式的一种变种, 带有保存点的事务具有易失性, 当系统奔溃的时候, 保存点都将消失, 当进行恢复的时候需要从开始重新执行
  • 链事务的思想就是在提交一个事务的时候, 原子的执行下一个事务, 意味着下一个事务可以看到上一个事务的结果, 就好像在一个事务中一样, 他是一个双向链表, 所以链事务中的每一个节点只能指向其上一个事务和下一个事务, 在回滚中吗也只能回滚到上一个事务中, 而带有保存点的事务是可以回滚到任意保存点的

嵌套事务

  • 嵌套事务是一个层次架构, 有一个顶层事务控制各个层次的事务
  • 嵌套事务是有若干事务组成的一棵树, 子树既可以是嵌套事务吗也可以是扁平事务, 也就是说叶子事务一定是扁平事务
  • 子事务既可以提交也可以回滚,但是他的提交并不会马上生效, 除非其父事务已经提交,因为可以得到结论, 任何子事务都在顶层事务提交后才算真正的提交
  • 树中任何一个事务的回滚都会触发让其子事务都进行回滚

分布式事务

  • InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现
  • 分布式事务指的是:允许多个独立的事务资源参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源
  • 全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的ACID要求又有了提高。另外,在使用分布式事务时,InnoDB的事务隔离级别必须设置为SERIALIZABLE
  • XA事务允许不同数据库之间的分布式事务,如一台服务器是MySQL数据库的,另一台是Oracle数据库的,又可能还有一台服务器是SQL Server的,只要参与在全局事务中的每个节点都支持XA事务
  • 很简单的一个例子就是一个用户在西安的银行给另一个用户在北京的银行转钱, 这就是一个分布式事务, 其资源不再一台资源管理器上, 如果任何一个结点失败都会带来很大的麻烦

XA事务

  • XA事务由下面3部分组成:
  1. 资源管理器(Resource Managers):提供访问事务资源的方法。通常一个数据库就是一个资源管理器
  2. 事务管理器(Transaction Manager):协调参与全局事务中的各个事务。需要和参与全局事务的所有资源管理器进行通信
  3. 应用程序(Application Program):定义事务的边界,指定全局事务中的操作
  • 在MySQL数据库的分布式事务中,资源管理器就是MySQL数据库,事务管理器为连接MySQL服务器的客户端
    在这里插入图片描述

在这里插入图片描述

  • 分布式事务使用两段式提交的方式:
    第一阶段,所有参数全局事务的节点都开始准备(PREPARE),告诉事务管理器它们准备好提交了
    第二阶段,事务管理器告诉资源管理器质性ROLLBACK还是COMMIT。如果任何一个节点显示不能提交,则所有的节点都被告知需要回滚
    可- 见与本地事务不同的是,分布式事务需要多一次的PREPARE操作,待收到所有节点的同一信息后,再进行COMMIT或是ROLLBACK操作

内部XA事务

  • 之前讨论的分布式事务是外部事务,即资源管理器是MySQL数据库本身。在MySQL数据库中还存在另外一种分布式事务,其在存储引擎与插件之间,又或者在存储引擎与存储引擎之间,称之为内部XA事务
  • 为了解决内部事务问题,MySQL数据库在内部之间还是采用XA事务。当事务提交时,InnoDB会先做一个PREPARE操作,将事务的xid写入,接着进行二进制日志的写入
  • 如果在InnoDB存储引擎提交前,MySQL数据库发生宕机了,那么MySQL数据库在重启后会先检查准备的xid事务是否已经提交,若没有,则在存储引擎层再进行一次提交操作

事务的实现原理

  • 事务的持久性主要依靠redo log(重做日志)来完成,而原子性、一致性则通过undo log(撤销日志)来完成。至于隔离性,则通过锁来完成,
  • 首先要知道,redo和undo的左右都可以视为一种恢复操作,所以undo绝不是redo的逆过程。其中redo恢复提交事务修改的页操作,而undo回归行记录到某个特定版本。也因为如此,两者记录的内容不同,redo通常是物理日志,记录的是页的物理修改操作。而undo是逻辑日志,根据每行记录进行记录。

redo

  • redo log是物理日志, 在事务提交时,必须先将该事务的所有执行信息日志写入到redo日志文件中
  • redo log由两部分组成,一是存在于内存中的重做日志缓冲(redo log buff),由于存在内存中,所以其具有易失性。二是重做日志文件(redo log file),其存在于硬盘中,所以是持久的。
  • redo主要通过Force Log at Commit机制来实现事务的持久性。
    在这里插入图片描述
  • **当事务提交时,将该事务的所有重做日志从缓冲区中写入到重做日志文件进行持久化,当事务的提交完成后才算完成。**为了确保每次日志每次都写入文件中,在每次将日志缓冲写入日志文件后,都会发起一次异步操作(fsync)

还有需要了解的是:
redo log buffer将内存中的log block刷新到磁盘是有一定的规则的:事务提交时(前面已经提到)、当log buffer中有一半的内存空间被使用时、log checkpoint时。

  • 为什么需要这个异步调用呢?**因为重做日志缓冲会先写入文件系统缓冲,为了保证其能够成功写入磁盘,必须发起一次异步调用。**由于异步调用的效率取决于磁盘的性能,因此磁盘的性能决定了事务提交的性能,即数据库性能。
  • 在InnoDB存储引擎中,重做日志都是以512字节为单位进行存储的,这意味着重做日志缓存、重做日志文件块都是以块(block)的方式进行保存的,称为重做日志块(redo log block)。每块的大小512字节。由于重做日志块的大小和磁盘扇区大小一样,都是512字节,因此重做日志的写入可以保证原子性,不需要double write技术

undo

  • undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。
  • 当事务需要回滚操作的时候,就需要用到undo。undo是撤销日志,其中保留了数据库各个版本的状态,我们可以借助undo逻辑地将数据库恢复到原来地样子。除了进行回滚之外,undo的另一个作用就是实现MVCC技术, 实现非锁定读
    -
  • 从一上图可以看出,每当事务发生变更的时候,都会伴随着undo log的产生,并且为了防止其丢失,undo log会比数据先持久化到硬盘上。
  • 由于undo log是逻辑日志,所以其中记录的都是对于数据库的操作指令。而事务的回滚,其实也就是根据这个操作来进行一个逆向操作。如下面几种

当执行一个insert指令时,其逆向指令为delete
当执行一个delete指令时,其逆向指令为insert
当执行一个update指令时,其逆向指令为update

  • 原子性就是借助以上机制实现,倘若事务中的某一个步骤未能成功完成,则借助undo log中存储的记录来回滚到事务的最原始状态
  • 而至于一致性,则主要依靠上述的其他三种特性来实现,也就是说一致性是目的,而原子性、隔离性、持久性则是数据库实现一致性的手段,只有满足这三个性质,才能够保证一致性。
  • 在InnoDB里,undo log分为如下两类:
    ①insert undo log : 事务对insert新记录时产生的undo log, 只在事务回滚时需要, 并且在事务提交后就可以立即丢弃。
    ②update undo log : 事务对记录进行delete和update操作时产生的undo log,不仅在事务回滚时需要,快照读也需要,所以不能轻易删除,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被purge线程删除。

Purge线程:上文提到了InnoDB删除一个行记录时,并不是立刻物理删除,而是将该行数据的DB_TRX_ID字段更新为做删除操作的事务ID,并将删除位deleted_bit设置为true(已删除),将其放入update undo log中。为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。purge线程自己也维护了一个read view,如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。

事务注意事项

  1. 不要再循环中去提交事务, 因为InnoDB默认是自动提交的, 如果再进行额外的提价只是在浪费资源
  2. 不使用自动提交, 方便开发人员去控制事务
  3. 不使用自动回滚, 因为这样就会很难找出发生了什么样的错误
  4. 不使用长事务, 如果执行的事务很长的话, 会上很多的锁会很大的降低数据库的性能而且不易于事务的回滚, 所以将其转换为小批量的事务来进行处理,将完成的数据记录下来,后面一旦出错也只用回滚一部分即可
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值