查看mysql日志post_(转)MySQL 日志组提交

原文:https://jin-yang.github.io/post/mysql-group-commit.html

组提交 (group commit) 是为了优化写日志时的刷磁盘问题,从最初只支持 InnoDB redo log 组提交,到 5.6 官方版本同时支持 redo log 和 binlog 组提交,大大提高了 MySQL 的事务处理性能。

下面将以 InnoDB 存储引擎为例,详细介绍组提交在各个阶段的实现原理。

简介

自 5.1 之后,binlog 和 innodb 采用类似两阶段提交的方式,不过不支持 group commit;在 5.6 中,将 binlog 的 commit 阶段分为三个阶段:flush stage、sync stage 以及 commit stage。

这三个阶段中,每个阶段都会去维护一个队列,各个列表的定义如下。

Mutex_queue m_queue[STAGE_COUNTER];

如上,每个阶段都在维护一个队列,第一个进入该队列的作为 leader 线程,否则作为 follower 线程;leader 线程会收集 follower 的事务,并负责做 sync,follower 线程等待 leader 通知操作完成。

尽管维护了三个队列,但队列中所有的 THD 实际上都是通过 next_to_commit 连接起来。binlog 在事务提交阶段,也就是在 MYSQL_BIN_LOG::ordered_commit() 函数中,开始 3 个阶段的流程。

接下来,看看 MySQL 中事务是如何提交的。

事务提交

接下来,看看 InnoDB 和 binlog 提交的流程。

二阶段提交

详细介绍下二阶段提交的过程。

未开启binlog时

InnoDB 通过 redo 和 undo 日志来恢复数据库 (safe crash recovery),当数据恢复时,通过 redo 日志将所有已经在存储引擎内部提交的事务应用 redo log 恢复,所有已经 prepared 但是没有 commit 的事务则会通过 undo log 做回滚。

然后客户端连接时就能看到已经提交的数据存在数据库内,未提交被回滚地数据需要重新执行。

开启binlog时

为了保证存储引擎和 MySQL 的 binlog 保持一致,引入二阶段提交 (two phase commit, 2pc) 。

因为备库通过 binlog 重放主库提交的事务,假设主库存储引擎已经提交而 binlog 没有保持一致,则会使备库数据丢失造成主备数据不一致。

二阶段提交

如下是二阶段提交流程。

3ba8e2a9e71041d306ab353bb2166622.png

详细执行流程为:

InnoDB 的事务 Prepare 阶段,即 SQL 已经成功执行并生成 redo 和 undo 的内存日志;

binlog 提交,通过 write() 将 binlog 内存日志数据写入文件系统缓存;

fsync() 将 binlog 文件系统缓存日志数据永久写入磁盘;

InnoDB 内部提交,commit 阶段在存储引擎内提交,通过 innodb_flush_log_at_trx_commit 参数控制,使 undo 和 redo 永久写入磁盘。

开启 binlog 的 MySQL 在崩溃恢复 (crash recovery) 时:

在 prepare 阶段崩溃,恢复时该事务未写入 binlog 且 InnoDB 未提交,该事务直接回滚;

在 binlog 已经 fsync() 永久写入 binlog,但 InnoDB 未来得及 commit 时崩溃;恢复时,将会从 binlog 中获取提交的信息,重做该事务并提交,使 InnoDB 和 binlog 始终保持一致。

以上提到单个事务的二阶段提交过程,能够保证 InnoDB 和 binlog 保持一致,但是在并发的情况下怎么保证存储引擎和 binlog 提交的顺序一致?当并发提交的时,如果两者不一致会造成什么影响?

组提交异常

首先看看,对于上述的问题,当并发提交的时,如果两者不一致会造成什么影响?

7be72b008fe08379a988c828ece4c8b3.png

如上所示,事务按照 T1、T2、T3 顺序开始执行,并依相同次序按照写入 binlog 日志文件系统缓存,调用 fsync() 进行一次组提交,将日志文件永久写入磁盘。

但是存储引擎提交的顺序为 T2、T3、T1,当 T2、T3 提交事务之后做了一个 On-line 的备份程序新建一个 slave 来做复制;而搭建备库时,CHANGE MASTER TO 的日志偏移量在 T3 事务之后。

那么事务 T1 在备机恢复 MySQL 数据库时,发现 T1 未在存储引擎内提交,那么在恢复时,T1 事务就会被回滚,此时就会导致主备数据不一致。

结论:需要保证 binlog 的写入顺序和 InnoDB 事务提交顺序一致,用于 xtrabackup 备份恢复。

早期解决方案

