MYSQL中binlog优化的一些思考汇总
作者:丁凯 发布时间:2024-01-23 01:58:25
问题
问题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则等待其任务完成即可。
一旦某阶段的链表任务执行完成,这些任务会进入下一个阶段,同样加入该阶段的任务链表,重复上述执行流。
如此设计有以下几点好处:
使用Leader执行而非每个线程各自执行可有效减少write/fsync等调用次数,提高效率
可保证事务写binlog和引擎层提交的顺序一致
多事务可并发执行,而不再需要被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一定可以被落盘。
来源:https://zhuanlan.zhihu.com/p/147459036


猜你喜欢
- python爬虫要用到的库:请求库:实现 HTTP 请求操作urllib:一系列用于操作URL的功能。requests:基于 urllib
- 图像的二值化或阈值化(Binarization)旨在提取图像中的目标物体,将背景以及噪声区分开来。通常会设定一个阈值T,通过T将图像的像素划
- 代码如下:arr = array(12,52,14,43,24,58,15,64,24,57,17,56,45)&nbs
- '****'函数名称: strReplace(Str)'函数功能: 过滤单引号'参数说明: Str 
- 参数说明以官方说明为例,gather()函数需要三个参数,输入input,维度dim,以及索引indexinput必须为Tensor类型di
- 信号和槽机制是 QT 的核心机制,要精通 QT 编程就必须对信号和槽有所了解。信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核
- Observer模式也叫观察者模式,是由GoF提出的23种软件设计模式的一种。Observer模式是行为模式之一,它的作用是当一个对象的状态
- 随着网络技术的不断发展,网络应用已经渗透到人类社会的各个角落。作为网络世界的支撑点的网站,更是人们关注的热点:政府利用网站宣传自己的施政纲领
- 引题:该题源自一个网友的求助,作为水群龙王的我义不容辞的接下了这道题目,先来看题目:拿到这道题,题目的意思已经很清楚了,列表里含有2的元素都
- 这篇文章主要介绍了Python列表切片常用操作实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 首先要知道,修改滚动条样式,利用伪元素-webkit-scrollbar。注意, ::-webkit-scrollbar仅仅支持WebKit
- 爬取了下小猪短租的网站出租房信息但是输出的时候是这种:百度了下。python2.7在window上的编码确实是个坑解决如下如果是个字典的话要
- 需求背景业务表tb_image部分数据如下所示,其中id唯一,image_no不唯一。image_no表示每个文件的编号,每个文件在业务系统
- 在计算机科学中,二进制是一种由 0 和 1 组成的数字系统。Python 中提供了一种易于使用的方式来将二进制字符串转换为整数,并支持多种进
- 遵循Web标准的思想,网页要表现出一种亲和力。那么,针对残障用户来说,其“阅读”器可不能读取图像上传递的信息的。所以我们会采用一种Using
- 这个javascript农历日历,万年历代码网上看到的,很不错,功能齐全,值得收藏!功能介绍:动态显示当前世界各国各时区时间,显示当前农历,
- 本文实例讲述了ThinkPHP中url隐藏入口文件后接收alipay传值的方法。分享给大家供大家参考。具体方法如下:现在公司项目的需求变化多
- 说明1、PCA是最经典、最实用的降维技术,尤其在辅助图形识别中表现突出。2、用来减少数据集的维度,同时保持数据集中对方差贡献最大的特征。保持
- 本文实例分析了python的私有属性和方法。分享给大家供大家参考。具体实现方法如下:python默认的成员函数和成员变量都是公开的,并且没有
- scikit-learn是python的第三方机器学习库,里面集成了大量机器学习的常用方法。例如:贝叶斯,svm,knn等。scikit-l