RDS AliSQL 面向 Binlog 的性能优化大揭密(上)—— 极致 IO 优化

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: RDS MySQL使用AliSQL内核,为用户提供了MySQL所有的功能,同时提供了企业级的安全、备份、恢复、监控、性能优化、只读实例、Serverless等高级特性

image.png

RDS AliSQL简介

AliSQL是阿里云RDS团队深度优化的独立 MySQL 分支,除了社区版的所有功能外,AliSQL提供了类似于MySQL企业版的诸多功能,如企业级备份恢复、审计日志、线程池、Binlog in Redo 等。RDS MySQL使用AliSQL内核,为用户提供了MySQL所有的功能,同时提供了企业级的安全、备份、恢复、监控、性能优化、只读实例、Serverless等高级特性。


Binlog 是谓何物

Binlog (Binary log)是 MySQL Server 层维护的一种二进制日志,以事务级别记录了对数据库的所有修改操作。


事务级别是指:一个事务的日志,是在事务提交时被写入 binlog 文件中的。具体来说,事务在执行过程中,会不断的生成 binlog events,暂存在 session 级别的 binlog cache 中;事务提交时,会一次将 binlog cache 中所有内容写到 binlog 文件中。


Binlog 的一致性保证

在 MySQL 中,binlog 是为数不多可以做到 “准” 的日志(另一个 “准” 的是 redo log),即保证日志中存在的修改,数据中一定存在,反之亦然。也正因为它可以做到 “准”,MySQL 才可以基于 binlog 做复制和备份。


Sync_binlog

Sync_binlog 是 binlog 相关的一个重要参数,它控制了 binlog 的刷盘方式。在了解这个参数的具体含义前,先要了解一下 Linux 系统的 page cache 机制。


Page Cache

我们知道计算机的存储介质笼统上说分三层,其读写速度从高到低分别是 cpu cache,memory 和磁盘。Linux 内核为了优化文件的读写速度,在磁盘之前加入了一层缓存,叫做 page cache。


Page cache 本质上是由操作系统直接管理的一块内存空间,应用程序写文件时,会先写到 page cache 上,然后操作系统择机进行刷盘。当然,这样的机制会带来的一个隐患,即当机器 crash 时,没有从 page cache 刷到磁盘的数据会丢失。很多应用程序不能容忍这样的数据丢失,因此操作系统也为应用程序提供了主动刷盘的接口。


Flush 和 Sync 的含义

在 binlog 中,写 binlog events 到 binlog 文件的 page cache 的行为被称为 flush,binlog 文件刷盘的行为被称为 sync。需注意,这个叫法是 binlog 语境中的习惯,redo log 中的叫法又有不同。


参数含义

Sync_binlog 参数控制了 binlog 刷盘的频率,当配置为 0 时,binlog 不主动进行刷盘;当配置为 n (n > 0) 时,binlog 每 n 个事务一起刷盘。


当 sync_binlog 设置为 1 时,每个事务提交时,都会主动 binlog 刷盘,这种配置下,不会出现已提交的事务的 binlog 丢失的现象,基于 binlog 的复制和备份都能保证可靠性。配合 innodb 中的参数innodb_flush_log_at_trx_commit = 1(每个事务 redo log 都主动刷盘),可以达到数据和日志完全一致的高可靠性,这种配置俗称双一,下文讨论过程中,我们都默认实例处于双一配置。


两阶段提交

在双一的配置下,MySQL 使用两阶段机制,保证 Binlog 与 innodb 的崩溃一致性(crash safe)。也就是说,无论实例在什么时间 crash,重启后通过 crash recovery,Binlog 与 innodb 都可以达到一致性状态。

image.png

上图是一个两阶段提交的示意图,所谓两阶段,就是将事务提交分为 prepare 和 commit 两个阶段,将事务分为了 active 和 prepared 两种状态。


Prepare 阶段

