MySQL两阶段提交串讲,晒出收入

一般当我们的功能函数中有批量的增删改时,我们会添加一个事物包裹这一系列的操作,要么这一组操作全部执行成功,只要有一条SQL执行失败了我们就全部回滚。相信你一定听说过这个比较经典的转账的Case。有一定工作经验的同学都知道,这么做其实是保护我们的数据库中不出现脏数据。整体数据会变得可控。

对MySQL来说你可以通过下面的命令显示的开启、提交、回滚事务

Copy# 开启事务

begin;

或者下面这条命令

start transaction;

提交

commit;

回滚

rollback;

但是日常开发中大家普遍使用编程语言操作数据库。比如Java、Golang… 在使用这种具体编程语言持久层的框架时,它们一般都支持事务操作,比如:在Spring中你可以对一个方法添加注解@Transctional显示的开启事务。Golang的beego中也提供了让你可以显示的开启事务的函数。

有一点不太好的地方是:大家在享受这种编程框架带来的便利的同时,它也屏蔽了你对MySQL事务认知。让人们懒得去往细了看事务

你可以看看我下面这个很简单的Case。

我有一张数据表

CopyCREATE TABLE test_backup (

id int(11) NOT NULL AUTO_INCREMENT,

name varchar(255) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

然后我往这个表中insert几条数据

Copymysql> insert into test_backup values(1,‘tom’);

mysql> insert into test_backup values(2,‘jerry’);

mysql> insert into test_backup values(1,‘herry’);

再去查看binlog。

全网最牛X的!!!MySQL两阶段提交串讲

你会不会诧异?我上面明明没有显示的添加begin、commit命令,但是MySQL实际执行我的SQL时,竟然为我添加上了!

原因很简单:跟大家分享一个参数如下:

全网最牛X的!!!MySQL两阶段提交串讲

一般大家的线上库都会将这个参数置为ON,你的SQL会自动的开启一个事物,并且MySQL会自动的帮你把它提交。

也就是说: 当这个参数为ON时,你使用的DAO持久层框架发送给数据库的SQL其实都会被放在一个事物中执行,然后这个事物被自动提交,而我们对这个过程是无感知的。 具体一点,比如你使用某框架的@Transctional注解,或者在golang中可以像下面的方式获得一个事物:

Copydb := mysql.Client

ops := &sql.TxOptions{

Isolation: 0,

ReadOnly: false,

}

tx, err := db.BeginTx(ctx, ops)

// todo with tx

然后你所有的操作都放在这个事物中执行。

这时你使用的持久层框架肯定会向MySQL发送一条命令:`begin;`或者是`start transcation;`来保证你这一组SQL中执行一条SQL后,开启的事物不会被MySQL自动帮你提交了。

其实还是推荐将这个参数设置成ON的,当然你也可以像下面这样将它关闭

Copymysql> set autocommit = 0;

但是关闭它之后,MySQL不会帮你自动提交事物,全靠研发同学自己来维护就容易会出现长事物,在内存中产生一个极其长的undo log链条。坏处多多。

推荐观看:传送门

=======================================================

三、简单看下两阶段提交的流程#

===============

了解了什么是事物,再来看下什么是两阶段提交。其实所谓的两阶段就是把一个事物分成两个阶段来提交。就像下图这样。

全网最牛X的!!!MySQL两阶段提交串讲

上图为两阶段提交的时序图。

你可以粗略地观察一下上图,MySQL想要准备事务的时候会先写redolog、binlog分成两个阶段。

两阶段提交的第一阶段 (prepare阶段):写rodo-log 并将其标记为prepare状态。

紧接着写binlog

两阶段提交的第二阶段(commit阶段):写bin-log 并将其标记为commit状态。

不了解这些日志是什么有啥用也没关系

四、两阶段写日志用意?#

============

你有没有想过这样一件事,binlog默认都是不开启的状态!

也就是说,如果你根本不需要binlog带给你的特性(比如数据备份恢复、搭建MySQL主从集群),那你根本就用不着让MySQL写binlog,也用不着什么两阶段提交。

只用一个redolog就够了。无论你的数据库如何crash,redolog中记录的内容总能让你MySQL内存中的数据恢复成crash之前的状态。

所以说,两阶段提交的主要用意是:为了保证redolog和binlog数据的安全一致性。只有在这两个日志文件逻辑上高度一致了。你才能放心地使用redolog帮你将数据库中的状态恢复成crash之前的状态,使用binlog实现数据备份、恢复、以及主从复制。而两阶段提交的机制可以保证这两个日志文件的逻辑是高度一致的。没有错误、没有冲突。

当然,两阶段提交能做到足够的安全还需要你合理地设置redolog和binlog的fsync的时机

五、加餐:sync_binlog = 1 问题#

=========================

如果你看懂了我下面说的这些话,能帮你更好的理解两阶段提交哦!纯干货!

在前面的分享binlog的文章中有跟大家提到过一个参数sync_binlog=1。这个参数控制binlog的落盘时机,并且你们公司线上数据库的该参数一定被设置成了1。

Notice!!! 这个参数为1时,表示当事物提交时会将binlog落盘。

现在你用15s中的时间,思考一下,蓝色句子中说的事物提交时会将binlog落盘,这个提交时,是下图中的step1时刻呢?还是step2时刻呢?

![全网最牛X的!!!MySQL两阶段提交串讲](https

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

😕/img-blog.csdnimg.cn/img_convert/42f8d4fc1d5cb8182f1ec2fd6ebf3fec.png)

答案是:step1时刻!

知道这个知识点很重要,下面我来描述这样一个场景。

假如要执行一条update语句,那你肯定知道,先写redolog(便于后续对update事务的回滚)。然后你的update逻辑将Buffer Pool中的缓存页修改成了脏页。

当你准备提交事物时(也就是step1阶段),会写redolog,并将其标记为prepare阶段。然后再写binlog,并将binlog落盘。

然后发生了意外,MySQL宕机了。

那我问你,当你重启MySQL后,update对BufferPool中做出的修改是会被回滚还是会被提交呢?

答案是:会根据redolog将修改后的recovery出来,然后提交。

那为什么会这样做呢?

其实总的来说,不论mysql什么时刻crash,最终是commit还是rollback完全取决于MySQL能不能判断出binlog和redolog在逻辑上是否达成了一致。只要逻辑上达成了一致就可以commit,否则只能rollback。

比如还是上面描述的场景,binlog已经写了,但是MySQL最终选择了回滚。那代表你的binlog比BufferPool(或者Disk)中的真实数据多出一条更新,日后你用这份binlog做数据恢复,是不是结果一定是错误的?

六、如何判断binlog和redolog是否达成了一致#

============================

这个知识点可是纯干货!

当MySQL写完redolog并将它标记为prepare状态时,并且会在redolog中记录一个XID,它全局唯一的标识着这个事务。而当你设置`sync_binlog=1`时,做完了上面第一阶段写redolog后,mysql就会对应binlog并且会直接将其刷新到磁盘中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值