查看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逻辑…

mysql maxpreparedstmtcount_NodeJs Mysql Cant't create more than max_prepared_stmt_count statements

这阵子碰到一个数据库上的问题,一个刚上线不到一周的 NodeJs 接口服务里所有的查询全部都挂掉了,接口一直处于 pending 状态,看了下 pm2 的日志发现了报错:Cantt create more than max_prepared_stmt_count statements&#xff0c…

MySQL查询语句转postGRE语句_PostgreSQL DBA常用SQL查询语句

查看帮助命令DB# help --总的帮助DB# \h --SQL commands级的帮助DB# \? --psql commands级的帮助按列显示,类似MySQL的\GDB# \xExpanded display is on.查看DB安装目录(最好root用户执行)find / -name initdb查看有多少DB实例在运行(最好root用户执行)find / -name…

苹果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…

yum mysql 版本低_mysql小版本升级(yum方式)

5.7.21 --> 5.7.231. 数据库备份(略)2. mysql配置文件备份cp /etc/my.cnf /etc/my.cnf.bak3. yum配置mysql源3.1安装mysql57-community-release-el6-8.noarch.rpm之类的东西rpm -ivh mysql57-community-release-el6-8.noarch.rpm3.2 查看mysql的repovim /etc/yum.repos…

mysql definer设置为root_mysql如何修改所有的definer

mysql中的definer是什么,有什么作用?我们在mysql创建view、trigger、function、procedure、event时都会定义一个Definer‘xxx’,类似如下:CREATEALGORITHMUNDEFINEDDEFINER root% SQL SECURITY DEFINERVIEW v_ questions ASSELECTq.idASid,q.…

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

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

java 创建mysql表_MySQL 创建数据表

MySQL 创建数据表创建MySQL数据表需要以下信息:表名表字段名定义每个表字段语法以下为创建MySQL数据表的SQL通用语法:CREATE TABLE table_name (column_name column_type);以下例子中我们将在 W3CSCHOOL 数据库中创建数据表w3cschool_tbl:tut…

python内置对象是什么_#【Python】【基础知识】【内置对象常用方法】

数字的常用方法:>>> dir(int)[‘__abs__‘, ‘__add__‘, ‘__and__‘, ‘__bool__‘, ‘__ceil__‘, ‘__class__‘, ‘__delattr__‘, ‘__dir__‘, ‘__divmod__‘, ‘__doc__‘, ‘__eq__‘, ‘__float__‘, ‘__floor__‘, ‘__floordiv__‘, ‘__for…

模为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 android上传文件_Java-Android-如何将txt文件上传到网站?

我想将txt文件上传到网站,我承认我没有对其进行任何详细的研究,但是我看了一些示例,并希望就我是否朝着正确的方向提出更多的经验性意见. .这是我到目前为止的内容&#xff1a;DefaultHttpClient httpClient new DefaultHttpClient();HttpContext localContext new BasicHttp…

java授查 非授查异常_java检查异常与非检查异常

Java的可检测异常和非检测异常泾渭分明。可检测异常经编译器验证&#xff0c;对于声明抛出异常的任何方法&#xff0c;编译器将强制执行处理或声明规则。非检测异常不遵循处理或声明规则。在产生此类异常时&#xff0c;不一定非要采取任何适当操作&#xff0c;编译器不会检查是…

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;随着Java SE 7的发布&#xff0c;二进制符号标准开箱即用。语法是非常直接和明显的&#xff0c;如果你有一个良好的理解二进制&#xff1a;byte fourTimesThree 0b1100;byte data 0b0000110011;short number 0b111111111111111;int overflow 0b10101010101…

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

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