Prepare 阶段最主要的动作是将事务从 active 状态置为 prepared 状态,并将 undo 回滚段也置为 prepared。这个动作的意义是,标记事务所有操作都已经结束,在 crash recovery 过程中,处在 prepared 状态的事务是可以回滚也可以提交的(active 状态事务只能回滚不能提交)。有了这个状态,redo log 和 binlog 才能在 commit 阶段和 crash recovery 过程中相互协调。需注意,这个阶段修改事务状态的动作是内存态的,只有记录 undo 回滚段的 redo log 刷盘,事务状态才算被持久化到磁盘。这是由于 innodb 的 WAL 机制导致的,有兴趣的同学可以去查阅相关资料学习。


在早期的 MySQL 上,prepare 阶段还会进行 redo log 的刷盘操作。但随着 binlog group commit 优化的诞生,刷盘操作的性能有了巨大提升,因此 redo log 的刷盘操作被移到了 commit 阶段的 binlog group commit 流程中以优化 IO 性能。这部分细节将在 binlog group commit 优化的章节中详细讲解。值得一提的是,这个改动和 AliSQL 的早期成员印风有很大关联,他在性能测试中首先发现了这个问题,并将 idea 和 code patch 一并贡献给了 MySQL 官方。对这段历史感兴趣的可以参照 http://bugs.mysql.com/bug.php?id=73202


Commit 阶段

Commit 阶段除了事务提交的操作外,还包括 redo log 的刷盘,binlog 的写入和刷盘。这里对这两个日志文件的操作顺序很重要。Binlog 刷盘,就意味着事务已经可以被传给从库,此时就算实例没有完成 commit 就 crash 了,crash recovery 过程中也需要能够提交此事务,这样才不会造成主从不一致。而事务在 crash recovery 过程中事务可提交,就需要事务的 prepared 状态已经被持久化到磁盘,也就是 redo log 已经刷盘。因此必须先将 redo log 刷盘,才可以写 binlog 文件并刷盘。因此,在提交阶段,以 binlog 刷盘完成为标志,刷盘完成前发生 crash,事务回滚,刷盘完成后发生 crash,事务提交。


在完成 binlog 刷盘后,事务就可以提交。提交时会把事务从 prepared 状态改为 commit 状态,这个操作是需要写 redo log 的,但是事务完成提交并不需要等待这个 redo log 刷盘。刚才说过,只要 binlog 刷盘完成,就可以认为这个事务已经持久化的提交了,因此不必再等待一次 redo log 刷盘,直接返回事务提交成功即可。


各时间点 Crash 分析

当实例发生 crash 时(我们这里假设一种最严重的 crash——机器故障导致重启,所有page cache全丢)


  • 如果 redo log 刷盘没有完成,事务的 prepared 状态还没有刷盘,还是 active 的状态,直接回滚即可。
  • 如果 redo log 刷盘完成,binlog 刷盘没有完成,重启时事务是 prepared 状态,在启动阶段这样的事务又叫未决事务,可以选择提交或回滚。通过扫描 binlog,发现事务没有完整的存在于 binlog 中,因此选择回滚;
  • 如果 binlog 完成刷盘,重启时事务是 prepared 状态,binlog 中也完整的记录了这个事务,必须选择提交。


IO 性能问题

上述机制中,为了保证 binlog 的完整和 crash safe,每个事务在提交时,都需要等待两次 IO 操作,一次 redo log,一次 binlog。这导致了极大的性能问题。

image.png

为了优化性能,MySQL 官方引入了 Binlog Group Commit 优化来合并 IO 操作。


Binlog Group Commit 优化

BGC (Binlog Group Commit)优化是 MySQL 官方做的,针对 binlog 提交阶段的一个性能优化,目的是合并 IO 操作。


合并 IO 操作

在文件系统中,IO 操作是成批进行的,在一般情况下,10 次 1KB 的 IO 要比 1 次 10KB 的 IO 慢得多,这就是合并 IO 操作的意义。这个思想在 MySQL server 和 innodb 的众多设计中都有体现。


Binlog Group Commit 流程

image.png

