本文为《MySQL归纳学习》专栏的第十三篇文章,同时也是关于《MySQL事务》知识点的开篇文章。
欢迎您阅读《MySQL探秘:解码事务、分布式事务与长事务的奥秘》。事务在MySQL中是如何实现的?分布式事务又是如何达成的呢?我们又该如何避免陷入一些不良的事务习惯?更别提那些让人头疼的长事务问题。本篇文章将揭示这些问题的答案,让你深入了解事务分类、分布式事务的实现以及长事务的特性。
事务分类
从事务理论的角度来看,可以把事务分为以下五种类型:
- 扁平事务(Flat Transactions)
- 带有保存点的扁平事务(Flat Transactions with Savepoints)
- 链事务(Chained Transactions)
- 嵌套事务(Nested Transactions)
- 分布式事务(Distributed Transactions)
上述五大事务分类的讲解,推荐阅读《MySQL技术内幕 InnoDB存储引擎 第2版》章节中的事务,仅作了解即可。
对于InnoDB存储引擎来说,其支持扁平事务,带保存点的事务,链事务,分布式事务。对于嵌套事务,其原生不支持。因此对有并行事务需求的用户来说,MySQL数据库或InnoDB存储引擎就显得无能为力,然而用户仍可以通过带保存点的事务来模拟串行的嵌套事务。
事务实现方式
原子性实现
undo log
名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,它需要记录你要回滚的相应日志信息。 例如
- (1)当你 delete 一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
- (2)当你 update 一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
- (3)当年 insert 一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作
undo log
记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
隔离性实现
利用的是锁和MVCC机制。
关于锁,分为表级锁和行级锁,某一事务先获取到锁,则其他事务因获取不到锁,无法操作数据,进入阻塞状态。
至于MVCC,即多版本并发控制(Multi Version Concurrency Control),一个行记录有多个版本的快照数据,这些快照数据在undo log
中。
如果一个事务读取的行正在做 DELELE 或者 UPDATE 操作,读取操作不会等行上的锁释放,而是读取该行的快照版本。
持久性实现
redo log(重做日志)用来实现事务的持久性,即事务 ACID 中的D。其由两部分组成∶一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log fle),其是持久的。 redo log 是 innodb 引擎层实现的,并不是所有引擎都有。
一致性实现
从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。
但是,如果你在事务里故意写出违反约束的代码,一致性还是无法保证的。例如,你在转账的例子中,你的代码里故意不给B账户加钱,那一致性还是无法保证。因此,还必须从应用层角度考虑。
从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!
上面提到的 redo log、undo log、锁和MVCC 机制,后续会有专门的章节进行讲解。
分布式事务
基于 2pc 的分布式事务
分布式事务有多种实现方式,如 2PC(二阶段提交)、3PC(三阶段提交)、TCC(补偿事务)等,MySQL 是基于 2PC 实现的分布式事务,下面介绍 2PC 分布式事务实现方式。
两阶段提交:Two-Phase Commit , 简称 2PC,为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法。
2PC 的算法思路可以概括为,参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报,决定各参与者是否要提交操作还是中止操作。这里的参与者可以理解为 Resource Manager (RM),协调者可以理解为 Transaction Manager(TM)。
下图说明了 RM 和 TM 在分布式事务中的运作过程:
- 第一阶段提交:TM 会发送 Prepare 到所有 RM 询问是否可以提交操作,RM 接收到请求,实现自身事务提交前的准备工作并返回结果。
- 第二阶段提交:根据 RM 返回的结果,所有 RM 都返回可以提交,则 TM 给 RM 发送 commit 的命令,每个 RM 实现自己的提交,同时释放锁和资源,然后 RM 反馈提交成功,TM 完成整个分布式事务;如果任何一个 RM 返回不能提交,则涉及分布式事务的所有 RM 都需要回滚。
MySQL 分布式事务 XA
InnoDB 存储引擎提供了对 XA 事务的支持,并通过XA 事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources)参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的 ACID 要求又有了提高。另外,在使用分布式事务时,InnoDB 存储引/擎的事务隔商级别必须设置为 SERIALIZABLE。
MySQL 分布式事务 XA 是基于上面的 2pc 框架实现,下面详细介绍 MySQL XA 相关内容。
XA 规范中分布式事务由 AP、RM、TM 组成:
- **资源管理器(Resource Managers):**提供访问事务资源的方法。通常一个数据库就是一个资源管理器
- **事务管理器(Transaction Manager):**协调参与全局事务中的各个事务。需要和参与全局事务的所有资源管理器进行通信
- **应用程序(Application Program):**定义事务的边界,指定全局事务中的操作
在MySQL数据库的分布式事务中,资源管理器就是MySQL数据库,事务管理器为连接MySQL服务器的客户端。
分布式事务使用两段式提交的方式:
- **第一阶段,**所有参数全局事务的节点都开始准备(PREPARE),告诉事务管理器它们准备好提交了。
- **第二阶段,**事务管理器告诉资源管理器质性ROLLBACK还是COMMIT。如果任何一个节点显示不能提交,则所有的节点都被告知需要回滚。
可见与本地事务不同的是,分布式事务需要多一次的PREPARE操作,待收到所有节点的同一信息后,再进行COMMIT或是ROLLBACK操作。
避免不好的事务习惯
1、在循环中提交SQL
我们先以 DBA 的角度来循环执行 SQL,下面有3个存储过程。
CREATE TABLE `t_color` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE DEFINER=`root`@`localhost` PROCEDURE `idata1`()
begin
declare i int;
set i=1;
start transaction;
while(i