MYSQL中binlog优化的一些思考汇总

问题

问题1:如何解决事务提交时flush redo log带来的性能损失

WAL是实现事务持久性(D)的一个常用技术,基本原理是将事务的修改记录redo log。redo log顺序追加写入。事务提交时,只需要保证事务的redo log落盘即可,通过redo log的顺序写代替页面的随机写提升数据库系统的性能。但是,该方案必须要求每个事务提交时都将其生成的redo log进行一次刷盘,效率不高。

问题2:binlog和引擎层事务提交的顺序问题

对于单个事务而言,日志写入顺序是先redo log再binlog,只要维持该顺序即可维持正确性。但对于一个高并发的数据库系统而言,每时每刻可能都会存在众多并发执行的事务。我们还需要通过一定的手段来维护Server层binlog和引擎层事务提交的顺序一致性。

维护这种顺序一致性其实是为了保证备份工具Xtrabackup的正确性。

当 binlog 作为协调者,如果其中记录的事务顺序和存储引擎层记录的顺序不一样的话,备份工具(Innodb Hot Backup)拿到备份集的位点可能会存在空洞。因为备份工具会拷贝 redo 日志,在 redo 的头部会记录最后一个提交的事务对应的 binlog 位点,备份集建立之后就会根据这个位点继续从主库 dump binlog。

假如有三个事务 T1,T2,T3 已经 fsync 到 binlog 文件中,三个事务的在文件中的位点分别是 100,200,300,但是在引擎层的只有 T1 和 T3 完成了 commit 并记录到 redo 中,最后一个 commit 的事务 T3 位点是 300。此时通过备份工具拿到的数据就是这样的状态,备份集启动的时候会走崩溃恢复的流程,prepare 事务被回滚(备份集不会备份 binlog 文件,对应上个小节 xid 集合为空),自位点 300 继续从主库同步binlog并apply,导致 T2 在备库就丢失了。

因此,我们必须设计一种机制来保证Server层的binlog写入顺序和存储引擎层的事务提交顺序保持一致。

问题3:同时写redo和binlog带来的性能下降

问题1中提到每次的事务提交会带来性能问题,而这个问题在引入binlog后会变得更加严重。每个事务提交都会增加一次文件IO,且需要刷盘。如果系统并发比较高,那么这些IO将会成为拖慢整体性能的瓶颈。

解决方案

问题1:Redo log组提交技术

redo组提交技术思想很简单:通过将多个事务redo log的刷盘动作合并,减少刷盘次数。Innodb的日志系统里面,每条redo log都有一个LSN(Log Sequence Number)。事务将日志拷贝到redo log buffer时,都会获取当前最大的LSN,且LSN单调递增,因此可以保证不同事务的LSN不会重复。那么假设三个事务Trx1、Trx2、Trx3的日志的最大LSN分别为LSN1、LSN2、LSN3(LSN1 < LSN2 < LSN3),它们同时进行提交,那么如果trx3率先执行提交,它会要求刷盘至LSN3处,这样就顺便将Trx1、Trx2的redo log也刷了,Trx1和Trx2会判断自己的LSN小于当前已落盘的最大LSN,就无需再次刷盘。

问题2:内部XA事务

开启binlog情况下,引入内部XA事务来协调上层和存储引擎层,具体来说,在事务提交时引入两个阶段:

prepare:将redo log刷盘操作以确保data页和undo页的更新已经刷新到磁盘,设置事务状态为PREPARE状态;

commit:1). 写binlog并刷盘,2).调用引擎层事务提交接口。将事务状态设置为COMMIT。

如此两阶段提交主要是要保证数据库崩溃时的正确性。因为一旦binlog落盘了,它就可能被下游节点消费。这种事务必须在重启后被commit而非rollback。而对于binlog未落盘的事务,崩溃恢复时直接回滚。

具体来说,故障恢复时,扫描最后一个binlog文件(在flush阶段,如果binlog大小超过阀值,进行rotate binlog文件,会保证该文件记录的最后一个事务一定被提交),提取其中的xid。重做检查点以后的redo日志,读取事务的undo段信息,搜集处于prepare阶段的事务列表,将事务的xid与binlog中记录的xid对比,若存在,则提交,否则就回滚。