Group Commit 顾名思义,就是把若干事务组成一个组,一起提交。MySQL 将 Binlog 提交阶段分成 flush stage,sync stage 和 commit stage。


Flush stage

image.png

Flush stage 中包含 sync redo 和 flush binlog。进入提交阶段的事务,首先会等待进入 flush stage。为什么会等待呢?具体来说,正在 flush stage 中进行 sync redo 和 flush binlog 的 group,会持有一把 lock_log 锁,这样就阻塞住了新来的事务,它们需要等待当前 group 结束释放 lock_log 锁,才能进入 flush stage。在等待的过程中,系统可以积攒很多等待进入 flush stage 的事务,把他们全部组成一个 group。Group 中的第一个事务作为 leader,flush stage 中所有的操作都由 leader 的线程完成。这样一来,原先多个事务进行多次 sync redo 的操作,就变成了一次 sync redo,最大限度合并了 IO 操作。


MySQL-80 上 redo log 引入了无锁化设计后,sync redo 的动作变为后台线程完成 。Flush stage 中主动进行 sync redo 的操作就变为了等待后台线程完成 sync redo。这种设计下,group commit 也非常有意义,因为等待后台线程是需要拿锁的,很多事务独立等待时,会造成严重的锁冲突。在这种情况下 group commit 实质上是合并了锁等待,避免了锁冲突。


Sync stage

Sync stage 中包含 sync binlog 的操作。完成 flush stage 的 flush group,在进入 sync stage 前还需要再次组队,由几个 flush group 组成一个 sync group,进一步合并 IO。

image.png

Sync stage 组队的机制和 flush stage 类似,由正在 sync binlog 的 sync group 持有一把 lock_sync 锁,准备进入 sync stage 的 flush group 在等待这把锁的过程中积攒,组成一个 sync group。当上一组 sync binlog 完成 lock_sync 被释放,由本组第一个 flush group 的 leader 作为新组的 leader,完成本组的 sync stage。


Commit stage

Commit stage 中包含事务的提交操作。Commit stage的目的并非合并 IO,而是为了让 binlog 中事务的顺序和事务真实提交的顺序一致。

在开启了 binlog_order_commits 或者 clone 过程中需要依赖提交顺序时,完成 sync stage 的事务会再次进入 commit stage;没有开始顺序提交的实例,commit stage会被跳过,每个事务自己进行事务提交。


Binlog in Redo--极致 IO 优化

BGC 优化通过分组的方式,可以将很多小的 IO 操作合并,但是事务提交需要等待的 IO 次数(sync redo 和 sync binlog 两次 IO)并没有改变。为了进一步优化 IO,AliSQL 引入了 Binlog in redo 优化,旨在将事务提交阶段的两次 IO 操作合并为一次 IO 操作。


而具体如何实施优化,以及如何验证优化效果,会在后续文章中进行讲述。


Binlog in redo 架构

image.png

前章曾提到过 Innodb 的 WAL 机制,innodb 写任何数据前,都需要先写下数据的 redo log,随后提交时,只需要同步等待 redo log 刷盘,数据后续异步的刷盘即可。即便出现了 crash,只要 redo log 完整,数据也可被准确无误的恢复。


Binlog in redo 顾名思义,将 binlog 写到 redo log 中。在这个架构下,binlog 对于 redo log 来说也是一份“数据”,受到 redo log 的保护;binlog 的刷盘由异步线程完成,事务提交过程中,无需等待 binlog 刷盘。如果发生 crash,根据 redo log 恢复binlog 文件即可。 这样一来,提交阶段需要等待的 IO 次数就变为了 1 次,能够大幅提升性能。


Crash Recovery 机制

image.png

Binlog in redo 架构中,当实例 crash 后,binlog 中的内容有可能丢失,因此需要在重启后的 recovery 过程中补齐。Server 层中实现了一个 Binlog applier 去补齐 binlog。在 crash recovery 期间 apply redo 时,每次在 redo log 中读到 binlog event,就解析他的 binlog position,如果这个位置已经存在于 binlog文件中,就不需要回填,如果这个位置不在了,就需要回填一下。