早期,使用 prepare_commit_mutex 保证顺序,只有当上一个事务 commit 后释放锁,下个事务才可以进行 prepara 操作,并且在每个事务过程中 binlog 没有 fsync() 的调用。

1d1a3d9da66f5bf822947dc65deb4cf8.png

由于内存数据写入磁盘的开销很大,如果频繁 fsync() 把日志数据永久写入磁盘,数据库的性能将会急剧下降。为此提供 sync_binlog 参数来设置多少个 binlog 日志产生的时候调用一次 fsync() 把二进制日志刷入磁盘来提高整体性能,该参数的设置作用为:

sync_binlog=0,二进制日志 fsync() 的操作基于系统自动执行。

sync_binlog=1,每次事务提交都会调用 fsync(),最大限度保证数据安全,但影响性能。

sync_binlog=N,当数据库崩溃时,可能会丢失 N-1 个事务。

prepare_commit_mutex 的锁机制会严重影响高并发时的性能,而且 binlog 也无法执行组提交。

改进方案

接下来,看看如何保证 binlog 写入顺序和存储引擎提交顺序是一致的,并且能够进行 binlog 的组提交?5.6 引入了组提交,并将提交过程分成 Flush stage、Sync stage、Commit stage 三个阶段。

这样,事务提交时分为了如下的阶段:

InnoDB, Prepare

SQL已经成功执行并生成了相应的redo和undo内存日志;

Binlog, Flush Stage

所有已经注册线程都将写入binlog缓存;

Binlog, Sync Stage

binlog缓存将sync到磁盘,sync_binlog=1时该队列中所有事务的binlog将永久写入磁盘;

InnoDB, Commit stage

leader根据顺序调用存储引擎提交事务;

每个 Stage 阶段都有各自的队列,从而使每个会话的事务进行排队,提高并发性能。

如果当一个线程注册到一个空队列时,该线程就做为该队列的 leader,后注册到该队列的线程均为 follower,后续的操作,都由 leader 控制队列中 follower 行为。

leader 同时会带领当前队列的所有 follower 到下一个 stage 去执行,当遇到下一个 stage 为非空队列时,leader 会变成 follower 注册到此队列中;注意:follower 线程绝不可能变成 leader 。

配置参数

与 binlog 组提交相关的参数主要包括了如下两个参数。

binlog_max_flush_queue_time

单位为微妙,用于从 flush 队列中取事务的超时时间,这主要是防止并发事务过高,导致某些事务的 RT 上升,详细的内容可以查看函数 MYSQL_BIN_LOG::process_flush_stage_queue() 。

注意:该参数在 5.7 之后已经取消了。

binlog_order_commits

当设置为 0 时,事务可能以和 binlog 不同的顺序提交,其性能会有稍微提升,但并不是特别明显.

源码解析

binlog 的组提交是通过 Stage_manager 管理,其中比较核心内容如下。

class Stage_manager {

public:

enum StageID { // binlog的组提交包括了三个阶段

FLUSH_STAGE,

SYNC_STAGE,

COMMIT_STAGE,

STAGE_COUNTER

};

private:

Mutex_queue m_queue[STAGE_COUNTER];

};

组提交 (Group Commit) 三阶段流程,详细实现如下。

MYSQL_BIN_LOG::ordered_commit() ← 执行事务顺序提交,binlog group commit的主流程

|

|-#########>>>>>>>>> ← 进入Stage_manager::FLUSH_STAGE阶段

|-change_stage(..., &LOCK_log)

| |-stage_manager.enroll_for() ← 将当前线程加入到m_queue[FLUSH_STAGE]中

| |

| | ← (follower)返回true

| |-mysql_mutex_lock() ← (leader)对LOCK_log加锁,并返回false

|

|-finish_commit() ← (follower)对于follower则直接返回

| |-ha_commit_low()

|

|-process_flush_stage_queue() ← (leader)对于follower则直接返回

| |-fetch_queue_for() ← 通过stage_manager获取队列中的成员

| | |-fetch_and_empty() ← 获取元素并清空队列

| |-ha_flush_log()

| |-flush_thread_caches() ← 对于每个线程做该操作

| |-my_b_tell() ← 判断是否超过了max_bin_log_size,如果是则切换binlog文件

|

|-flush_cache_to_file() ← (follower)将I/O Cache中的内容写到文件中

|-RUN_HOOK() ← 调用HOOK函数,也就是binlog_storage->after_flush()

|

|-#########>>>>>>>>> ← 进入Stage_manager::SYNC_STAGE阶段

|-change_stage()

|-sync_binlog_file()

| |-mysql_file_sync()

| |-my_sync()

| |-fdatasync() ← 调用系统API写入磁盘,也可以是fsync()

|

