05 深入专题:复制与高可用

05 深入专题:复制与高可用

这一篇聚焦 MySQL 复制与高可用:主从/GTID 复制原理、半同步、并行复制、MGR、主从延迟与数据一致性校验、故障切换与脑裂处理。内容整理自爱可生开源社区《大智小技》系列(2019/2020 两册)技术文章精选。

bug#89370 中半同步 ACK 接收线程为什么会导致 slave_io_thread 停滞?

5.7 中将接收 ACK 从复制线程拆出,由半同步插件 ACK 接收线程单独处理。该线程在 while(1) 循环里 mysql_mutex_lock(&m_mutex)→select→unlock,基本时刻占有互斥锁。当启动另一 slave 时,master 新复制线程需抢该锁才能启动,但长时间抢不到,导致复制线程启动不了,slave 的 slave_io_thread 停滞。加 sched_yield() 可缓解,官方已修复。

配置半同步到多从库时,部分从库长时间无复制数据但状态正常,这是什么 bug?

这是 MySQL bug#89370(影响 5.7.16/17/21)。复现:rpl_semi_sync_master_wait_for_slave_count=2 时正常;改为 1 后重启其中一个 slave,该 slave 长达数分钟无 master 复制数据流入,但复制状态全正常(Slave_IO_Running/SQL_Running 均为 Yes)。根因在 ACK 接收线程与复制线程抢占互斥锁产生竞争。

group_replication_consistency 各取值的适用场景分别是什么?

EVENTUAL(默认,最终一致,不等待回放,快但可能旧数据);BEFORE(本地强一致,读前等先序事务回放完,适合偶尔读一致数据/敏感操作);AFTER(全局强一致,等所有节点回放完才返回,适合写少读多/只读集群);BEFORE_AND_AFTER(两者兼顾);BEFORE_ON_PRIMARY_FAILOVER(切换时阻塞连到新主的事务直到先序回放完,保证切换读到最新)。缺点:对性能影响大,尤其网络不稳时。

为什么 MGR 也会产生读写不一致,MySQL 8.0.14 引入了什么机制解决?

MGR 在 relay log 前增加了冲突检查协调,但 binlog 回放仍可能延时(类似半同步 IO 线程回放延迟,大事务尤甚),所以 MGR 不是全同步。8.0.14 引入’读写一致性’特性,参数 group_replication_consistency(5 个值:EVENTUAL/BEFORE/AFTER/BEFORE_AND_AFTER/BEFORE_ON_PRIMARY_FAILOVER),可在 SESSION/GLOBAL 级别设置,解决读非写节点数据过期问题。

5.6.29/5.7.11 之前,mixed 格式下创建含 sysdate() 的 event 为什么导致复制中断?

在 mixed 模式下,CREATE EVENT 语句内同一事务既产生 DDL statement,又因 sysdate() 是非复制安全函数被转成 row 事件(写 mysql.event 表)。主库正常但某些版本从库应用 event 时因 row 事件与 statement 混合导致复制中断。官方在 5.6.29 修复。规避:升级到 5.6.29+,或改用 row 模式(sysdate 自动转 row 不中断)。

MGR 中出现相同 GTID 对应不同事务(导致数据不一致、从节点脱离集群),根因是什么 bug?

关联 bug#92690。在 Paxos 协商中,prepare 阶段的提案标识 ballot 由’数值编号+节点编号’组成。某从节点因网络没收到主节点的 learn_op,自行发起新 prepare(value=no_op, ballot=1.1,节点编号大于主)。新一轮 prepare 中数值编号被初始化为 0,ballot 大小完全由节点编号决定,从节点选了较大 ballot 的 no_op 提案,跳过了主实例的事务。主实例仍提交原事务(GTID=…:57305280),从节点该 GTID 被 no_op 占用,后续新 GTID 同步覆盖了该 GTID,造成同 GTID 不同事务。

MGR 相同 GTID 故障在哪个 MySQL 版本修复?修复时需如何人工处理?

官方反馈社区版 5.7.26 和 8.0.16 修复,企业版可申请 hotfix。该故障会让 no_op 占用本应属于真实事务的 GTID,导致被踢节点丢失该事务数据。未升级前若发生,需人工检查切换时 binlog 中 GTID 信息与新主节点对应 GTID 是否一致;不一致要人工补齐(如重做该节点、从新主重建或从备份恢复并校正 GTID 集合),一致后才能将被踢出的原主安全加回集群。5.7.26 之前社区版 MGR 用户务必注意避坑。

