很多DBA和运维第一次在MySQL 8.0上搭主从复制时,都会撞上这条报错:
[ERROR] [MY-010584] [Repl] Slave I/O for channel '': error connecting to master 'repl@192.168.x.x:3306' - retry-time: 60 retries: 1 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.如果你没开SSL,也没做额外配置,八成就是被MySQL 8.0默认的caching_sha2_password认证插件卡住了。这个报错在5.7时代几乎见不到,因为5.7默认还是mysql_native_password,复制用户建好、授权给足、网络通,基本就完事了。到了8.0,事情变了,而且报错信息有歧义,很容易让人误判成"网络不通"或者"密码不对",结果折腾半天方向全错。
这篇文章把这条例报错从原理到实操完整拆一遍,内容包括:这条错误信息到底在说什么、8.0的认证机制为什么会导致复制连不上、完整的排查链路怎么走、以及一份可以直接照抄的主从重建操作清单。不管你是刚接触MySQL的运维新手,还是被这个报错折磨过的老手,这篇都能让你少走弯路。
1. 先搞懂报错:MY-010584 和 Slave I/O 线程的连接流程
1.1 错误码背后的执行单元:I/O线程
MySQL主从复制的架构里,从库上有两个关键线程:I/O线程和SQL线程。I/O线程负责连接主库,把主库binlog拉取到从库的中继日志(relay log);SQL线程负责把中继日志里的SQL在从库上重新执行。两者是串行配合的关系,I/O线程挂了,SQL线程拿不到新数据,主从就断了。
MY-010584这个错误码对应的就是I/O线程连接主库失败。Slave I/O for channel ''里的channel是复制通道,默认是空字符串,如果你配了多源复制,这里会显示具体通道名。
I/O线程的连接流程可以拆成四步:
- 解析
CHANGE MASTER TO里配置的主库地址、端口、用户名; - 通过TCP建立到主库的网络连接;
- 使用复制账号完成MySQL协议层的认证;
- 认证通过后,向主库请求binlog坐标,启动拉取。
这四步任何一步出问题,I/O线程都会报错并进入重试循环。报错信息里retry-time: 60 retries: 1的意思是默认每60秒重试一次,当前重试计数到1,这本身不是额外的问题,不用去动重试参数,先把连接失败的原因找到才是正道。
1.2 报错信息里真正有价值的部分
很多人看到报错就慌,其实这条报错信息分三段:第一段是连接目标,第二段是重试机制说明,第三段是message:后面的具体错误原因。前两段只是背景,最后一段才是真正的诊断线索。
比如下面这条:
error connecting to master 'repl@192.168.1.10:3306' - retry-time: 60 retries: 1 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.重点全在最后:Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.。这说明TCP层已经通了,账号也能找到,卡在认证环节——8.0的caching_sha2_password插件要求走加密连接,或者通过RSA公钥交换密码。
但如果你看到的是下面这样:
message: Access denied for user 'repl'@'192.168.1.20' (using password: YES)那问题就在账号密码或host白名单上。如果看到的是:
message: Lost connection to MySQL server at 'reading initial communication packet', system error: 95那大概率是网络层的问题,比如防火墙DROP了端口、主库bind_address限制、或者主库根本没在监听。所以排查的第一步不是去网上搜错误码,而是把message:后面的内容完整读一遍,用它来定位方向。
2. 主从连接失败的11个高频诱因,按概率排序
根据我这些年的经验,MySQL 8.0主从I/O线程连接失败的原因,无外乎下面这些。按出现概率大致排个序:
| 序号 | 诱因 | 典型报错特征 | 排查难度 |
|---|---|---|---|
| 1 | 复制账号认证插件与连接方式不匹配 | Authentication requires secure connection | 低 |
| 2 | 账号密码错误 | Access denied for user | 低 |
| 3 | 账号host白名单限制 | Access denied for user | 低 |
| 4 | 防火墙拦截3306端口 | Lost connection... system error: 95或直接卡死 | 中 |
| 5 | 主库bind_address只监听了127.0.0.1 | 连接超时或被拒 | 中 |
| 6 | 复制账号缺少REPLICATION SLAVE权限 | Access denied或权限不足相关报错 | 低 |
| 7 | 从库提前执行了START SLAVE但没做CHANGE MASTER TO | 连接信息不完整或NULL | 低 |
| 8 | server_id或server_uuid冲突 | 报错信息会提示server_uuid重复 | 中 |
| 9 | 主库skip_name_resolve开启但账号host用的是主机名 | Access denied | 中 |
| 10 | 主库openssl/SSL配置异常 | SSL相关报错 | 高 |
| 11 | 主从版本差异过大导致协议不兼容 | 协议握手失败 | 低 |
别急着一个个试,先用手工方式复现连接,一次就能区分出到底是认证问题还是网络问题。具体怎么做下一节讲。
3. MySQL 8.0的"认证插件深坑":为什么复制用户更容易连不上
3.1 8.0默认认证插件的变化
MySQL 5.7及之前版本,默认认证插件是mysql_native_password,密码以哈希形式存储,客户端连接时直接做一次SHA1挑战-响应验证,整个握手过程可以完全走明文TCP,不需要额外的加密通道。
从8.0开始,默认认证插件改成了caching_sha2_password。这个插件安全性更强,但代价是:当连接没有走SSL/TLS时,首次认证需要额外一次RSA公钥交换来加密传输密码。如果客户端不知道服务器RSA公钥,并且连接参数里没指定GET_MASTER_PUBLIC_KEY=1或--get-server-public-key,认证就会失败。
你可以在主库上用SQL确认一下默认认证插件:
SHOW VARIABLES LIKE 'default_authentication_plugin';在8.0.27及之前版本,参数名是default_authentication_plugin,8.0.28开始改名为authentication_policy,用法略有差别:
SHOW VARIABLES LIKE 'authentication_policy';如果显示caching_sha2_password,那就说明你建复制账号时如果没有显式指定认证插件,建出来的用户用的就是它。
3.2 三种解法对比
解决caching_sha2_password导致的复制连接失败,业内实际用下来有三条路,各有优劣:
方案一:创建复制用户时显式指定mysql_native_password
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED WITH mysql_native_password BY 'your_password'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';这是最省事的办法,5.7时代的账号怎么建,现在照搬就行。缺点是这个插件在8.0里已标记为废弃,8.4里默认不启用,未来升级时还得改。如果你短期内不升级大版本,这是最稳妥的。
方案二:保持8.0的默认插件,在CHANGE MASTER TO里加公钥获取参数
8.0.23及之后版本的CHANGE MASTER TO支持GET_MASTER_PUBLIC_KEY=1:
CHANGE MASTER TO MASTER_HOST='192.168.1.10', MASTER_USER='repl', MASTER_PASSWORD='your_password', MASTER_PORT=3306, GET_MASTER_PUBLIC_KEY=1, MASTER_LOG_FILE='binlog.000003', MASTER_LOG_POS=157;这样从库在认证前会主动向主库请求RSA公钥,完成密码加密传输。优点是不需要动账号插件,安全等级更高;缺点是RSA公钥交换增加了握手开销,但复制连接是长连接,只在重连时才认证一次,日常开销可以忽略。
方案三:给主库配置SSL并让复制走加密连接
在主库的my.cnf里配置证书,然后在CHANGE MASTER TO里加MASTER_SSL=1:
CHANGE MASTER TO MASTER_HOST='192.168.1.10', MASTER_USER='repl', MASTER_PASSWORD='your_password', MASTER_PORT=3306, MASTER_SSL=1, MASTER_LOG_FILE='binlog.000003', MASTER_LOG_POS=157;这方案最安全,但需要证书体系,内网环境一般没必要额外折腾。
我个人建议:内网环境图省心用方案一,云环境或者安全审计比较严格的环境用方案二。注意,方案二要求两端都是8.0,如果主库是5.7从库是8.0,GET_MASTER_PUBLIC_KEY对5.7主库的mysql_native_password用户无效,这时候直接用方案一就好。
3.3 手工验证认证链路是否打通
不管用哪个方案,配置完之后,先在从库机器上手工连一次主库,直接验证认证链路,比反复看主从状态快得多:
mysql -urepl -p'your_password' -h192.168.1.10 -P3306 --get-server-public-key如果能正常登录并且执行SHOW DATABASES;有输出,说明账号、密码、权限、认证方式全部没问题。如果报错,错误信息会直接告诉你是认证插件问题还是账号问题。这个验证步骤我每次都做,能省掉大量排查时间。
4. 完整的排查链路:从报错发生到复制恢复
4.1 第一步:拿到完整报错,确认报错段落
在从库执行:
SHOW REPLICA STATUS\G8.0.22以前版本用SHOW SLAVE STATUS\G,从8.0.22开始官方把"Slave"替换成了"Replica",两种写法在8.0里兼容,但我建议直接用新写法。重点关注这几行:
Slave_IO_Running: Connecting Slave_SQL_Running: Yes Last_IO_Errno: 2061 Last_IO_Error: error connecting to master 'repl@192.168.1.10:3306' - retry-time: 60 retries: 1 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.Last_IO_Errno是错误码,2059通常跟认证插件有关,2003是连不上网络,1045是访问被拒。先用错误码缩小范围。
4.2 第二步:手工模拟从库连接,区分网络层与认证层
拿到完整报错后,不要急着改配置,先在从库机器上手工执行:
mysql -urepl -p'your_password' -h192.168.1.10 -P3306 --connect-timeout=5这个命令把TCP连接和MySQL认证一起测了。结果分三种情况:
能登录,说明网络通、账号通。问题大概率出在复制链路的配置细节上,比如
CHANGE MASTER TO里密码打错、没有配置GET_MASTER_PUBLIC_KEY等。报登录被拒,说明账号相关。去主库检查用户是否存在、host白名单是否覆盖了从库IP、密码是否正确。
连不上或超时,说明网络相关。检查防火墙、
bind_address、主库端口监听状态。
4.3 第三步:排查账号与host白名单
在主库执行:
SELECT user, host, plugin FROM mysql.user WHERE user = 'repl';确认三点:
user是否拼写正确;host是否覆盖了从库的来源IP(不是从库主机名,而是握手时看到的IP);plugin是不是caching_sha2_password,如果是,结合第3节的方案处理。
再检查权限:
SHOW GRANTS FOR 'repl'@'192.168.1.%';必须有REPLICATION SLAVE权限,少了这个权限在部分版本上会报Access denied。
顺带说一个容易踩的坑:如果你在创建用户时写的是'repl'@'%',但从库连接时MySQL会按匹配规则先找更精确的条目,如果有一个'repl'@'192.168.1.%'存在而密码不一致,实际用的密码和你想的不一样。这种"隐藏账号"问题最气人,查的时候记得把所有host条目都看一遍。
4.4 第四步:检查主库网络监听与防火墙
这一步适合手工连接超时或报2003错误时做。
在主库确认MySQL监听地址和端口:
SHOW VARIABLES LIKE 'bind_address'; SHOW VARIABLES LIKE 'port';如果bind_address是127.0.0.1,那非本机连接全部进不来。改成0.0.0.0或具体网卡IP,并确认配置文件里没有别的覆盖项,然后重启MySQL:
systemctl restart mysqld确认端口在监听:
ss -lntp | grep 3306正常会看到类似0.0.0.0:3306或具体IP:3306的监听记录。
然后是防火墙,Linux上分两种:
firewall-cmd --list-all如果firewalld在运行且没放行3306:
firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload如果你用的是云服务器,还得检查安全组是否放行了3306。任何一层拦截都会表现出"连接超时"或"Connection refused"。
还有skip_name_resolve这个变量,开启后MySQL不会对客户端IP做反向DNS解析,账号的host字段必须用IP而不能用主机名。检查方式:
SHOW VARIABLES LIKE 'skip_name_resolve';开启状态下,如果从库账号的host是'repl'@'db-slave'这种主机名写法,连接会被拒。改成IP段通配。
4.5 第五步:看日志,别只盯着一行报错
从库错误日志的位置:
tail -100 /var/log/mysql/error.log不同安装方式路径不一样,源码编译默认在数据目录下(通常是/usr/local/mysql/data/里的*.err)。从库日志里通常只记一两条关键信息,真正有价值的有时在主库日志里。如果主库的log_error_verbosity设得够高(2或3),认证失败记录会很详细。
把两边日志的时间戳对齐看,基本能还原完整链路。这套"从库报错+手工连接+主库查账号+两边日志交叉验证"的路径,我每次排查主从问题都是这么走的,从来没失手过。
5. 重建主从复制的一份可复现操作清单
5.1 建复制账号的正确姿势
连接主库执行:
CREATE USER IF NOT EXISTS 'repl'@'192.168.1.%' IDENTIFIED WITH mysql_native_password BY 'StrongPass@2024'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%'; FLUSH PRIVILEGES;注意一点:GRANT REPLICATION SLAVE只需要这个权限就能拉取binlog,不需要给整个库的读权限。网上有些教程让你直接GRANT ALL,这是坏习惯,复制账号权限越小越好。host段写成从库网段(如192.168.1.%),比写'%'安全得多。
5.2 从库的正确配置顺序
在主库执行SHOW MASTER STATUS;(8.4里改为SHOW BINARY LOG STATUS;),拿到当前的binlog文件和位置:
+------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | binlog.000003 | 157 | | | | +------------------+----------+--------------+------------------+-------------------+如果你用GTID模式,位置号其实不太关键,但我见过不少用GTID还是会把Position写错的情况。稳妥起见,Position照抄。
然后在从库执行:
STOP REPLICA; CHANGE MASTER TO MASTER_HOST='192.168.1.10', MASTER_USER='repl', MASTER_PASSWORD='StrongPass@2024', MASTER_PORT=3306, MASTER_LOG_FILE='binlog.000003', MASTER_LOG_POS=157, GET_MASTER_PUBLIC_KEY=1; START REPLICA;STOP REPLICA这步不要省,哪怕刚初始化完的从库也要执行一次,避免前一次实验残留配置。
5.3 验证主从状态各字段的含义
执行完START REPLICA后,等两三秒再查状态:
SHOW REPLICA STATUS\G关键看这几个字段:
Slave_IO_Running: Yes Slave_SQL_Running: Yes Seconds_Behind_Master: 0 Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error:Slave_IO_Running: Yes表示I/O线程连上了主库并且正在拉binlog;Slave_SQL_Running: Yes表示SQL线程在正常执行中继日志;Seconds_Behind_Master: 0表示没有延迟。任何一个不是预期值,把对应的Last_*_Errno和Last_*_Error贴到搜索引擎都比问人快。
注意,Slave_IO_Running刚执行完START REPLICA时可能短暂显示Connecting,如果几秒后变成Yes就正常。如果一直是Connecting,回到第4节的排查链路重新走一遍。
5.4 数据一致性校验
复制链路正常不代表数据没问题。建议在从库执行:
SELECT COUNT(*) FROM your_table;再在主库执行同样的SQL对比,先粗查几张大表。更严谨的做法是用pt-table-checksum做全量校验,但一般业务先比行数和关键表就够用了。
6. 除了认证插件,还有几个"隐形坑"容易被忽略
6.1 server_uuid重复导致复制异常
克隆虚拟机或从模板恢复实例时,MySQL数据目录下的auto.cnf会带着原来的server_uuid。如果主从两个实例的server_uuid一样,I/O线程连接主库成功后也会被立刻断开,而且报错信息可能被解读成网络问题。
检查方法:
SHOW VARIABLES LIKE 'server_uuid';两边对比,一样就删掉从库数据目录下的auto.cnf(先停从库),重启MySQL后会自动生成新的UUID。
6.2 从库server_id没改或重复
如果两台机器都是从同一个模板克隆的,my.cnf里的server_id大概率一样。MySQL主从要求每个实例的server_id全局唯一。检查:
SHOW VARIABLES LIKE 'server_id';修改my.cnf后重启。这个坑和上面server_uuid的区别是:server_uuid在auto.cnf里,server_id在my.cnf里,两者都要检查。
6.3 8.0.23之后的CHANGE MASTER TO语法变化
8.0.23开始,官方把MASTER_*参数改名为SOURCE_*,比如MASTER_HOST对应SOURCE_HOST,MASTER_LOG_FILE对应SOURCE_LOG_FILE。旧的MASTER_*写法在8.0里还能用,但日志里会有一条warning,到了8.4及以后旧语法被移除。新项目直接用新语法:
CHANGE REPLICATION SOURCE TO SOURCE_HOST='192.168.1.10', SOURCE_USER='repl', SOURCE_PASSWORD='StrongPass@2024', SOURCE_PORT=3306, SOURCE_LOG_FILE='binlog.000003', SOURCE_LOG_POS=157, GET_SOURCE_PUBLIC_KEY=1;对应的启停命令也变成了START REPLICA和STOP REPLICA。这些改动不影响5.7老经验迁移,但新环境尽量用新语法写配置。
6.4 主从版本差异过大
MySQL的复制协议大体向后兼容,但8.0和5.7混搭时,有些特性(比如默认字符集、排序规则、JSON实现)会导致SQL线程异常。如果你是从5.7升级到8.0再搭的主从,建议主从版本保持一致,至少在同一个大版本内。像开头搜索词里那种"Django要求8.4但环境只有8.0"的场景,说明业务侧对版本有明确要求,先解决版本匹配,再谈复制。
6.5 从库MySQL服务没起来就开始配置
这种低级错误反而最常见。systemctl status mysqld先确认从库MySQL进程活着,再执行任何复制相关命令。否则CHANGE MASTER TO本身可能成功,START REPLICA也会执行,但状态全是NULL,查半天才发现服务根本没起来。
7. 谈谈我踩过几次坑之后的固定排错顺序
这条例报错我前前后后帮人排查过不下二十次,自己也踩过两回。第一次遇到caching_sha2_password报错时,我第一反应也是去查网络,折腾了半小时才反应过来是8.0的认证机制变化。后来学乖了,只要遇到Authentication plugin字样,先往认证方向想。
我现在搭主从的固定顺序是这样的:先在主库建好复制账号,顺手查一下账号的plugin字段;然后在从库机器上手工mysql -urepl -p... -h...连一次,确认网络和认证都没问题;确认通了再执行CHANGE MASTER TO和START REPLICA;最后看一眼SHOW REPLICA STATUS确认两个线程都显示Yes。
这个顺序看着简单,但它把问题按概率从高到低排列了,每一步都在快速缩小排查范围。手工连接那一步尤其重要,它能一次性区分"能不能连上"和"配没配对"这两个完全不同的层面。
另外一个小建议:复制账号的密码里尽量避免使用@、#、空格这些特殊字符,不是不能用,而是在mysql -p'密码'这种命令行场景下容易踩shell转义的坑。真要用,记得单引号包好,或者用MYSQL_PWD环境变量临时指定(注意这招在本机有安全风险,用完记得unset MYSQL_PWD)。
MySQL 8.0的报错信息已经比5.7时代友好不少,但DBA的排查思路还是要成体系。把这篇文章里的排查链路走一遍,绝大多数I/O线程连接失败问题都能在10分钟内定位到根因。别看到报错就重置复制配置重来,先读懂错误码,再动手,效率高得多。