|-#########>>>>>>>>> ← 进入Stage_manager::COMMIT_STAGE阶段

|-change_stage() ← 该阶段会受到binlog_order_commits参数限制

|-process_commit_stage_queue() ← 会遍厉所有线程,然后调用如下存储引擎接口

| |-ha_commit_low()

| |-ht->commit() ← 调用存储引擎handlerton->commit()

| | ← ### 注意,实际调用如下的两个函数

| |-binlog_commit()

| |-innobase_commit()

|-process_after_commit_stage_queue() ← 提交之后的后续处理,例如semisync

| |-RUN_HOOK() ← 调用transaction->after_commit

|

|-stage_manager.signal_done() ← 通知其它线程事务已经提交

|

|-finish_commit()

在 enroll_for() 函数中,刚添加的线程如果是队列的第一个线程,就将其设置为 leader 线程;否则就是 follower 线程,此时线程会睡眠,直到被 leader 唤醒 (m_cond_done) 。

注意,binlog_max_flush_queue_time 参数已经取消。

commit stage

如上所述,commit 阶段会受到参数 binlog_order_commits 的影响,当该参数关闭时,会直接释放 LOCK_sync ,各个 session 自行进入 InnoDB commit 阶段,这样不会保证 binlog 和事务 commit 的顺序一致。

当然,如果你不关注两者的一致性,那么可以关闭这个选项来稍微提高点性能;当打开了上述的参数,才会进入 commit stage 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/563819.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

like语句太慢 sqlserver_SQLServer找出执行慢的SQL语句

SELECT(total_elapsed_time / execution_count)/1000 N平均时间ms,total_elapsed_time/1000 N总花费时间ms,total_worker_time/1000 N所用的CPU总时间ms,total_physical_reads N物理读取总次数,total_logical_reads/execution_count N每次逻辑读次数,total_logical_reads N逻辑…

苹果cms10自适应模板_哪里有苹果cms10自适应模板?

1,苹果CMSv10大图轮播高端大气自适应视频网站模板源码苹果cms10自适应模板下载地址:https://www.mytheme.cn/maccms/54.html第一款大图宽屏的海报轮播幻灯样式,宽屏模板支持DIY扩展自适应影视模板苹果cms10自适应模板苹果cms10自适应模板2&am…

python实现进程通信_python进程间的通讯实现

1:进程间通讯的方法:apply_async()非阻塞式通讯 apply()阻塞式通讯2:使用Queue实现对Process创建的进程间通讯,Queue本身是一个消息队列程序,Queue常用方法:Queue.qsize():返回当前消息队列的消息数量Q…

vscode php formatter mac配置_Mac上配置Vs code时,遇到的几个“坑”!

在写一些简单的程序时,你喜欢用什么编译器呢?之前我一直用的是sublime,它打开的速度快,占用的内存小。但是有个麻烦的地方,配置的时候要花一些时间,如果你的网不好还时不时给你来个timeout。最近在Youtube上…

模为2的逆元是什么_两种求模m逆元的方法

在a|b(a能整除b)的前提下&#xff0c;计算(b/a)mod m的时候转化为 计算(b*x)mod m ; 这时的x就是a的逆元(a模m的逆元)&#xff1b;此时x满足 (a*x mod m 1)&#xff1b; 这个x的求法有一下两种&#xff1a;1)扩展欧几里得算法求解 a*xm*y1; 因为 a*x mod m 1 <> a…

java值栈_Struts2 中的值栈是什么?

7.1值栈7.1.1值栈是什么简单的说&#xff1a;值栈是对应每一个请求对象的轻量级的内存数据中心。Struts2中一个很激动人心的特性就是引入了值栈&#xff0c;在这里统一管理着数据&#xff0c;供Action、Result、Interceptor等Struts2的其他部分使用&#xff0c;这样一来&#x…

java try的用法_Java中try、catch的使用方法

Java中有两种处理异常的方式&#xff0c;分别是用throws抛出异常、用try、catch捕获异常。try-catch在Javatry-catch语句的语法格式&#xff1a;try{//代码块}catch(Exception1 e){//抛出异常后所要进行的操作}当try语句当中捕获到异常时&#xff0c;会将异常抛出到catch语句中…

java not a jpeg file_javax.imageio.IIOException: Not a JPEG file: starts with 0x47 0x49

java处理图片时出现异常javax.imageio.IIOException: Not a JPEG file: starts with 0x47 0x49at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(Unknown Source)at com.sun…

java if用法_java中if语句的写法