MySQL5.6以前,为了保证数据库binlog的写入顺序和InnoDB层的事务提交顺序一致,MySQL数据库内部使用了prepare_commit_mutex锁。

具体来说,在两阶段提交引擎层 prepare 的时候加锁,在引擎层 commit 之后释放锁:

innobase_xa_prepare()
write() and fsync() binary log
innobase_commit()

这样确实可以保证 binlog 和 innodb 的事务顺序一致,但是这把锁会导致所有的事务串行化执行,且每次提交都会至少调用多次fsync,效率很低。这也是接下来需要探讨并解决的一个问题。

问题4

参考redo log优化技术,引入组提交技术来优化binlog的写入性能。

考虑未优化时事务提交流程:

prepare:该阶段刷存储引擎层(innodb)的redo log并将事务状态设置为PREPARED(更新undo page上事务状态),该阶段不涉及binlog
commit:写binlog日志并刷盘,同时引擎层释放锁,释放回滚段、设置事务状态为COMMITTED等
所谓的组提交技术其本质上是将耗时的commit步骤进行更细粒度的拆分,具体来说:

将步骤2的commit 分为三个阶段:

Flush:写binlog,但不sync
Sync: 调用 fsync 操作将文件落盘
Commit :调用存储引擎接口提交事务

这里的fsync是耗时操作,因此我们希望能攒足够多的写入后才进行一次fsync调用,在这里使用batch技术。其原理是:上述步骤中的每个阶段都有一个对应的任务链表,每个进入该阶段的线程会将自己的任务加入至该链表中,链表加锁以保证正确性。第一个加入该链表的线程会成为Leader,后续的线程成为Follower。链表中的所有任务组成一个Batch,由Leader负责执行,而Follower则等待其任务完成即可。

一旦某阶段的链表任务执行完成,这些任务会进入下一个阶段,同样加入该阶段的任务链表,重复上述执行流。

如此设计有以下几点好处:

  1. 使用Leader执行而非每个线程各自执行可有效减少write/fsync等调用次数,提高效率
  2. 可保证事务写binlog和引擎层提交的顺序一致
  3. 多事务可并发执行,而不再需要被prepare_commit_mutex锁强制串行化

除此之外,MYSQL还对prepare阶段刷redo log进行了进一步优化。原来的设计是多事务可并发地刷redo log,同样效率不够高。可以将prepare阶段的redo log刷盘放在commit阶段的Flush阶段执行。但有个小问题需要说明的是:优化前每个线程各自负责自己的redo log的落盘,且知道需要flush的redo log的lsn,如果改为在Flush阶段由其Leader线程统一落盘,此时它不了解每个线程的redo log的lsn,因此它简单粗暴地flush至log_sys的最大lsn,这就保证了要提交事务的redo log一定可以被落盘。

总结

