MySQL 数据库适用场景广泛, 相较于 Oracle,DB2 性价比更高, web 网站, 日志系统, 数据仓库等场景都有 MySQL 用武之地, 但是也存在对于事务性支持不太好(MySQL 5.5 版本开始默认引擎才是 InnoDB 事务型), 存在多个分支, 读写效率瓶颈等问题.
所以如何用好 MySQL 变得至关重要, 一方面需要通过 MySQL 优化找出系统读写瓶颈, 提高数据库性能; 另一方面需要合理涉及数据结构, 调整参数, 以提高用户操作响应; 同时还有尽可能节省系统资源, 以便系统可以提供更大负荷的服务. 本文将为大家介绍腾讯云团队是如何对 MySQL 进行内核级优化的思路和经验.
早期的 CDB 主要基于开源的 Oracle MySQL 分支, 侧重于优化运维和运营的 OSS 系统. 在腾讯云, 因为用户数的不断增加, 对 CDB for MySQL 提出越来越高的要求, 腾讯云 CDB 团队针对用户的需求和业界发展的技术趋势, 对 CDB for MySQL 分支进行深度的定制优化. 优化重点围绕内核性能, 内核功能和外围 OSS 系统三个维度展开, 具体的做法如下:
一. 内核性能的优化
由于腾讯云上的 DB 基本都需要跨园区灾备的特性, 因此 CDB for MySQL 的优化主要针对主从 DB 部署在跨园区网络拓扑的前提下, 重点去解决真实部署环境下的性能难题. 经过分析和调研, 我们将优化的思路归纳为:"消除冗余 I/O, 缩短 I/O 路径和避免大锁竞争". 以下是内核性能的部分案例:
1. 主备 DB 间的复制优化
问题分析
如上图所示, 在原生 MySQL 的复制架构中, Master 侧通过 Dump 线程不断发送 Binlog 事件给 Slave 的 I/O 线程, Slave 的 I/O 线程在接受到 Binlog 事件后, 有两个主要的动作:
写入到 Relay Log 中, 这个过程会和 Slave SQL 线程争抢保护 Relay Log 的锁.
更新复制元数据(包含 Master 的位置等信息).
优化方法
经过分析, 我们的优化策略是:
Slave I/O 线程和 Slave SQL 线程是典型的单写单读生产者 - 消费者模型, 是可以做到无锁设计的; 因此实现思路就是 Slave I/O 线程在每次写完数据后, 原子更新 Relay Log 的长度信息, Slave SQL 线程读取 Relay Log 的时以长度信息为边界. 这样就将原本竞争激烈的 Relay Log 锁化解为无锁;
由于 Binlog 事件中的 GTID(Global Transaction Identifier)和 DB 事务是一一对应的关系, 所以 Relay Log 中的数据本身已经包含了所需要的复制元数据, 所以我们可以不写 Master info 文件, 消除了冗余的文件 I/O;
于 DB 都是以事务为更新粒度的, 因为在 Relay Log 文件 I/O 上, 我们通过合并离散小 I/O 为事务粒度的大 I/O 等手段, 使磁盘 I/O 得以大幅提升.
优化效果
如上图所示, 经过优化: 左图 35.79% 的锁竞争 (futex) 已经被完全消除; 同压测压力下, 56.15% 的文件 I/O 开销被优化到 19.16%,Slave I/O 线程被优化为预期的 I/O 密集型线程.
2. 主库事务线程和 Dump 线程间的优化
问题分析
如上图所示, 在原生 MySQL 中多个事务提交线程 TrxN 和多个 Dump 线程之间会同时竞争 Binlog 文件资源的保护锁, 多个事务提交线程对 Binlog 执行写入, 多个 Dump 线程从 Binlog 文件读取数据并发送给 Slave. 所有的线程之间是串行执行的!
优化方法
经过分析, 我们的优化策略是:
将读写分离开来, 多个写入的线程还是在锁保护下串行执行, 每一个写入线程写入完成后更新当前 Binlog 的长度信息, 多个 Dump 线程以 Binlog 文件的长度信息为读取边界, 多个 Dump 线程之间并行执行. 以这种方式来让复制拓扑中的 Dump 线程发送得更快!
效果
经过测试, 优化后的内核, 不仅提升了事务提交线程的性能, 在 Dump 线程较多的情况下, 对主从复制性能有较大提升.
二. 主备库交互流程优化
问题分析
如上图所示, 在原生 MySQL 中主备库之间的数据发送和 ACK 回应是简单的串行执行, 在上一个事件 ACK 回应到达之前, 不允许继续发送下一个事件; 这个行为在跨园区 (RTT 2-3ms) 的情况性能非常差, 而且也不能很好地利用带宽优势.
优化方法
经过分析, 我们的优化策略是:
将发送和 ACK 回应的接收独立到不同的线程中, 由于发送和接收都是基于 TCP 流的传输, 所以时序性是有保障的; 这样发送线程可以在未收 ACK 之前继续发送, 接受线程收到 ACK 后唤醒等待的线程执行相应的任务.
效果
根据实际用例测试, 优化后的 TPS 提升为 15% 左右.
三. 内核功能的优化
1. 预留运维帐号连接数配额
在腾讯云上, 不时遇到用户 App 异常或者 BUG 从而占满 DB 的最大连接限制, 这是 CDB OSS 帐号无法登录以进行紧急的运维操作. 针对这个现状, 我们在 MySQL 内核单独开辟了一个可配置的连接数配额, 即便在上述场景下, 运维帐号仍然可以连接到 DB 进行紧急的运维操作. 极大地降低了异常情况下 DB 无政府状态的风险. 该帐号仅有数据库运维管理权限, 无法获取用户数据, 也保证了用户数据的安全性.
2. 主备强同步
针对一些应用对数据的一致性要求非常高, CDB 在 MySQL 原生半同步的基础上进行了深度优化, 确保一个事务在主库上提交之前一定已经复制到至少一个备库上. 确保主库宕机时数据的一致性.
四. 外围系统的优化
除了以上提到的 MySQL 内核侧的部分优化, 我们也在外围 OSS 平台进行了多处优化. 例如使用异步 MySQL ping 协议实现大量实例的监控, 通过分布式技术来加固原有系统的 HA / 服务发现和自动扩容等功能, 在数据安全 / 故障切换和快速恢复方面也进行了多处优化.
来源: https://segmentfault.com/a/1190000017505060