row 模式下 delete/update 大表在 slave 回放极慢(超 10 小时),根因是什么?

根因是无主键、只有非唯一索引,导致回放时按 BI 逐行查找匹配记录效率极低。slave 在 row 模式重放依赖 Rows_log_event,delete/update 含查找操作;无主键时默认 slave_rows_search_algorithms=TABLE_SCAN,INDEX_SCAN,遍历每行事件再用 BI 查对应记录。文中 50 万记录 delete 在 TABLE_SCAN 下超 11145s 未完成。

slave_net_timeout 与 MASTER_HEARTBEAT_PERIOD 的关系及修复方法?

5.7.7+ 默认 slave_net_timeout=60s(之前 3600s),修改需重启主从生效。未指定 MASTER_HEARTBEAT_PERIOD 时默认为 slave_net_timeout/2,但 change master 时定下后不随参数变更(需重新 change master)。修复:设 slave_net_timeout 为心跳周期的 2 倍并重启主从。源码上 IO 线程用 slave_net_timeout 设连接超时;change master 用 min(SLAVE_MAX_HEARTBEAT_PERIOD, slave_net_timeout/2) 设心跳;Binlog_sender 用心跳超时等待发送心跳 Event。

sysdate() 和 now() 在复制中有什么差异?从库 event_scheduler 默认状态是什么?

now() 返回语句/事务开始时间,主从一致;sysdate() 返回函数实际执行时间,主从有差异(STARTS 时间不同)。mixed 下遇非安全函数转 row,statement/row 模式不会中断。另:主从环境下创建 event_scheduler,slave 默认禁用状态 SLAVESIDE_DISABLED,主从切换后需考虑原主从 event_scheduler 状态,避免 new slave 意外写入数据。

从库产生大量很小的 relay log(堆积 2600+),根因是什么?

三个条件同时满足:①MASTER_HEARTBEAT_PERIOD > 从库 slave_net_timeout;②主库压力小、持续超过 slave_net_timeout 时间无新 Event;③之前主从有一定延迟。主库心跳 Event 发送给从库 IO 线程前,IO 线程已因超时而断开并重连,每次重连生成新 relay log;因有延迟不能清理,堆积大量小 relay log。主库 error log 出现 ‘zombie dump thread’ 日志。

多从库时 rpl_semi_sync_master_wait_for_slave_count=1,第二个半同步从库启动后 slave_io_thread 停滞,如何定位根因?