到此这篇关于MYSQL中binlog优化思考的文章就介绍到这了,更多相关MYSQL binlog优化思考内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL中的binlog相关命令和恢复技巧

    操作命令: 复制代码 代码如下: show binlog events in 'mysql-bin.000016' limit 10; reset master 删除所有的二进制日志flush logs  产生一个新的binlog日志文件 show master logs; 或者 show binary logs; 查看二进制文件列表和文件大小 复制代码 代码如下: ./mysqlbinlog --start-datetime="2012-05-21 15:30:00" --stop-

  • MySQL数据库恢复(使用mysqlbinlog命令)

    1:开启binlog日志记录 修改mysql配置文件mysql.ini,在[mysqld]节点下添加 复制代码 代码如下: # log-bin log-bin = E:/log/logbin.log 路径中不要包含中文和空格.重启mysql服务.通过命令行停止和启动mysql服务 复制代码 代码如下: c:\>net stop mysql; c:\>net start mysql; 进入命令行进入mysql并查看二进制日志是否已经启动 Sql代码 复制代码 代码如下: mysql>sho

  • Mysql Binlog快速遍历搜索记录及binlog数据查看的方法

    目标,开发人员说有个数据莫名其妙添加了,但是不知道是从哪里添加的,而且应用功能里面不应该添加这样的数据,为了查清楚来源,所以我就准备去binlog里面找了,但是binlog有好几个月的数,我这样一个个mysqlbinlog下去,也不是办法,所以想到准备用脚本循环来操作. 1,去binlog目录复制所有的binlog到临时目录/tmp/bl/ cp /home/data/mysql/binlog/mysql-bin.* /tmp/bl 2,写脚本遍历 [root@wgq_idc_dbm_3_61

  • mysql开启binlog步骤讲解

    binlog是二进制日志文件,用于记录mysql的数据变更,数据在恢复的时候binlog日志能起到很大的作用.mysql的主从复制就是利用的binlog原理 1.登录mysql之后使用下面的命令查看是否开启binlog show variables like 'log_%'; 2.编辑配置文件 vi /etc/my.cnf 3.加入以下内容 server_id=2 log_bin = mysql-bin binlog_format = ROW expire_logs_days = 30 4.重启

  • Mysql数据库之Binlog日志使用总结(必看篇)

    binlog二进制日志对于mysql数据库的重要性有多大,在此就不多说了.下面根据本人的日常操作经历,并结合网上参考资料,对binlog日志使用做一梳理: 一.binlog日志介绍 1)什么是binlog binlog日志用于记录所有更新了数据或者已经潜在更新了数据(例如,没有匹配任何行的一个DELETE)的所有语句.语句以"事件"的形式保存,它描述数据更改. 2)binlog作用 因为有了数据更新的binlog,所以可以用于实时备份,与master/slave主从复制结合. 3)和b

  • mysql 正确清理binlog日志的两种方法

    mysq 正确清理binlog日志 前言: MySQL中的binlog日志记录了数据库中数据的变动,便于对数据的基于时间点和基于位置的恢复,但是binlog也会日渐增大,占用很大的磁盘空间,因此,要对binlog使用正确安全的方法清理掉一部分没用的日志. [方法一]手动清理binlog 清理前的准备: ① 查看主库和从库正在使用的binlog是哪个文件 show master status\G show slave status\G ② 在删除binlog日志之前,首先对binlog日志备份,以

  • Mysql Binlog数据查看的方法详解

    binlog介绍 binlog,即二进制日志,它记录了数据库上的所有改变. 改变数据库的SQL语句执行结束时,将在binlog的末尾写入一条记录,同时通知语句解析器,语句执行完毕. binlog格式 基于语句,无法保证所有语句都在从库执行成功,比如update ... limit 1; 基于行,将每一次改动记为binlog中的一行.在执行一个特别复杂的update或者delete操作时,基于行的格式会有优势. 登录到mysql查看binlog 只查看第一个binlog文件的内容 show bin

  • MySQL 自动清理binlog日志的方法

    说明: 开启MySQL binlog日志的服务器,如果不设置自动清理日志,默认binlog日志一直保留着,时间一长,服务器磁盘空间被binlog日志占满,导致MySQL数据库出错. 使用下面方法可以安全清理binlog日志 一.没有主从同步的情况下清理日志 mysql -uroot -p123456 -e 'PURGE MASTER LOGS BEFORE DATE_SUB( NOW( ),INTERVAL 5 DAY)'; #mysql 定时清理5天前的binlog mysql -u root

  • mysql对binlog的处理说明

    然而这里不打算对某种存储引擎的实现细节进行描述,也不打算介绍各种存储引擎的优缺点,只是描述一下mysql如何处理binlog,并澄清几个容易混淆的问题. Binlog对mysql而言是重要的,主要体现在它的功能上.Mysql官方文档明确指出,binlog的启动大概会为mysql增加1%的负载,因此在绝大多数情况下,binlog都不会成为mysql的性能瓶颈. Binlog是mysql以二进制形式打印的日志,它默认不加密,不压缩.每个正常的binlog文件头部,有4个字节的标记,值为0xfe 0x

  • Mysql Data目录和 Binlog 目录 搬迁的方法

    如果全过程使用的是Mysql用户,应该可以正常启动. 如果用的ROOT用户,可能不能正常启动,原因是新建的目录权限不对. 可能会这样的错误提示: /usr/local/mysql/libexec/mysqld: File '/home/mysql/mysqllog/binlog/mysql-bin.index' not found (Errcode: 2) 1. stop mysql service 一定要先停止,非常重要. # /etc/init.d/mysqld stop 2. 修改Mysq

随机推荐