if语句if 语句的语法如下&#xff1a;if(布尔表达式){//如果布尔表达式为true将执行的语句}如果布尔表达式的值为 true&#xff0c;则执行 if 语句中的代码块&#xff0c;否则执行 if 语句块后面的代码。免费视频教程推荐&#xff1a;java视频教程if...else语句if 语句后面可以…

java子类和父类实例_java中父类与子类之间的转换示例

java中父类与子类之间的转换示例有以下三点&#xff1a;示例一父类强制转子类pre class"brush:php;toolbar:false">Father f new Son();Son s (Son)f;//可以创建一个父类的实例&#xff0c;想要强制把父类对象转换成子类的&#xff0c;不行&#xff01;通俗的想…

java 原子类能做什么_死磕 java原子类之终结篇(面试题)

概览原子操作是指不会被线程调度机制打断的操作&#xff0c;这种操作一旦开始&#xff0c;就一直运行到结束&#xff0c;中间不会有任何线程上下文切换。原子操作可以是一个步骤&#xff0c;也可以是多个操作步骤&#xff0c;但是其顺序不可以被打乱&#xff0c;也不可以被切割…

java对docker_如何在docker中运行java程序

吃鸡游戏创建一个redis docker容器首先&#xff0c;我们先为redis创建一个DockerfileFROM ubuntu:12.10RUN apt-get updateRUN apt-get -y install redis-serverEXPOSE 6379ENTRYPOINT ["/usr/bin/redis-server"]现在你需要通过Dockerfile创建一个镜像&#xff0c;将…

java canvas 画图片_canvas画布——画八卦图

浏览器支持Internet Explorer 9、Firefox、Opera、Chrome 以及 Safari 支持 arc() 方法。注释&#xff1a;Internet Explorer 8 或更早的浏览器不支持 元素。定义和用法arc() 方法创建弧/曲线(用于创建圆或部分圆)。提示&#xff1a;如需通过 arc() 来创建圆&#xff0c;请把起…

java多线程编程—高级主题_Java day20 高级编程【第一章】Java多线程编程

【第一章】Java多线程编程一.进程与线程多个时间段会有多个程序依次执行&#xff0c;但是同一时间点只有一个进程执行线程是在进程基础之上划分的更小的程序单元 &#xff0c;线程是在进程基础上创建并且使用的&#xff0c;所以线程依赖于进程的支持&#xff0c;但是来讲&#…

java object数组转实体类_详解Java中String JSONObject JSONArray List实体类转换

JSON使用阿里的fastJson为依赖包gradle依赖管理如下&#xff1a;compile group: "com.alibaba", name: "fastjson", version:"1.2.41"1、String转JSONObject前言&#xff1a;String 是JSONObject格式的字符串eg:JSONObject jSONObject JSONObje…

e x泰勒 java_maven project

最近没事了玩一下maven&#xff0c;使用maven管理工程中的依赖包非常的方便。建立maven web工程的时候开始不知道怎么用tomcat来调试&#xff0c;总是使用mave的tomcat插件发布了后来调试&#xff0c;觉得非常的麻烦&#xff0c;网上找了点材料&#xff0c;看看maven web工程不…

java定义说话方法_类定义的基本形式_Java语言程

类定义的基本形式_Java语言程4&#xff0e;2&#xff0e;1 类定义的基本形式前面一节我们已经了解了类和对象的概念&#xff0c;然而在实际编程过程中&#xff0c;该如何定义一个类以及类中的对象呢&#xff1f;在Java语言中&#xff0c;用户自己可以定义一个类&#xff0c;作…

java创建线程几种_java中创建线程有几种方式

详细内容线程的创建方式1、继承Thread类实现多线程2、覆写Runnable()接口实现多线程&#xff0c;而后同样覆写run()。推荐此方式3、使用Callable和Future创建线程相关视频教程推荐&#xff1a;java学习视频实例如下&#xff1a;1、继承Thread类实现多线程/** 继承Thread类创建线…

php自定义中文分词方法,php实现的中文分词类完整实例

本文实例讲述了php实现的中文分词类。分享给大家供大家参考&#xff0c;具体如下&#xff1a;该中文分词类源码使用http://tools.ddpool.cn/code/jb51_php_format进行了格式化处理&#xff0c;便于阅读。具体代码如下&#xff1a;class Segmentation {var $options array(lowe…

手机电脑自适应导航源码php,自适应各终端懒人网址导航源码 v2.0

自适应各终端懒人网址导航源码。V2.0版本是在原1.8版本的基础上修复和增加了些功能。推荐直接使用新版本&#xff0c;舍弃旧版本&#xff0c;后期会继续不定期更新。测试环境&#xff1a;宝塔Nginx -Tengine2.2.3的php5.5mysql先导入数据库文件db/db.sql再修改config.php数据库…