通过 performance_schema.threads 看主库 dump 线程(Binlog Dump GTID),发现新 dump 线程一直 starting;error log 出现 ‘found a zombie dump thread with the same UUID’。用 gstack 看线程栈:新 dump 线程和旧 dump 线程都在等 Ack_receiver 的锁,而 Ack_receiver 线程持锁等 select。gstack/pstack 是定位此类线程竞争的有效手段(同 bug#89370)。

如何优化无主键大表的 slave 回放性能?

设置 slave_rows_search_algorithms=‘INDEX_SCAN,HASH_SCAN’。HASH SCAN 先把 binlog 事件记录 hash 放入 hash 表,再对表每行 hash 对比匹配回放;有非唯一索引时 HASH over index(Hi) 更快。文中测试 INDEX_SCAN,HASH_SCAN 约 2000s 完成,远优于 TABLE_SCAN 的 >11145s。注意 HASH_SCAN 有内存开销需保障内存;根本解决是表加主键、避免无 where 的 delete 大表(改用 truncate)。

MySQL 5.7 基于组提交的并行复制(LOGICAL_CLOCK)如何工作?

设 slave_parallel_workers>0 且 slave_parallel_type=‘LOGICAL_CLOCK’(默认是 DATABASE)。主库 ordered_commit 第二阶段给同一批 commit 的 binlog 打相同 last_committed 标签,同一 last_committed 的事务在备库可并行执行,互不干扰;后一批需等前一批执行完。相比 5.6 的 DATABASE 模式(按库分发,单库无提升甚至回退),LOGICAL_CLOCK 打破同 DB 不能并行的限制,大幅提升回放速度。

MySQL 多源复制(Multi-Source)适合哪些业务场景?

三类:①备份多台 Server 数据到一台(垂直切分,如业务 A/B/C/D 汇总备份);②聚合前端多 Server 的分片数据(水平切分,如按年份拆分,配 DBLE 等中间件);③汇总并合并多 Server 数据(各 Server 表结构不同字段,用 Event 定时 insert ignore … natural join 合并到目标表 A)。5.7 引入,用 FOR CHANNEL ’name’ 区分各源。

WRITESET 历史 MAP 与 binlog_transaction_dependency_history_size 的作用是什么?

Writeset 历史 MAP(Writeset_history,map<hash,seq_number>)保存近期修改行的快照。事务的 Writeset 与其比对,无冲突则 last commit 降到 m_writeset_history_start(最早 seq),有冲突取最大冲突 seq。binlog_transaction_dependency_history_size 默认 25000,是 MAP 元素上限(约等于 行数×(1+唯一键数)/2);越大 last commit 越精确、并发越高,但内存消耗越大,MAP 满则清空重记。

WRITESET 并行复制中 Writeset 由什么构成?没有主键会怎样?

Writeset 是 hash 值集合,元素来自行数据的主键和唯一键(每种索引生成二进制/字符串两种格式的 hash,如 PRIMARY/test/jj10/值)。函数 add_pke 只处理唯一索引(HA_NOSAME);若表无主键也无唯一键,会 set_has_missing_keys,则不修改 last commit,回退到 ORDER_COMMIT。因此无主键可用唯一键,都没有则 WRITESET 不生效。

infobin 分析 binlog 时,binlog_row_image=FULL 下如何估算大事务行数?

在 FULL 模式下:Insert/Delete 只有 before 或 after image,约 100 字节/行+10 字节开销≈110 字节/行;Update 含 before+after image,约 220 字节/行。若定义大事务为 100M,则 Insert/Delete 约修改 100W 行,Update 约 50W 行。文中用 bigtrxsize(字节)和 bigtrxtime(秒)两个阈值分别识别大事务与长期未提交事务。

relay_log_info_repository 和 sync_relay_log_info 应如何配置以保证崩溃一致性?

运维规范要求 relay_log_info_repository=TABLE。5.6.10 GA 后该表默认 InnoDB,每回放一个事务在同一事务里更新,保证 sql thread 位置与数据一致(此时 sync_relay_log_info 默认 10000 失效)。配合 innodb_flush_log_at_trx_commit=1。注意:io thread 持久化靠 master_info_repository=TABLE+sync_master_info=1,但刷盘单位是 event 写放大严重,通常 sync_master_info 用默认 10000,故 io 位置用 relay_log_recovery 机制保证。

relay_log_recovery 的作用是什么?GTID 与非 GTID 环境下如何保障 crash 后一致?

relay_log_recovery 在 mysqld 启动时生成新 relay log,把 sql thread 位置重置到已回放点(Relay_Log_Pos=4),io thread 位置初始化为 slave_relay_log_info 中的 Master 位点(丢弃旧 io 位置,从已回放处重新拉 binlog),并防 relay log 损坏。结论:开启 GTID+master_auto_position+relay_log_recovery=1,即使 repository=file 也能保证一致(重复事务因 GTID 存在被跳过);未开 GTID 则必须 relay_log_info_repository=table 且 relay_log_recovery=1。

slave_relay_log_info 表与 show slave status 的位点信息有什么关系?

slave_relay_log_info 存 slave sql thread 工作位置(持久化),show slave status 输出的是内存中状态,二者可能不同。stop slave 或正常关闭 mysqld 会把内存态持久化到表;start slave 生效的是内存态。同一事务在从库 relay log 的 position 与主库 binlog 的 position 不相等,该表用 Master_log_name/Master_log_pos 记录其在主库 binlog 中的对应位置。io thread 位置则存于 slave_master_info。

主从复制延迟常见场景有哪些及对应解决办法?

①无主键/无索引/索引区分度低:show slave status 中 position 不变、某表 in_use=1;解决是备库加索引或设 slave_rows_search_algorithms 含 HASH_SCAN;②主库大事务:事前沟通异步写入、事中调 innodb_flush_log_at_trx_commit/sync_binlog 或开并行复制;③主库写入频繁从库追不上:升级硬件、用 @丁奇 relay fetch 预热、用基于行的并行复制;④大量 MyISAM 表备份时 FTWRL 阻塞 SQL 线程:改 InnoDB。

基于 WRITESET 的并行复制相比 COMMIT_ORDER 有什么优势,如何开启?

COMMIT_ORDER 只有压力大有组提交时才并行度高;WRITESET 在主库串行执行的事务在从库也能并行(尽可能降低 last_commit)。需主库设 transaction_write_set_extraction=XXHASH64 和 binlog_transaction_dependency_tracking=WRITESET(均 5.7.22 引入)。其通过扫描 Writeset(行主键/唯一键的 hash 值)与历史 MAP 比对冲突,降低 last commit,粒度从’组’细化到’行’。

基于 Xtrabackup export + 可传输表空间实现多源恢复的关键步骤有哪些?

①源端 sysbench 造数+压力;②innobackupex –databases=sbtest 备份单库;③mysqldump –no-data 备表结构;④用 concat 拼批量 DISCARD/IMPORT TABLESPACE SQL;⑤目标端对备份做 innobackupex –apply-log –export(生成 .cfg/.exp);⑥目标端建库、导入表结构、DISCARD 表空间、拷 .ibd+.cfg 并 chown、IMPORT TABLESPACE;⑦CHANGE MASTER … FOR CHANNEL ‘xxx’ 建多源复制,建议 ANALYZE TABLE 更新统计信息。

如何用 binlog Event 分析’长期未提交的事务’和大事务(infobin 工具思路)?

infobin 工具思路:①长期未提交事务:用 XID_EVENT 时间(commit 发起)减 QUERY_EVENT 时间(首条 DML 发起)得耗时,超过 bigtrxtime 即标记;②大事务:累加 GTID_LOG_EVENT 与 XID_EVENT 间所有 Event 大小,超过 bigtrxsize 标记(20M 较合适,100M≈更新 100W 行/Update 50W 行);③按 piece 分片统计 Event 生成速度;④扫 MAP_EVENT+table id 统计每表 DML 量。

用 Xtrabackup 做多源复制初始化时,为什么需要配合可传输表空间(Transportable Tablespaces)?

多源复制用物理备份初始化时,常规方式只有第一个通道可覆盖还原,后续通道需逻辑还原。Xtrabackup 的 –export 参数对 InnoDB 表做 export 转换,生成 .cfg 配置文件,且备份元数据记录 binlog/GTID 同步点。单纯可传输表空间无法记录事务点(不知从哪个 binlog 位点同步),结合 xtrabackup 的 export 即可获知同步位点,从而高效完成多库汇聚的初始导入。

mysqlbinlog 解析大 binlog 时没有 –progress 参数,如何查看解析进度?

mysqlbinlog 未提供进度参数。可查看其文件句柄读取进度来估算:先用 ps 找到 mysqlbinlog 进程 pid,解析时用 ls -l /proc//fd 找到它读取 binlog 的句柄(如 fd 3),该句柄显示的偏移量除以 binlog 总大小即得整体进度百分比。例如 1.1G 的 binlog 解析时句柄读到约 600M,则进度约 54%。也可用 lsof/pv 等手段观测同一文件偏移,便于在恢复场景估算剩余时间。

MGR 集群中一个节点异常退出后,Primary 节点会做什么?

通过 Wireshark(MGR 协议)抓包可见:Primary 节点很快向其他存活节点发送三类信息:①view_msg(视图,显示节点1/2在线、节点3离线);②remove_node_type(通知删除离线节点3);③一秒后新的 view_msg(只有两个节点)。即 Primary 迅速更新全员某节点离线,将其踢出集群并通知全员。可用公式 mgr.app_data.body.cargo_type 在 Wireshark 增加信息类型列观察。

如何快速从 binlog 中找出最大的几个事务(判断是否大事务)?

利用 GTID 模式下每个事务开头必有 GTID_event,用 mysqlbinlog 解析后 grep “GTIDlast_committed” -B1 取前一行 ‘at xxx’(事务在 binlog 中的字节位置),awk 计算相邻事务位置差即得每个事务大小,sort -n -r | head 取最大。命令形如:mysqlbinlog … | grep “GTID$(printf ‘\t’)last_committed” -B1 | grep -E ‘^# at’ | awk ‘{print $3}’ | awk ‘NR==1{tmp=$1} NR>1{print $1-tmp;tmp=$1}’ | sort -n -r | head -n 10。

MGR 某节点网络不稳,消息缓存会被撑满导致节点被踢吗?

不会无限撑满。消息缓存受 group_replication_message_cache_size 上限约束,写满后淘汰最旧条目。文中实验:将故障节点网络断开,其他节点查询状态显示该节点被’质疑’但未踢出;重置内存统计观察缓存释放量超过缓存大小,说明缓存内容已完整换过一轮。恢复通讯后,故障节点所需消息可能已从缓存淘汰、无法接续,于是报错退出集群,随后经 auto-rejoin(默认重试3次)尝试重新加入并通过 binlog 接续数据。

MGR 的 group_replication_member_expel_timeout 和 group_replication_message_cache_size 如何权衡?

member_expel_timeout:节点意外离线达 (5秒 + 该值) 后才被踢出(默认 0,即 5 秒即踢);值越大越能容忍瞬时抖动、自动恢复而无需人工干预,但被质疑期间其他节点读到该节点过期数据的概率也越大。message_cache_size:缓存越多,故障节点恢复后凭缓存自动接续的概率越大(默认约 1GB),但更耗内存。结论是在’网络不稳定容忍度 / 自动化程度 / 读到过期数据概率 / 物理资源消耗’四者间平衡配置。

三节点 MGR 能否把某个高延迟节点(如地球另一端)放进去而不影响整体性能?

不能。实验:关闭流控后给 mgr-3 加网络延迟,sysbench 整体性能下降直到取消延迟。原理:MGR 用 multi-paxos 且需多数派(3 节点中至少 2 个)确认,每个节点轮流’坐庄’发起协商,非庄家节点发起事务需转交庄家。当轮到高延迟节点坐庄或它处于确认路径时,其他节点的提交都要等它的往返时延,延迟越高大家等越久,拖累整体。所以跨地域单节点放进同组会显著拉低吞吐,远距离站点建议用异步复制而非 MGR。

一主多从半同步架构中,master 提交性能慢,如何判断是哪个 slave 拖慢了?

半同步插件没有现成视图查看各 slave 谁拖慢,可通过调试日志定位:将主库 rpl_semi_sync_master_trace_level 设为 16(或 32 更详细),查看 master error log,里面会打印每次事务等待 ACK 的明细,扫一眼大部分半同步阻塞最后收到的 ack 都来自哪个 server_id 的 slave,即为拖慢者(文中 slave2 的 server_id=300)。原理是主库需等 rpl_semi_sync_master_wait_for_slave_count 个 ACK 才提交,最慢的那个决定提交延迟。调试日志量大,定位后记得把 trace level 调回 0。

MySQL 8.0.19 的 DNS SRV 支持解决了 InnoDB Cluster 什么部署问题?

之前 MySQL Router 建议与应用端绑定部署避免单点;否则需在 router 前加 VIP 或负载均衡。有了 DNS SRV(遵循 RFC 2782,支持 Priority/Weight),Connector 8.0.19 多语言(Connector/J、Python、Node.js、C++、ODBC、NET)支持 DNS SRV,配合 consul 等服务发现,router 不必与应用绑定,也省了 VIP/负载均衡,更易适配 service mesh。客户端须连优先级最低可达地址,同优先级权重越大访问概率越高。

应用如何使用 MySQL Connector 的 DNS SRV 连接 InnoDB Cluster?

用 consul 注册 router 服务(consul services register -name router -port 6446),本机设 DNS 转发(如 dnsmasq 将 consul 请求转 8600 端口)。Connector 连接时 host 填 consul 注册的服务地址(如 router.service.consul),加 dns_srv=True 参数,不指定端口。例:mysql.connector.connect(user=…,host=‘router.service.consul’,dns_srv=True)。从 router 日志可见请求以负载均衡方式分流。

MySQL 8.0.22 的 Async Replication Auto failover 是什么,适用什么架构?

用于一条异步复制通道配置多个复制源,当某源不可用(宕机/链路断)且 IO 线程按 master_retry_count×master_connect_retry 重连无效后,按权重自动选新源继续同步。典型用于两地三中心:同城双中心(百十公里,RPO=0)部署 MGR,异地容灾(上百公里,RPO>0)通过异步复制。当同城 MGR 主节点切换,异地节点能自动跟随新主。注意它只在同一 channel 的源间切换,原主恢复不会自动回切,除非当前链路再次故障,需结合监控系统做后续处理。

如何配置 Async Replication Auto failover?关键参数/函数有哪些?

①change master 时加 source_connection_auto_failover=1,并设 master_retry_count/master_connect_retry(决定重连多久才切换);②用函数 asynchronous_connection_failover_add_source(channel,host,port,ns,weight) 注册多个源(权重大的优先,可配合 MGR 选举权重);delete_source 删除。查是否启用:SELECT CHANNEL_NAME,SOURCE_CONNECTION_AUTO_FAILOVER FROM replication_connection_configuration。kill MGR primary 后从节点几轮重连失败自动切到次权重源。

InnoDB ReplicaSet 主节点故障后如何恢复?MySQL Router 如何感知切换?

手工 kill Primary 后副本集无法自动转移,需人工 rs.forcePrimaryInstance(‘新节点’) 强制提升,恢复后状态显示 AVAILABLE_PARTIAL(部分可用)。通过 MySQL Router –bootstrap 引导后,R/W 端口能自动识别并连接到被提升的新 Primary(show slave hosts 可见)。结论:Router 兼容良好,但 ReplicaSet 不支持自动故障转移、有数据丢失/脑裂风险,离生产尚远。

什么是 InnoDB ReplicaSet?与 MGR/InnoDB Cluster 有何区别?

InnoDB ReplicaSet 8.0.19 引入,本质是基于 GTID 的异步复制(非组复制),角色分 Primary(仅一个)/Secondary(一个或多个),通过 MySQL Shell AdminAPI 管理,MySQL Router 引导方式类似(cluster_type=rs)。与 MGR 不同:它是异步复制,不支持自动故障转移,Primary 宕机整个副本集不可用,需 rs.forcePrimaryInstance 人工提升;无完善选举机制,有脑裂风险;仅适合测试环境试用。

MySQL 8.0 在复制延迟观测上做了什么改进(WL#7319/WL#7374)?

WL#7319 在 binlog 的 gtid_log_event(启用 GTID)和 anonymous_gtid_log_event 新增事务提交时间戳:original_commit_timestamp(在 master 提交 binlog 的时间,各节点一致)和 immediate_commit_timestamp(slave/中继节点提交 binlog 的时间)。WL#7374 为 performance_schema 复制表(replication_connection_status / applier_status_by_coordinator / applier_status_by_worker)新增观测点,可计算事务在不同位置的精确延迟。

如何用 performance_schema 表观测复制链路中不同位置的延迟?

以 Master A→中继 C→Slave D 为例:位置1(完整同步延迟)=worker 表 LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP - ORIGINAL_COMMIT_TIMESTAMP;位置2(中继到从库)=END_APPLY - IMMEDIATE_COMMIT_TIMESTAMP;位置3(已调度到回放等待)=coordinator 表 LAST_PROCESSED_TRANSACTION_END_BUFFER_TIMESTAMP - IMMEDIATE;位置5(同步到 relay log 延迟)=connection_status 表 LAST_QUEUED_TRANSACTION_END_QUEUE_TIMESTAMP - IMMEDIATE_COMMIT_TIMESTAMP。相比旧心跳表方案更精准且不影响 binlog。

MySQL 8.0 的 binlog 压缩功能如何工作,有什么限制?

以事务为单位压缩,发生在 binlog 落盘前的缓存步骤,用 ZSTD 算法压缩编码后写入。从库/ MGR 成员接收时识别 Transaction_payload_event,保持压缩态写入 relay log,由 SQL 线程负责解压解码。限制:①仅事务引擎;②仅支持 ROW 模式;③目前仅 ZSTD(底层开放,后续或可加 zlib/lz4);④压缩并行进行。瓶颈在网络带宽时压缩能缓解主从延迟;瓶颈在本机算力时反而加大延迟。

binlog 压缩的实际效果如何(压缩率与性能)?

测试 MySQL 8.0.20 一主一从半同步,默认压缩等级 3,设 level=10 时压缩率约 50%(相同 SQL 压缩前约 300M、压缩后约 150M)。在低带宽网络(tc 限速 256kbit + 100ms 延迟)下,压缩前 TPS 9.17/s,压缩后 10.15/s,集群 TPS 不降反略升。压缩以事务为单位在 binlog 落盘前完成,从库以 Transaction_payload_event 保持压缩态落 relay log、由 SQL 线程解压;压缩等级由 binlog_transaction_compression_level_zstd 控制。即压缩能减少 binlog 空间并缓解带宽型主从延迟,但本机算力瓶颈时会加大延迟。

基于 GTID 搭建多源复制时,如何设置各源的 GTID?

步骤:①stop slave 停所有从库;②reset master 清理本机所有 GTID(注意会清本地 binlog,级联复制下若有下游延迟需先备份 binlog);③SET @@GLOBAL.GTID_PURGED=‘各源GTID集合’;④change master … master_auto_position=1 FOR CHANNEL ‘xxx’ 逐个加源;⑤start slave。添加新从库时同样先停 slave、把已 PURGED 的 GTID 并入后重新 SET GTID_PURGED。级联复制 reset master 前需确认下游无大延迟并备份未同步 binlog。

Thanos 如何为 Prometheus 提供无限存储的高可用监控方案(与 MySQL 高可用关系)?

Thanos 单一二进制按启动变量分组件:Sidecar(与 Prometheus 同部署,上传数据到对象存储 S3/阿里云OSS等并供 Querier 查询)、Store(对象存储网关)、Query(兼容 PromQL、无状态可水平扩展)、Compact(压缩+降采样)、Bucket(对象存储块检查)。配置 Prometheus 加 –storage.tsdb.min/max-block-duration=2h 与 external_labels 区分集群,Sidecar 将 30 天本地数据上传对象存储实现长期存储高可用。这是 MySQL 监控/可观测性层面的高可用支撑。

MySQL 8.0 的 xtrabackup 8.0 为什么用 performance_schema.log_status 而不是 ftwrl?

MySQL 8.0 提供 performance_schema.log_status 表供在线备份工具获取复制日志信息:查询时服务器阻止日志记录及相关更改以填充该表再释放,提供应记录的 binlog 位点、gtid_executed 值、各复制通道 relay log,及各存储引擎(如 InnoDB)最后 LSN/检查点 LSN。8.0 仅 InnoDB 表时不执行 ftwrl(有非 InnoDB 表或 –slave-info 时才执行),减少锁开销。

Xtrabackup 2.4 备份后,xtrabackup_binlog_info 记录的 GTID 与恢复实例 show master status 不一致,数据丢了吗?

没丢。Xtrabackup 2.4 在 ftwrl 后通过 show master status 获取 binlog 位点/GTID 写入 xtrabackup_binlog_info(准确)。但恢复实例 show master status 的 Executed_Gtid_Set 来自 mysql.gtid_executed 表(启用 log_bin 时该表仅在 binlog rotate 时记录,当前 binlog 的 GTID 未入表),故显示比备份文件缺失一部分。实际数据都在,文件记录是准确的。

Xtrabackup 8.0 对 MySQL 8.0 备份时,xtrabackup_binlog_info 与恢复后 show master status 哪个准确?

8.0 备份仅 InnoDB 表时不再执行 ftwrl,而是通过 performance_schema.log_status 获取位点/GTID。当大量写入时 log_status 提供的 binlog position 与 GTID 不一致,故 xtrabackup_binlog_info 记录’不一定准确’;但 8.0 会 FLUSH NO_WRITE_TO_BINLOG BINARY LOGS 并拷贝 binlog,恢复实例启动后既读 gtid_executed 表也读 binlog 更新 GTID,所以恢复后 show master status 准确。若有非 InnoDB 表则执行 ftwrl,两者都准确。

MGR 一致性级别 EVENTUAL / BEFORE / AFTER 在实测中各有什么表现与缺点?

EVENTUAL(默认最终一致):读立即返回,不等待中继日志应用完,可能返回旧数据(优点快,缺点旧)。BEFORE(本地强一致):本节点等中继日志全部应用完才执行新请求,返回的是最新数据,但可能长时间 HANG(文中等 3分17秒),受未应用事务量影响。AFTER(全局强一致):必须等集群所有节点应用完自己事务才返回,提交时间依赖最慢节点,最慢节点故障则其他节点等待超时回滚(文中等 6分47秒)。参数可 SESSION/GLOBAL 设。

如何通过 binlog 找回历史执行 SQL?ROW 模式下要注意什么参数?

binlog 是二进制,需 mysqlbinlog 解析(优于 show binlog events,后者慢)。常用:mysqlbinlog bin.000008 –database db –base64-output=decode-rows -vv –skip-gtids=true | grep -C1 -i ‘delete from t’ > log。ROW 模式下要获取原始 SQL 需开启 binlog_rows_query_log_events(默认关闭,建议开),该参数通过 rows_query_event 记录原始 SQL;否则只能看到行数据。注意:–database 无法过滤 rows_query_event;触发器执行的 SQL 不记在 rows_query_event 中。

MGR/InnoDB Cluster 中为什么大事务(如 load data 大文件)容易导致节点被踢出集群?

组复制参考 paxos 实现 xcom 引擎(单线程)负责消息收发,大事务消息处理会阻塞其他消息(含心跳);若 5s 无心跳回应,节点被踢。group_replication_transaction_size_limit 限制事务大小(超限制回滚不广播)。8.0.16 支持消息分片(group_replication_communication_max_message_size,默认10MB、上限1GB,须≤slave_max_allowed_packet)自动分包。但分片后仍受 xcom cache 限制,缺失消息被淘汰则节点无法自动加回。

在 InnoDB Cluster 中如何高效加载大文件数据?相关参数如何调?

正确做法:拆分小文件并行导入,推荐 mysql shell AdminAPI 的 util.importTable(自动拆分并行,如 –bytes-per-chunk=10M)。参数层面:group_replication_transaction_size_limit(默认147MB,设0取消限制但生产不建议)、group_replication_communication_max_message_size、group_replication_message_cache_size(8.0.16 取消固定上限,配 member_expel_timeout 容忍更长网络延迟)。xcom cache 使用可在 memory_summary_global_by_event_name 表观测(memory/group_rpl/GCS_XCom::xcom_cache)。

MySQL 因 binlog flush 失败导致 Crash,根因通常是什么?

通常因磁盘空间满。关键参数 binlog_error_action 默认 ABORT_SERVER,写 binlog 遇严重错误(磁盘满/不可写)时直接退出保证 binlog 安全。具体:当事务超过 binlog_cache_size(默认32K),MySQL 在 tmpdir(默认 /tmp,常位于根分区)生成临时文件存储事务;若 tmpdir 满,flush 阶段报错,随后在同会话 commit 或再开事务即触发 binlog_error_action 导致 Crash。Crash 后临时文件释放,故事后看空间正常。

如何避免 binlog flush 失败导致的 MySQL Crash?

①根本是减少大事务,避免高并发下同时产生大量临时文件撑满 tmpdir(加大 binlog_cache_size 不可取——每连接都分配,300连接×32MB≈10G易 OOM);②增大 tmpdir 所在分区;③监控 tmpdir 与 binlog 分区剩余空间。扩展:navicat 还原大库走事务超 binlog_cache_size 会报 No space left on device,但因报错后直接断开连接不 commit,故不会 Crash。

MGR 能否在生产环境大规模使用?作者给出什么结论与注意点?

作者认为基于 8.0 的 MGR 可以大规模使用(银行体系用得很多,跳过主从直接用 MGR,5.7 升 8.0 后较稳定),但建议用 8.0 而非 5.7,且需做好前期测试/预案/回退/降级。缺点:官方配套驱动(Router/Connector)不如 MongoDB 方便;多写需业务代码适配(try-catch 捕获 commit 失败,因冲突检测可能回滚);VIP 切换方案不推荐,建议用 DNS。

基于 MGR 开发时,业务代码为什么要在 try-catch 中捕获 commit 失败?

MGR(尤其多主)基于 Paxos 做分布式共识与冲突检测,事务本质类似快照隔离级别。本地 commit 可能成功,但做冲突检测(基于主键/唯一键 write set 认证)时若与其他节点写冲突会返回失败并回滚——即’打 commit 可能失败’,这与传统主从架构 commit 必然成功不同。单主模式在主切换瞬间也可能回滚在途事务。因此业务代码需适配 MGR:把 commit 放进 try-catch,捕获失败(如 3101/3092 类错误)后整体重试,而非假定 commit 一定成功。