
做监控运维的人早晚会撞上同一个坎Zabbix 的数据库文件越来越大history和trends表动辄几千 GBMySQL 的磁盘 IO 和主从同步被拖垮前端报表打开转圈告警也能延迟几分钟。我之前在某个中等规模的监控项目里也遇到这个问题检查了所有常规优化手段之后决定把 Zabbix 的后端存储从 MySQL 迁移到 TiDB也就是把 MySQL 里的历史监控数据完整导入 TiDB 服务里运行。这条改造路线走完整体方向是对的但中间有不少细节是官方文档不会明确告诉你的。这篇文章把从选型、环境规划、数据迁移、切换验证到后续排障的完整过程都写下来给后面打算做同样改造的人留一份可复现的参考。1. 改造背景Zabbix 后端从“能跑”到“扛不住”1.1 监控历史数据是存储增长的主要来源Zabbix 默认把监控数据存在 MySQL 或 PostgreSQL 里最占地方的是几张历史表history浮点数型监控项的历史采样值history_uint无符号整数型监控项的历史采样值history_str、history_text、history_log字符和日志类数据trends、trends_uint趋势数据按小时聚合这些表的数据量增长和监控项数量、采样频率直接挂钩。比如每 5 秒采集一次单一监控项一小时就是 720 条记录采集几百上千台设备一天就能产生数百万行数据。MySQL 单机存储在这种写入强度下一段时间后必然出现两个症状一是写入跟不上复制延迟飙高二是历史数据查询慢前端图表区域明显卡顿。我做改造前先统计了一下现状监控项总数约 12 万历史数据表约 160 GBhistory_uint单表最大占了近 100 GB。当时的 MySQL 是单机主从架构磁盘已经是 SSD但仍经常出现主库 CPU 打满、从库明显延迟的情况。即使清理脚本 housekeeper 一直开着删除历史数据的速度也赶不上新增速度属于典型的“存储成为瓶颈”场景。1.2 为什么选择 TiDB 而不是继续堆 MySQL遇到瓶颈后第一个念头是继续压榨 MySQL升级硬件、优化索引、把历史表分区。但这些都只能延缓问题解决不了本质。监控数据的写入是持续且并发的单机 MySQL 的写入能力有物理上限再往上堆就是主从复制、分库分表运维复杂度和成本立刻上升。选择 TiDB 主要看中这几点原生分布式数据自动打散到多个 TiKV 节点写入可以水平扩展完全兼容 MySQL 协议Zabbix 不需要改业务代码连接方式还当成 MySQL 看待自动 Rebalance扩容节点时数据自动迁移不用手工拆库拆表支持分区表可以继续使用 Zabbix 官方的分区维护脚本按时间切片管理历史数据事务与一致性TiDB 提供强一致事务对 Zabbix 这种同时读写的系统很重要一句话总结TiDB 能让 Zabbix 的历史数据存储变成“可水平扩展的 MySQL”而这正是监控场景最需要的。1.3 什么样的环境不适合这个方案不是所有 Zabbix 环境都适合迁移。如果监控规模很小比如只有几十台设备、历史数据不到 20 GB那完全没有上 TiDB 的必要单机 MySQL 配合分区策略完全够用。另一个条件是团队有没有分布式数据库的运维能力TiDB 集群涉及 PD、TiKV、TiDB 三个组件的日常巡检和维护比单机 MySQL 多不少事。如果这两条不满足建议不要动。2. 环境规划TiDB 集群与 Zabbix 的版本配套2.1 软件版本选择与兼容性结论版本选择是迁移前最重要的一步。我的选择是组件版本说明Zabbix6.4 LTS当前长期支持版本前端数据库检查要求 MySQL 8.0TiDB6.5 LTSTiDB 长期支持版本与 Zabbix 6.x 兼容源 MySQL8.0.28原 Zabbix 数据库所在实例Zabbix 6.4 前端在安装或初始化时会对数据库版本做检查要求 MySQL 8.0 及以上。TiDB 6.5 默认返回的版本号是5.7.25-TiDB-v6.5.x这个版本号会被 Zabbix 前端判定为“不受支持的数据库版本”虽然不影响后端读取但会在前端告警区一直挂着。解决办法是在 TiDB 配置里把回传给客户端的版本号改成 8.0配置项是server-version。我使用的是 TiDB 配置文件方式[server] server-version 8.0.27-TiDB-v6.5.6改完重启 TiDB 进程客户端再查SELECT VERSION()就会返回这个自定义版本号Zabbix 前端的版本检查就过了。2.2 TiDB 最小集群硬件与部署拓扑TiDB 集群最小化生产部署建议如下组件节点数建议配置磁盘TiDB Server1 台起4C 8GSSD 100 GBPD3 台2C 4GSSD 100 GBTiKV3 台起8C 32GSSD 1 TB节点数量上PD 必须是奇数3 台起步TiKV 至少 3 台。TiDB Server 可以后面按需增加但至少要 1 台。磁盘空间估算时要把 TiKV 默认 3 副本考虑进去原 MySQL 数据 160 GB会占用 TiKV 总空间约 480 GB再加上 Region 调度余量和日志文件单台 TiKV 给 1 TB SSD 比较稳妥。我用 TiUP 部署集群拓扑文件里定义了 3 个 PD、3 个 TiKV 和 1 个 TiDB 实例的地址与配置。部署完成后用tiup cluster display确认所有节点都是健康状态再继续。2.3 迁移前务必处理的三处 MySQL 差异TiDB 兼容 MySQL 协议但不代表全盘兼容所有表结构和 SQL 细节。我提前处理了以下三个差异避免导入时才发现不行存储引擎MySQL 的 MyISAM 表在 TiDB 里不支持导入前全部改成 InnoDB字符集与排序规则Zabbix 在 MySQL 8 默认使用utf8mb4_0900_ai_ciTiDB 不支持这个排序规则需要在导出/导入时统一为utf8mb4_bin或utf8mb4_general_ci版本号返回上文提到的server-version配置提前设好这三项不处理后续要么导入直接报错要么导入成功但前端检查通不过。3. 数据迁移实操用 Dumpling Lightning 完成替换3.1 停机策略离线全量为什么最稳迁移方案上我最终选择了离线全量导入而不是在线迁移工具做实时同步。原因很简单Zabbix 的监控数据是持续写入的实时数据如果一边在 MySQL 继续写入、一边往 TiDB 导就还得处理增量同步和双写一致性问题复杂度明显上升容易出幺蛾子。对监控系统来说停机迁移的窗口是可以接受的只要在业务低峰窗口进行比如周末上午影响面很小。实际操作流程是停止 Zabbix Server 服务让 MySQL 数据库处于静止状态用 Dumpling 从 MySQL 导出zabbix全库结构加数据用 TiDB Lightning 将导出文件导入 TiDB修改 Zabbix Server 和前端配置指向 TiDB启动服务验证数据完整性3.2 用 Dumpling 从 MySQL 导出 zabbix 全库Dumpling 是 TiDB 生态的数据导出工具兼容 MySQL 协议可以从 MySQL 实例导出逻辑备份文件导出的格式可以被 TiDB Lightning 直接识别导入。先安装 Dumpling我用的是 TiUP 自带方式tiup install dumpling导出命令dumpling \ -h 127.0.0.1 \ -P 3306 \ -u root \ -p 原mysql密码 \ --regex ^(zabbix)\..*$ \ --filetype sql \ --threads 8 \ --output-dir /data/dumpling_output \ --sql-mode ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION参数说明--regex ^(zabbix)\..*$表示只导出zabbix库下的所有表--filetype sql生成 SQL 格式文件包含建表语句和插入语句--threads 8并发线程数根据源 MySQL 的磁盘能力调整8 线程对机械盘可能打满SSD 可以更高--sql-mode指定导出会话的 SQL 模式尽量贴近 MySQL 的默认模式减少导出过程中的兼容问题导出完成后在/data/dumpling_output目录下会看到类似zabbix-schema.sql、zabbix.history-schema.sql、zabbix.history.sql这样的文件分别对应库结构、表结构和数据。3.3 用 TiDB Lightning 导入 TiDBTiDB Lightning 是 TiDB 的高性能数据导入工具我做本次导入用的是物理导入模式local backend原理是直接把数据写成 TiKV 能识别的 SST 文件再调度到集群里。相比用 mysql 命令行一条条执行 SQL速度快得多我这套 160 GB 数据全程导入大约用了 40 分钟。Lightning 的配置文件tidb-lightning.toml这样写[tidb] host 10.0.0.21 port 4000 user root password tiup部署时的root密码 status-port 8280 pd-addr 10.0.0.21:2379 [tikv-importer] backend local sorted-kv-dir /data/sorted-kv-dir [mydumper]>tiup tidb-lightning -config tidb-lightning.toml日志里看到checksum pass和import finished字样说明导入成功。导入前我在 TiDB 里已经创建了空的zabbix库字符集统一为utf8mb4、排序规则utf8mb4_binLightning 会自动建表并插入数据。3.4 修改 Zabbix 服务端与前端配置完成切换数据导入完成后开始切换连接。修改 Zabbix Server 配置文件/etc/zabbix/zabbix_server.confDBHost127.0.0.1 DBNamezabbix DBUserzabbix DBPassword新数据库密码 DBPort4000DBPort改为 TiDB 的服务端口 4000而不是 MySQL 的 3306。DBUser 和 DBPassword 在 TiDB 里要提前创建好并授权访问zabbix库。前端配置文件/etc/zabbix/web/zabbix.conf.php同样改端口和密码$DB[SERVER] 127.0.0.1; $DB[PORT] 4000; $DB[DATABASE] zabbix; $DB[USER] zabbix; $DB[PASSWORD] 新数据库密码; $DB[TYPE] MYSQL; $DB[CHARSET] utf8mb4;Zabbix 端不需要改类型因为 TiDB 走的就是 MySQL 协议前端仍然按 MySQL 类型连接。改完后重启服务systemctl restart zabbix-server systemctl restart httpd # 或者 php-fpm按前端部署方式而定3.5 数据切换后的五步验证切换完成不代表结束我做了一套完整的验证清单查看 Zabbix 前端首页能否正常加载最新监控数据和告警检查前端“报告”页的趋势图能否正常出图这是验证trends表数据是否完整的最快方式在 TiDB 里执行核心数据量统计和 MySQL 导出前的总数对比SELECT COUNT(*) AS cnt FROM zabbix.history_uint; SELECT COUNT(*) AS cnt FROM zabbix.trends_uint;用 Zabbix 自带的探针或手动执行新的数据采集确认能正常写入 TiDBSELECT itemid, clock, value FROM zabbix.history_uint ORDER BY clock DESC LIMIT 5;确认 housekeeper 进程日志正常历史清理任务能执行不会因为切库失败而堆积五步都通过后说明迁移完成。此时不要着急删除原 MySQL 备份建议保留至少一周回滚窗口要留足。4. 上线后的排查与调优经验4.1 切换后的真实性能变化上线稳定运行一周后我做了前后对比。最直观的感受是前端打开“最新数据”和“图形”不再卡顿。以前点开一个包含几十个监控项的图形页面MySQL 要扫大量历史数据经常等 5 秒以上切换 TiDB 后基本在 1 秒内出图。这一方面是因为数据分散到了多台 TiKV 并行计算另一方面也和 TiDB 对读请求的弹性调度有关系。写入方面Zabbix Server 的队列监控显示历史数据发送到数据库的间隔明显缩短不再出现因为数据库写入慢导致的积压告警。4.2 高频报错与处理速查表迁移和上线过程中我碰到了几个别人大概率也会撞上的问题整理成速查表报错/现象原因解决方式前端提示数据库版本过低TiDB 默认返回 MySQL 5.7 版本号设置server-version为 8.0.x建表报Unknown storage engine MyISAM遗留 MyISAM 表改为 InnoDB 后重新导入导入报Unknown collation utf8mb4_0900_ai_ciMySQL 8 默认排序规则 TiDB 不支持导出前统一排序规则为utf8mb4_binZabbix Server 起不来日志提示数据库连接失败DBPort/IP 没改或 TiDB 未启动检查DBPort4000确认tiup cluster display状态正常前端页面卡顿TiDBCPU 高并发查询多集中在 TiDB Server增加 TiDB Server 实例或优化慢查询查询报Out of sorted memoryTiDB 单次排序内存超限调大tidb_mem_quota_query或优化 SQL那几天我印象最深的坑是排序规则问题。源 MySQL 的表结构里带着utf8mb4_0900_ai_ciDumpling 导出时数据中心保留了原排序规则导入到 TiDB 直接报错。最后是把所有导出的-schema.sql文件里的utf8mb4_0900_ai_ci批量替换为utf8mb4_bin再重新导入才正常。4.3 历史数据清理从 housekeeper 到分区表Zabbix 默认用 housekeeper 定期清理过期历史数据本质是执行 DELETE 语句。在 TiDB 里DELETE 因为底层要扫描多个 Region删除速度一般如果历史数据量很大housekeeper 可能来不及清。我当时观察到的现象是历史表的数据量不再下降housekeeper 任务一堆排队。解决思路是放弃对历史表依赖 DELETE改用分区表的 TRUNCATE PARTITION 方式。以history_uint为例按天做 RANGE 分区ALTER TABLE zabbix.history_uint PARTITION BY RANGE (clock) ( PARTITION p20250101 VALUES LESS THAN (UNIX_TIMESTAMP(2025-01-02 00:00:00)), PARTITION p20250102 VALUES LESS THAN (UNIX_TIMESTAMP(2025-01-03 00:00:00)) );清理时直接丢弃过期分区ALTER TABLE zabbix.history_uint DROP PARTITION p20250101;TRUNCATE 分区的开销远小于逐行 DELETE而且对 TiDB 的 Region 调度更友好。Zabbix 官方有一个分区维护脚本继续在 TiDB 上运行即可它会定期为新的一天添加分区再按保留天数删除过期分区。4.4 TiDB 侧值得调整的配置项稳定运行后我对 TiDB 集群做了一些针对 Zabbix 工作负载的调整这里挑重点说SQL 模式在 TiDB 集群配置里打开ONLY_FULL_GROUP_BY避免 Zabbix 某些按监控项分组查询的 SQL 因为模式不一致出现意想不到的行为TiKV 存储引擎缓存把storage.block-cache.capacity调整为节点物理内存的 30% 左右提高热点数据命中率内存保护设置tidb_mem_quota_query 8GB避免了大查询直接打爆 TiDB Server 内存慢查询日志打开慢查询记录把执行超过 1 秒的 SQL 捞出来分析这些配置都改在拓扑文件的配置段里用tiup cluster reload滚动生效不中断业务。5. 项目复盘这三个坑最值得避开5.1 最值得避开的三个坑如果让我重新做一遍有三件事会提前做得更充分导出前先统一排序规则。在 MySQL 导出前就把表结构里的utf8mb4_0900_ai_ci全部改成utf8mb4_bin而不是等导入报错再批量替换文件能少走很多弯路。提前确认 Zabbix 前端版本检查机制。server-version这个配置项要写在集群初始化配置里而不是上线前临时改。迁移前评估 Lightning 落盘空间。sorted-kv-dir如果空间不够导入中途会卡住那个状态折腾人。5.2 如果规模再大一级后续怎么做这次改造解决的是 GB 级历史数据的瓶颈。如果监控规模再往上走比如监控项超过 50 万、历史数据到 TB 级别TiDB 这边依然有扩展空间直接给集群加 TiKV 节点数据会自动均衡TiDB Server 加一台做负载分担如果分析类报表多还可以给trends表挂 TiFlash 列存副本分析查询走列存会更快。加节点操作并不复杂用 TiUP 修改拓扑文件后执行scale-out就行。还有个方向是把 Zabbix Server 与 TiDB 之间的连接前面加一个连接池代理避免前端并发激增时把 TiDB 连接打满。5.3 我个人的迁移心得这次改造给我的最大体会是Zabbix 存储选型与其在单机 MySQL 上不断加索引、清数据、做分片不如直接换一个原生支持水平扩展的分布式数据库。TiDB 的 MySQL 兼容性让整个切换过程不需要改一行业务代码迁移工具链也足够成熟难度远比想象中低。再补一个细节迁移完成后原 MySQL 彻底停用前一定要留一台离线备机保留原库一个月左右。监控系统运行过程中经常需要回溯“半个月前的原始数据”如果新库验证没做透回退还有后路。我是在一个月确认无误后才把原 MySQL 实例下线的这个谨慎一点不亏。这次的项目记录就写到这里。如果你正准备做类似的 Zabbix 存储迁移希望这篇能帮你少踩几个坑。迁移本身不复杂复杂的是把细节做踏实。