首页 / 资讯中心 / 文章详情

Maxwell启动报错 Could not find first log file name 排查与修复

Maxwell启动报错 Could not find first log file name 排查与修复 ★ FEATURED ARTICLE
早上到工位把改好的config.properties放到新环境里执行bin/maxwell日志先是正常连上了 MySQL然后咔一下卡在一个让人摸不着头脑的错误上Could not find first log file name in binary log index file第一眼我以为是 Maxwell 本身的 bug把binlog.file改来改去重启了好几次始终是同一个错。后来才意识到这句话根本不是 Maxwell 说的是 MySQL 服务器在拒绝 Maxwell 的 binlog 拉取请求时抛出来的。搞清楚这一点整个排查方向就对了不是你的工具坏了而是它想去读的那个 binlog 文件在 MySQL 眼里已经不存在了。这篇文章就把这个报错从原理到修复完整拆一遍覆盖常见触发场景、排查链路、三种修复方案以及日常怎么配置才能不再遇到它。不管是刚上手 Maxwell 的新人还是已经跑了一段时间突然踩坑的老手照着这个思路走一遍基本都能解决。1. 这个报错其实是 MySQL 在说话先看完整的调用链1.1 从 Maxwell 启动到报错中间发生了什么Maxwell 启动后做的事情不是直接读文件而是模拟一个 MySQL 复制从库向主库发起COM_BINLOG_DUMP请求。这个请求里带了两个关键参数要读取的 binlog 文件名和文件内的偏移位置。MySQL 收到这个请求后会先去自己的 binlog 索引文件里查一下你给我的这个文件名在不在我的索引列表里如果不在直接返回错误码 1236也就是ER_MASTER_FATAL_ERROR_READING_BINLOG对应的错误消息就是那句 Could not find first log file name in binary log index file。Maxwell 把这个错误打印到自己的日志里然后退出。所以你看到的这行报错本质上是 MySQL 主库在跟你说你要的 binlog 文件已经被我清掉了我手里没有这个东西没法从这个位置开始给你发数据。这个机制和 MySQL 主从复制里的经典错误完全一样。做过主从的人应该都见过类似场景从库落后太多主库的 binlog 已经过期清理从库重连时报Got fatal error 1236 from master。Maxwell 的这个报错就是同一件事的另一个马甲。1.2 为什么 Maxwell 会拿着一个不存在的文件去启动Maxwell 启动时确定起始位置的优先级是这样的优先读maxwell.positions表里存的上次确认位置如果没有可用位置记录才读配置文件里的binlog.file/binlog.position都没有的话用init_position指定的位置或者从当前 master 位置开始也就是说Maxwell 只是一个读位置的角色它自己并不判断这个位置是否有效。真正判断有效性的是 MySQL而判断依据就是 binlog 索引文件。MySQL 的 binlog 索引文件一般叫mysql-bin.index在数据目录里。它的内容就是一行一个 binlog 文件的完整路径/var/lib/mysql/mysql-bin.000001 /var/lib/mysql/mysql-bin.000002 /var/lib/mysql/mysql-bin.000003MySQL 每次写满一个文件、切换到下一个文件时会把新文件名追加到这个索引里。反过来执行PURGE BINARY LOGS或者触发 binlog 过期清理时会把被清理的文件从索引里移除同时把磁盘上的文件也删掉。所以 Maxwell 要找的文件不在索引里只有两种可能要么这个文件曾经存在后来被清了要么这个文件从头到尾就不该出现在这个 MySQL 实例里。下面这些场景基本能把两种情况都覆盖到。2. 四类最常见的触发场景对号入座2.1 Maxwell 停机太久binlog 被过期策略清掉这是最常见的情况。Maxwell 每次处理完一批 binlog event会把位置写回maxwell.positions表。如果它持续运行这个位置始终在往前推进没问题。但它一旦停机这个位置就冻结在停机前的时刻。MySQL 这边不会因为 Maxwell 停了就停止写 binlog也不会停止清理。只要 binlog 超过配置的保留时间binlog_expire_logs_seconds或老版本的expire_logs_days就会被后台线程清掉。于是你重启 Maxwell 时它拿着停机前的位置去找文件而这个文件在几小时或几天前就已经被清理了MySQL 直接回你一句找不到。这种情况下的典型信号是报错里提到的 binlog 文件编号比当前SHOW BINARY LOGS里看到的最老文件编号还要老。2.2 手动删除 binlog 文件的操作事故第二种常见场景是人为清理。磁盘空间吃紧的时候有人会直接去数据目录里执行rm -f mysql-bin.0000xx以为删掉旧 binlog 就能腾空间不执行任何 MySQL 命令。这是非常危险的操作。因为你只删了磁盘文件binlog 索引文件里还留着对应的条目。删完之后可能出现两种诡异状态磁盘上文件没了但SHOW BINARY LOGS里还列着它索引文件也被手改过导致 MySQL 自己都搞不清楚现在有哪些 binlog无论哪种Maxwell 去读一个已经被半删除的文件要么报 1236要么直接读文件失败。正确清理 binlog 的方式只能是-- 保留 mysql-bin.000120 及其之后的所有文件删除它之前的 PURGE BINARY LOGS TO mysql-bin.000120; -- 或者按时间清理保留最近 3 天的 PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;PURGE命令会同时更新索引文件并删除磁盘文件两边保持一致不会留下半截状态。2.3 maxwell 元数据库被还原或迁移第三种场景隐蔽一点但遇到的人不少。MySQL 数据库做过恢复演练或者从备份机房回滚把整个数据目录都还原到了某个历史时间点。只要 Maxwell 的元数据存放在它自己创建的maxwell库里的positions表也跟着一起被还原里面的位置就会退回到备份时刻对应的 binlog 文件。结果就是业务库的 binlog 已经推进了很远maxwell 库里的位置却停留在过去。Maxwell 一启动拿着过去的位置去读现在的 binlogMySQL 当然找不到这个文件。还有一种变体在新环境部署 Maxwell 时图省事直接从旧环境把整个maxwell库导过来。两个环境的 binlog 序列完全不是一回事这个错几乎是必现的。2.4 配置文件或启动参数写死了不存在的文件最后一种最简单直接配置里写了binlog.file和binlog.position但写的是别的环境或者别的时间点的值。比如把某篇教程里的mysql-bin.000089直接抄进自己的config.properties而你的 MySQL 实例上根本没有这个文件。这种情况的特征是报错日志里显示的 binlog 文件名跟配置文件里写的完全一致。如果你用的是命令行参数同理bin/maxwell --userxxx --passwordxxx --host127.0.0.1 --init_positionmysql-bin.000089:4如果mysql-bin.000089已经被清理这条命令启动后就会撞上同样的错误。下面这张表可以帮你快速对号入座触发场景典型信号直接证据停机太久binlog 过期重启即报错位置明显偏旧SHOW BINARY LOGS最老文件比目标文件新手动删了 binlog磁盘清理记录里有 rm 操作磁盘文件列表与SHOW BINARY LOGS不一致元数据库被还原/迁移整个 maxwell 库从备份导回positions表时间戳远早于当前 binlog 写入时间配置写死旧文件报错文件与配置文件完全一致日志里binlog_file等于配置值3. 排查链路逐层确认到底是谁在说谎遇到这个报错别急着改配置重启。按下面的顺序把四个关键信息全部拉出来对齐之后再做决定不然很可能只是把错误从启动时报错变成启动后丢数据。3.1 第一步确认 Maxwell 想从哪里开始读先查maxwell.positions表SELECT * FROM maxwell.positions;重点看binlog_file和binlog_position两列。如果表里没有记录说明 Maxwell 会退回配置文件和启动参数。那就去看config.properties里有没有binlog.file/binlog.position以及启动命令里有没有--init_position。这一步要搞清楚的是Maxwell 以为自己应该从哪个位置开始。把这一步得到的文件名记下来后面要拿它和 MySQL 实际拥有的 binlog 列表做对比。提示如果在maxwell.positions表里看到多条记录通常对应不同的client_id或server_id记得按你当前运行的实例对应的那一条来确认。不确定的话把所有记录都拉出来看一眼每条都对比一遍。3.2 第二步确认 MySQL 现存哪些 binlog 文件在 MySQL 上执行两个命令-- 查看所有可用的 binlog 文件 SHOW BINARY LOGS; -- 查看当前正在写哪个文件、写到什么位置 SHOW MASTER STATUS;SHOW BINARY LOGS会列出当前所有的 binlog 文件SHOW MASTER STATUS会告诉你正在写入的文件名和当前偏移量。拿到这份列表后先看两件事第一Maxwell 想读的文件在不在列表里第二如果不在列表里最老的文件是哪个、和 Maxwell 想要的文件差了几个文件、大概差了多少时间。3.3 第三步核对 binlog 索引文件的实际内容SHOW BINARY LOGS读的就是 binlog 索引文件但有些手动操作会造成索引和实际磁盘文件不一致。所以这一步去数据目录把索引文件直接打开看一眼cat /var/lib/mysql/mysql-bin.index核对几个信息索引文件里的文件名是否都能在磁盘上找到对应文件磁盘上有没有文件存在、但索引文件里没有条目的情况SHOW BINARY LOGS的结果和cat索引文件的结果是否一致如果索引里有条目但文件没了基本可以断定有人手动rm过 binlog。这种状态比单纯的文件被过期清掉更麻烦因为 MySQL 在某些情况下会尝试去打开索引里第一个文件失败了可能连实例都起不来。如果开了 GTID这一步还要额外看一眼SHOW VARIABLES LIKE gtid_purged。就算文件名还能对上如果 Maxwell 保存的 GTID 位点已经在gtid_purged范围内一样拉不到数据。3.4 第四步估算数据缺口和影响范围前两步确认了Maxwell 要的位置和MySQL 实际有的文件之间存在断层接下来要判断这个断层到底意味着丢了多久的数据。最简单的方式是看 binlog 文件的时间跨度。数据目录下执行ls -lh --time-stylelong-iso /var/lib/mysql/mysql-bin.*从 Maxwell 想要的最后一个文件到现存最老文件之间差了几个文件、跨了多少小时基本就是停机窗口的长度。再配合业务侧的统计比如这一时间段内核心表的写入量就能判断缺口是否可以接受。如果 Maxwell 之前一直正常你只是在它停机一段时间后重启才遇到这个错那缺口就是停机窗口内发生的所有数据库变更。4. 三种修复路径按数据丢失容忍度挑选排查清楚之后修复本身不复杂。关键在于你先要想明白一个问题停机期间丢掉的变更你能不能接受根据答案不同选下面的路径。4.1 方案一把 Maxwell 指到当前 master 位置接受缺口如果业务能接受停机窗口内的数据不补这是最快的方式。先拿当前位点SHOW MASTER STATUS;假设结果是mysql-bin.000123Position154。有两种方式让 Maxwell 从这条线继续方式 A直接更新positions表UPDATE maxwell.positions SET binlog_file mysql-bin.000123, binlog_position 154 WHERE client_id default;方式 B删除位置记录用init_position指定DELETE FROM maxwell.positions;bin/maxwell --userxxx --passwordxxx --host127.0.0.1 \ --init_positionmysql-bin.000123:154两种方式选一种就行。我个人更倾向方式 A因为保留positions表里的其他字段比如last_heartbeat_readMaxwell 启动后的状态更连续。这里有个细节要解释一下MAXWELL 启动时优先读positions表如果表里有记录配置文件里的binlog.file这些是会被忽略的。所以如果你改了config.properties但重启还是报同一个错大概率就是positions表里的旧记录没动。要么 UPDATE要么 DELETE二选一。注意修复完成后Maxwell 会从154这个位置开始继续消费而154是当前正在写入的最新位置。也就是说从停机到现在的所有变更都不会补发到下游。如果你后面又觉得这些数据需要补只能走下面的 bootstrap 方案单独处理。4.2 方案二清空元数据干净地重新初始化如果maxwell库本身已经不可信——比如是从旧环境拷来的或者做了回滚——那不建议只改一个文件位置因为schemas、tables这些表里的库表结构信息可能也是旧的后面会引发更多奇怪问题。这种情况下直接把 Maxwell 的元数据清掉让它重新初始化DELETE FROM maxwell.positions; DELETE FROM maxwell.heartbeats;如果连库表结构都快照都不想要可以直接连maxwell库一并删掉重启 Maxwell 时会自动重建整个元数据 schemaDROP DATABASE maxwell;bin/maxwell --userxxx --passwordxxx --host127.0.0.1 --init_positioncurrent--init_positioncurrent会让 Maxwell 从当前 master 位置开始不再依赖任何历史记录。这个参数在全新部署和元数据重建场景里非常好用省得手动去SHOW MASTER STATUS再填一遍。4.3 方案三先恢复增量再对关键表做 bootstrap 补偿如果停机窗口里的数据不能丢但窗口又不长可以混合使用方案一和 Maxwell 的 bootstrap 功能。操作思路是按方案一先让 Maxwell 跑起来从当前位点继续消费保证增量不再断对停机窗口内有写入的核心表执行一次性 bootstrap把表当前的全量快照导到下游bin/maxwell-bootstrap --databaseorder_db --tablet_orderbootstrap 的机制是记录一个起始位点然后读取目标表的当前全量数据以bootstrap-insert类型的事件输出到下游。bootstrap 期间发生的增量变更Maxwell 会在快照输出完之后继续以正常事件输出下游按主键合并就能得到完整状态。要明确一点bootstrap 给的是当前时刻的快照 快照之后的增量并不是对停机窗口内每条变更的精确回放。如果业务的诉求是把那一小时的每一笔操作都恢复出来bootstrap 做不到那得从备份恢复或者用专门的同步链路重放。但对大多数补回最终状态的需求来说bootstrap 已经够用了。4.4 修复后的验证动作不管选了哪个方案重启之后都要做一轮验证确认 Maxwell 真的在正常消费而不是表面上启动了、实际上又卡在某个位置。验证顺序# 1. 确认 Maxwell 进程稳定运行不再报 1236 tail -f /path/to/maxwell.log-- 2. 确认 positions 表的文件位置开始往前推进 SELECT binlog_file, binlog_position, last_heartbeat_read FROM maxwell.positions;等一两分钟再查一次如果binlog_position变了说明它真的在读 binlog。-- 3. 查心跳延迟 SELECT * FROM maxwell.heartbeats;最后做一条端到端验证在业务库 update 一条记录去下游 Kafka 对应 topic 里确认这条变更 JSON 是否出现。这一步能一次性验证权限、网络、序列化链路都没问题。5. 预防与日常维护让这个报错彻底消失5.1 binlog 保留策略的正确配置方式这个报错的根源是 binlog 被清掉所以最直接的防线就是让 binlog 保留时间足够覆盖 Maxwell 的最长停机时长。MySQL 8.0 用binlog_expire_log_secondsMySQL 5.7 及以下用expire_log_days# my.cnf [mysqld] binlog_expire_log_seconds 604800保留时间的设置逻辑不是拍脑袋定天数而是先评估两个数第一你允许 Maxwell 故障的最长时长第二给这个时长留出至少一倍的缓冲。比如你规定Maxwell 挂了 2 天内必须恢复那 binlog 至少要保留 4 到 7 天。评估之前先看一眼磁盘用量du -sh /var/lib/mysql/mysql-bin.*算一下每天产生多少 binlog乘以保留天数确认磁盘放得下。如果磁盘紧张优先扩容或者压缩大事务产生的 binlog 体积不要靠缩短保留时间来硬扛。5.2 监控与告警在 binlog 被清掉之前发现风险Maxwell 自带心跳机制会周期性往maxwell.heartbeats表写心跳记录同时在下游产生心跳事件。通过心跳时间差可以精确知道消费延迟了多少秒。我建议设定两条告警线延迟告警Maxwell 的last_heartbeat_read与当前时间差超过 5 分钟说明消费链路可能卡住了危险告警SHOW BINARY LOGS里最老文件的时间戳距离当前时间已经接近 binlog 保留窗口的一半第二条是最容易被人忽略的。它代表的是如果 Maxwell 现在停机它能安全存活多久。比如 binlog 保留 7 天最老 binlog 已经写了 6 天那 Maxwell 现在一旦挂掉距离报 1236 只剩一天不到。这种情况就应该提前处理而不是等真出问题再救火。5.3 变更操作纪律几条硬性规定最后分享几条我在实际运维中定下的操作纪律每一条都是踩坑踩出来的第一严禁直接在数据目录里删 binlog 文件。所有清理操作必须走PURGE BINARY LOGS让 MySQL 自己维护索引的一致性。第二config.properties里不要写死binlog.file和binlog.position。这两个配置项只在特殊场景指定从某个历史位点启动才用日常配置里写了就是给未来的自己埋雷。全新部署直接--init_positioncurrent。第三备份和恢复 MySQL 时maxwell库必须和业务库作为一个整体来处理。不要只恢复业务库不恢复 maxwell 库也不要只恢复 maxwell 库不恢复业务库否则两边位置信息必然错位。第四改 MySQL 的 GTID 模式、清理策略、binlog 路径这类配置之前先停 Maxwell记录当前SHOW MASTER STATUS再动配置。改完确认无误后再启动 Maxwell。第五双跑多实例的场景下注意client_id和server_id不能冲突。多个 Maxwell 实例共用同一个maxwell库时positions表里有多条记录核对位置时一定要看清楚自己对应的是哪条。我自己的习惯是每次动完 Maxwell 相关配置都会把SHOW MASTER STATUS的结果和positions表内容存一份快照标注日期。下次再遇到启动异常翻一下快照就能立刻判断是正常推进还是位置回退排查时间能从小时级压缩到分钟级。这个小动作成本很低关键时刻真的能救命。
阅读完成 · 觉得有帮助?
咨询建站