作者介绍
去哪儿网 MySQL DBA 。2012年加入去哪儿,负责支付、酒店业务线数据库运维和 db 巡检系统的开发与运维工作。
业务线培训负责人,优秀培训讲师。
一、前言
PXC 全称 Percona XtraDB Cluster 是 Percona 公司针对 MySQL 提出的高可用性和可扩展性解决方案。
它拥有很多优点:同步复制,数据强一致;多主复制,可以在任意节点进行写操作;节点自动配置等。
但同时,使用中也有着一些限制,需要业务及运维人员了解、关注。
本文介绍了在PXC集群日常使用中出现的一次故障,希望能够给大家带来启发。
二、故障现象
在同一组服务器上的集群A(3307端口)和集群B(3308端口)写节点连接数在同一时间段暴增,SQL running 线程堆积,对业务产生了一定影响。
MySQL thread监控图
三、分析过程
最初的着眼点是业务新发布和两个集群交叉的业务变更。经排查,故障开始前并没有新发布,也没有跑任何程序任务,因此否定了这种可能性。
集群结构简图
两个集群都属于PXC架构,共用一组服务器。在查看第三个节点db3的监控情况时(这台是作为两个集群共同的读节点),发现了端倪:
db3 节点的 wsrep_flow_control_sent 指标上升,说明发送了流控(FC)消息。
Galera流控监控图
PXC数据同步流程
在介绍流控之前,我们需要先搞清一个问题:PXC是如何做数据同步的?
写节点执行事务;
每次产生对一行数据修改时,调用galera_append_key ,将这个行所在的库名表名、关键字段名集合起来成为写集的key;
提交时,调用 galera_append_data,将事务对应的 binlog 加入写集,与上面的 key 对应起来形成一个(key,value)对;
调用 galera_pre_commit 将当前事务写集加入队列,并串行发送;
后续等待 GTID 、验证写集是否冲突等;
各节点根据本地验证结果进行写集提交或 discard 。
流控原理
Flow control allows any node in the cluster to instruct the group when it needs replication to pause and when it is ready for replication to continue. This prevents any node in the synchronous replication group from getting too far behind the others in applying replication.
节点接收写集并把它们按照全局顺序组织起来,并将接收到的未应用和提交的事务保存在接收队列中。
如果当前节点无法及时处理队列中的写集,会导致队列越来越长;当这个接收队列长度达到我们配置的阈值,将触发流控。
触发流控时,写节点调用 galera_pre_commit 接口广播时(上述第 4 步)会进行阻塞,卡住所有写集,以便让执行慢的读节点尽快处理队列中堆积的写集内容。
当接收队列减小到一个我们配置的阈值以下时,读节点发送continue消息,复制将恢复。
其实就像水龙头往池子里灌水,池子水快满了就关上,池子水流走了水位线下去了再打开。
所以,流控的目的实际上是为了保证集群节点的一致性,而不像主从异步复制一样出现大量的延迟。
流控参数变量与状态值
gcs.fc_limit
表示的是接收队列达到多大时,触发 FC(单位是事务个数,与事务大小无关)。
gcs.fc_factor
表示接收队列在多大时,FC 会解除。度量值为 gcs.fc_limit*gcs.gc_factor ;如果小于这个长度,则 FC 解除。
wsrep_local_recv_queue
表示当前节点从其他节点接受的队列中事务个数。如果这个值达到 gcs.fc_limit 值的话,就会发生 flow control ,本节点会向整个集群发送flow control消息,整个集群会被阻塞。当该值小于 gcs.fc_limit*gcs.fc_factor 之后,flow control 解除。
wsrep_flow_control_paused_ns
流控发生时,复制同步暂停的时间。
wsrep_flow_control_paused
该状态值发生变化,含义为从上一次 SHOW GLOBAL STATUS 命令开始,流控占全体同步数据时间的百分比(初始值0.0),理想情况下应该趋近于0.0;当它比较大的时候(超过0.6)我们需要及时采取措施,缓解流控。
wsrep_flow_control_recv
本地节点收到的集群流控事件信息数量,由于流控信息每个节点都会收到,该状态值不适合用来定位问题节点。
wsrep_flow_control_sent
本地节点发送给集群的流控事件信息数量,可以用来确认哪个节点导致了流控产生。
流控产生的原因?如何解决?
第 1、2 类原因,我们需要关注监控和报警,及时处理服务器硬件故障和系统性能问题。
第 3 类原因,我们可以根据 wsrep_cert_deps_distance 的值调整读节点的参数 wsrep_slave_threads ;相当于提高读节点的并发度来缓解。
第 4 类原因,我们可以通过DB规范限制和日常巡检去规避和发现问题,解决问题。
现在回到我们当前的故障,查看 db3 系统监控,发现故障期间该服务器的 IO 使用率达到了 100% 。
系统磁盘IO监控图
到此,故障的直接原因我们找到了:读节点磁盘 IO 升高引起了流控降低了写节点的队列发送速度,造成了线程堆积。
那么故障的根本原因是什么呢?是什么引起了服务器 IO 飙高呢?
查看监控图可以看到,故障时间点 db3 服务器有明显的磁盘使用率变化(剩余空间先下降再上升)。
系统磁盘空间监控图
这种变化最容易想到的就是改表操作,使用 pt-online-schema-change 工具创建临时表,挪数据后再 rename 、删除旧表。
但改表的操作 3 个节点都会同步执行,会有相似的图形变化,从监控上看这个明显不是。
其他有可能的原因就是产生系统临时表了,这种临时表属于 MySQL 自动创建,使用完成后自动删除。
一般最常见的就是排序临时表,循着这条线索,查看了目标服务器的慢查询日志。
慢查询日志内容:
发现集群B确实有数个并发的排序慢查询,执行时间700余秒,向前推算和故障开始时间吻合。
需要注意的是,一般的排序操作都是在内存中完成的,产生磁盘临时表的原因:
排序结果集超过内存排序参数 tmp_table_size 阈值(我们系统默认256M )
查询字段中包含 text 等大字段类型
MySQL排序判断流程:
当排序数据量不超过 sort buffer 容量时,MySQL 将会在内存使用快速排序算法进行排序(内部排序);
当排序数据量超过 sort buffer 容量时,MySQL 将会借助临时文件使用归并排序算法进行排序(外部排序);
当需要借助临时表的时候,MySQL 会优先使用内存临时表(此时表引擎为 memory引擎),回内存临时表取数据并不涉及随机读和扫描行,所以会使用 rowId 排序方式;
当内存临时表大小超过 tmp_table_size 限制时,则需要将内存临时表转换为磁盘临时表,此时回表会有随机读,所以会选择全字段排序方式。
如上所述,查询字段是否可以使用内存排序成为了关键的性能点。而 TEXT 、 BLOB 等类型不被 memory 存储引擎支持,所以不能使用内存临时表。
Instances of BLOB or TEXT columns in the result of a query that is processed using a temporary table causes the server to use a table on disk rather than in memory because the MEMORY storage engine does not support those data types (see Section 8.4.4, “Internal Temporary Table Use in MySQL”). Use of disk incurs a performance penalty, so include BLOB or TEXT columns in the query result only if they are really needed. For example, avoid using SELECT *, which selects all columns.
慢查询表字段定义:
`command_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`command_content` varchar(2048) NOT NULL DEFAULT '',
`outer_feedback_result` mediumtext,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
四、总结
集群B的数据库中,两个表的连接查询存在排序,查询字段存在无法使用内存表的mediumtext类型,导致排序被挪到磁盘进行。
我们在数据库使用中要对带大字段的 SQL 保持警惕,应避免在线上执行可能造成重大影响的 SQL ,将其放到统计离线库执行。
在使用 PXC 架构时要注意单个节点性能对写角色节点和整个服务器上集群的连带影响。
对不同业务类型的 DB 最好能够隔离资源,避免互相影响。
参考文献:
《MySQL运维内参》作者:周彦伟、王竹峰、强昌金
《Galera Flow Control in Percona XtraDB Cluster for MySQL》by Jay Janssen
《MySQL 8.0 Reference Manual》11.3.4 The BLOB and TEXT Types 官方文档
招募贴:
END