性能优化效果

测试环境:32Core, 64G Ram, ESSD存储。

测试工具:sysbench


oltp_update_non_index

image.png

image.png

oltp_insert

image.png

image.png

oltp_write_only

image.png

image.png

结论

Binlog In Redo 功能在不损失可靠性的前提下,减少了 1 次IO。在不超过 256 并发的情况 下,Binlog In Redo 功能对性能的提升和延迟的降低都非常显著。但是对于大并发的场景,性能提升效果有限。这是由于大并发下,BGC 合并 IO 的作用更加明显,Binlog 提交期间的瓶颈来到了串行化的 Flush binlog。为了优化这一瓶颈,AliSQL 引入了 Binlog Parallel Flush 优化,最终使实例达到全业务场景的大幅性能提升。


欲知后事如何,请听下回分解。



作者简介


武根泽(子堪) RDS MySQL内核团队核心研发,擅长 MySQL 内核代码研发,性能调优和技术问题探索,在 MySQL Binlog,Innodb 等领域多有涉猎。关于RDS MySQL内核方面的问题,欢迎致电: vogts.wangt@alibaba-inc.com 咨询。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6天前
|
SQL 关系型数据库 MySQL
MySQL慢查询优化、索引优化、以及表等优化详解
本文详细介绍了MySQL优化方案,包括索引优化、SQL慢查询优化和数据库表优化,帮助提升数据库性能。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
MySQL慢查询优化、索引优化、以及表等优化详解
|
11天前
|
缓存 监控 关系型数据库
如何优化MySQL查询速度?
如何优化MySQL查询速度?【10月更文挑战第31天】
36 3
|
13天前
|
缓存 关系型数据库 MySQL
如何优化 MySQL 数据库的性能?
【10月更文挑战第28天】
37 1
|
21天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:百万级数据统计优化实践
【10月更文挑战第21天】 在处理大规模数据集时,传统的单体数据库解决方案往往力不从心。MySQL和Redis的组合提供了一种高效的解决方案,通过将数据库操作与高速缓存相结合,可以显著提升数据处理的性能。本文将分享一次实际的优化案例,探讨如何利用MySQL和Redis共同实现百万级数据统计的优化。
54 9
|
15天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
76 1
|
21天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
48 5
|
23天前
|
人工智能 Cloud Native Java
云原生技术深度解析:从IO优化到AI处理
【10月更文挑战第24天】在当今数字化时代,云计算已经成为企业IT架构的核心。云原生作为云计算的最新演进形态,旨在通过一系列先进的技术和实践,帮助企业构建高效、弹性、可观测的应用系统。本文将从IO优化、key问题解决、多线程意义以及AI处理等多个维度,深入探讨云原生技术的内涵与外延,并结合Java和AI技术给出相应的示例。
81 1
|
26天前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
45 1
|
26天前
|
缓存 关系型数据库 MySQL
一文彻底弄懂MySQL优化之深度分页
【10月更文挑战第24天】本文深入探讨了 MySQL 深度分页的原理、常见问题及优化策略。首先解释了深度分页的概念及其带来的性能和资源问题。接着介绍了基于偏移量(OFFSET)和限制(LIMIT)以及基于游标的分页方法,并分析了它们的优缺点。最后,提出了多种优化策略,包括合理创建索引、优化查询语句和使用数据缓存,帮助提升分页查询的性能和系统稳定性。
|
16天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第26天】数据库作为现代应用系统的核心组件,其性能优化至关重要。本文主要探讨MySQL的索引策略与查询性能调优。通过合理创建索引(如B-Tree、复合索引)和优化查询语句(如使用EXPLAIN、优化分页查询),可以显著提升数据库的响应速度和稳定性。实践中还需定期审查慢查询日志,持续优化性能。
47 0

相关产品

  • 云数据库 RDS MySQL 版
  • 云数据库 RDS