
MySQL主从集群这套东西网上教程一大堆但多数要么只讲原理不带实操要么直接给你一串docker命令跑完就完事主从到底是怎么连上的、binlog是怎么同步的、报错怎么排查全凭自己摸索。这篇我直接用一次完整的Docker实战部署过程串起来从原理说到落地把每一步为什么这么干讲清楚也把我在实际部署中踩过的坑一并交代照着做基本能一次跑通。1. 内容整体设计与思路拆解1.1 为什么用“主从集群”而不是其他方案先说为什么需要主从复制。单机MySQL最直接的痛点是所有读写压力都压在同一台实例上一旦这台机器磁盘坏了、进程崩了、机房断电业务直接停摆。MySQL主从复制的核心价值就两条高可用和读写分离。高可用好理解——主库挂掉之后从库可以立刻顶上数据不丢至少异步模式下主库已提交的binlog都同步过去了。读写分离则更实用主库负责写入从库负责查询把并发读的压力分散到多台机器上。尤其典型场景是报表业务、订单查询这类读多写少的接口一台主库扛不住大量并发Select加两台从库瞬间就把读能力扩上去了。再往后演进还有半同步复制、全同步复制、组复制MGR、InnoDB Cluster但那些都是在“经典主从复制”基础上的增强或替代方案。从零到一先把经典异步主从搞明白后面再迁移到任何高可用方案都顺理成章。用Docker部署而不是直接在宿主机上装两个MySQL考虑也很直接Docker可以把两个MySQL实例隔离在两个容器里环境互不污染端口随便映射删了重建也就是几秒钟的事尤其适合本地开发环境、测试环境快速搭建也适合在家里一台性能普通的电脑上模拟生产拓扑。1.2 整体架构设计一主一从起步这次实战的架构是最经典的一主一从Master节点容器名mysql-master宿主机端口3306对外提供读写服务Slave节点容器名mysql-slave宿主机端口3307从主库拉取binlog并回放数据库层面的逻辑是Master开启binlog生成一个用于复制账号的专用用户比如repl记录当前binlog的文件名和position偏移量Slave拿到Master的IP、端口、复制账号、binlog文件名和position之后执行CHANGE MASTER TO然后START SLAVE。从这一刻起Master上产生的每一个数据变更都会以binlog事件的形式流到Slave上被Slave的IO线程写进自己的relay log中继日志再由SQL线程一条一条回放到本地。生产环境一般还要在Slave上设置read_only1防止应用误连从库写入数据导致主从数据不一致。本地实验可以不开但养成好习惯没坏处。两个容器放在同一个Docker自定义网络里我后面用的网络名是mysql-net容器之间通过容器名互访比用IP省心得多。1.3 为什么选择“binlog POS”模式而不是GTIDMySQL主从复制有两种主流定位方式传统的binlog文件名 position偏移量以及MySQL 5.6之后引入的GTID全局事务标识符。这次实战我用的还是传统的File Position方式原因很实在这个方式最直观也最容易理解主从复制的底层机制。你通过SHOW MASTER STATUS能看到当前写到哪个binlog文件、偏移量多少然后把这个值告诉SlaveSlave就从那个点开始拉数据逻辑链路清清楚楚。GTID也不是不用生产环境我强烈建议优先考虑GTID它的好处是主从切换时不用手动找position故障恢复更容易复制关系维护起来不用关心binlog文件名。但GTID对binlog格式、事务要求更严格比如不能用CREATE TABLE ... SELECT这种非事务性语句新手上手容易踩坑。所以我建议顺序是先用File POS跑通理解机制再切GTID这样出了问题你也知道它底层在干什么。2. 核心细节解析与实操要点2.1 MySQL主从复制的完整链路一次讲透主从复制的核心链路可以用一句话概括Master把数据变更写进binlogSlave拉取binlog写入relay log再重放到自己的数据库里。再拆细一点就是六个环节应用在Master上执行写操作比如INSERT、UPDATE、DELETE。InnoDB引擎层把数据变更记录到binlog前提是Master开启了log_bin。Slave上有一个IO线程持续不断地连接Master请求从指定binlog文件和偏移量开始的所有binlog事件。Master端为每个Slave连接分配一个binlog dump线程负责把binlog事件推送给Slave。Slave的IO线程把收到的binlog事件写到自己的relay log中继日志里。Slave的SQL线程读取relay log按顺序把事件重放到本地存储引擎。这里有两个线程需要额外多说几句IO线程和SQL线程。IO线程是“搬运工”只管把主库的binlog搬到从库的relay log这个过程很轻量不涉及数据解析SQL线程才是“执行者”照着relay log一条条执行SQL真正把数据落到从库。所以在SHOW SLAVE STATUS里你会看到两个关键状态Slave_IO_Running和Slave_SQL_Running两个都得是Yes主从才算正常工作。binlog的格式也值得搞清楚。MySQL支持三种STATEMENT记录SQL语句本身、ROW记录每一行数据的前后镜像、MIXED混合模式。生产环境我强烈建议用ROW格式。STATEMENT模式虽然日志量小但遇到NOW()、UUID()这类函数主从执行结果可能会不一致ROW格式虽然日志量大一些但每一行的变更都记录得清清楚楚主从一致性最有保障。MySQL 8.0默认就是ROW这也是为什么8.0在主从复制上比5.7省心很多。2.2 binlog到底记录了什么为什么它这么重要binlog是MySQL的二进制日志文件记录的是所有引起数据变化的操作比如INSERT、UPDATE、DELETE、CREATE TABLE、ALTER TABLE等SELECT查询不会写进binlog。这跟InnoDB的redo log不一样redo log是物理日志记录“数据页第几页第几个偏移量改成了什么”InnoDB崩溃恢复靠它binlog是逻辑日志记录的是“哪条语句或者哪一行怎么变的”主从复制和数据恢复靠它。做个不恰当的类比redo log像是装修公司的施工记录写的是“客厅第三面墙刷了两次白漆”物理层面精确到位置binlog像是监理的验收报告写的是“客厅墙面完成白色涂料施工”逻辑层面描述发生了什么。MySQL的主从复制正是基于这份“验收报告”在另一套房子上重新施工一遍。binlog还有一个重要作用误删数据后的时间点恢复。比如你凌晨3点误删了一张表假如你有凌晨2点的全量备份加上从2点到3点的binlog就能把数据恢复到3点之前的状态。这也是为什么即使不做主从生产MySQL也一定要开binlog的原因之一。2.3 主从复制的三种同步模式异步、半同步、全同步MySQL默认的复制是异步的Master提交事务后立即返回给客户端成功不等待Slave确认收到binlog。好处是主库性能损耗几乎为零坏处也明显——主库写完就崩binlog还没来得及传给从库从库顶上时就会丢数据。半同步复制Semisynchronous Replication稍微严谨一点Master提交事务时必须至少等到一个Slave确认已经收到binlog并写入了relay log才返回客户端成功。这样主库崩溃时至少有一个从库不丢数据。代价是每次写操作多一次网络往返延迟性能略有下降。全同步复制则是所有从库都确认收到并回放完主库才返回成功。一致性最强但性能最差实际上MySQL原生并没有真正的全同步复制MGR组复制的Single-Primary模式其实就是一种改进的强一致方案不过这是后话。本次实战用的是默认的异步复制因为Docker本地实验不需要那么高的数据一致性要求。理解这三个模式的差异生产环境选型时就知道该往哪个方向调。2.4 Docker部署的核心注意事项用Docker部署MySQL主从有几个坑是特别容易踩的第一容器里的MySQL要设置server-id而且必须唯一。主从各自的server-id不能一样否则Slave连接Master之后复制会直接报错。这个配置项是写在MySQL配置文件里的Docker启动时可以用-v把宿主机上的配置文件挂载进容器也可以直接用命令行参数传。第二容器内的MySQL不要轻易做端口映射冲突。宿主机3306已经被Master占了Slave就必须换一个端口比如3307。但容器内部两个MySQL都是默认3306端口互不冲突因为各自在各自的网络命名空间里。这里不少新手会疑惑同一个宿主机上跑两个MySQL容器都是3306怎么不冲突因为Docker的网络隔离容器内3306是容器自己的端口宿主机3306和3307才是真正暴露给外部的端口。第三要建立一个Docker自定义网络。默认的bridge网络虽然容器间也能通信但IP是动态分配的重启容器之后IP可能变。创建一个mysql-net这样的自定义网络容器之间直接使用容器名通信稳定可靠。后面连接主从复制时Slave只需要访问mysql-master:3306就行了。第四权限问题。MySQL 8.0默认认证插件是caching_sha2_password从库连接主库时会要求RSA公钥交换这在Docker网络里可能会因为SSL/TLS配置问题导致连接失败。稳妥的做法是主库创建复制账号时明确指定mysql_native_password插件。我后面实操里会给出具体的SQL语句。3. 实操过程与核心环节实现3.1 环境准备拉镜像、建网络、准备配置目录我的环境是Windows 11 Docker DesktopWSL2后端Linux服务器上的操作基本一致只是不需要处理Docker Desktop那些GUI设置。先确认Docker已经正常运行。打开终端执行docker --version我这边显示的是Docker version 24.0.7没问题。接下来拉取MySQL 8.0镜像docker pull mysql:8.0不建议拉latest标签因为MySQL的latest版本更新太频繁不同小版本的默认配置和行为可能有差异。固定8.0这个主版本标签就够用了。创建自定义网络docker network create mysql-net这个网络在后续启动容器时直接指定两个容器就自动加入同一个隔离网络里了。网络建好之后看看主从各自需要的配置目录和文件。在宿主机上准备两个目录分别放Master和Slave的配置文件# Windows下用PowerShell执行Linux/macOS用mkdir -p mkdir -p D:/docker/mysql-master/conf mkdir -p D:/docker/mysql-slave/conf mkdir -p D:/docker/mysql-master/data mkdir -p D:/docker/mysql-slave/data目录结构大概是这样的D:/docker/ ├── mysql-master/ │ ├── conf/ │ │ └── my.cnf │ └── data/ └── mysql-slave/ ├── conf/ │ └── my.cnf └── data/data目录挂载进容器之后MySQL的数据文件会持久化到宿主机上。这样做的好处是即使容器被删除重建数据库文件还在数据不丢。3.2 Master配置与容器启动先写Master的配置文件。在D:/docker/mysql-master/conf/my.cnf里写入[mysqld] server-id1 log-binmysql-bin binlog_formatROW expire_logs_days7 max_binlog_size128M gtid_modeOFF enforce_gtid_consistencyOFF逐项解释一下server-id1主库的服务器唯一ID从库必须用不同的值。log-binmysql-bin开启binlog文件名前缀是mysql-bin。binlog_formatROWbinlog用行格式记录前面说过主从一致性最强。expire_logs_days7binlog保留7天避免日志无限增长把磁盘撑爆。MySQL 8.0里这个参数改名成binlog_expire_logs_seconds了但expire_logs_days仍然可用只是官方更推荐用秒数来精确控制。max_binlog_size128M单个binlog文件达到128M就滚动生成下一个文件默认值是1G调小一点方便观察binlog切换。gtid_modeOFF和enforce_gtid_consistencyOFF这次用FilePOS方式明确关掉GTID防止某些默认开启GTID的镜像干扰实验。配置文件写好后启动Master容器docker run -d \ --name mysql-master \ --network mysql-net \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123 \ -v D:/docker/mysql-master/conf/my.cnf:/etc/mysql/conf.d/my.cnf \ -v D:/docker/mysql-master/data:/var/lib/mysql \ mysql:8.0这里注意挂载路径/etc/mysql/conf.d/my.cnf是MySQL 8.0官方镜像默认读取的配置目录放在这里的配置会自动被加载不需要额外改/etc/mysql/my.cnf。端口-p 3306:3306映射后宿主机上的MySQL客户端就能通过127.0.0.1:3306访问主库了。容器启动需要一点时间第一次启动要初始化数据目录。等十几秒后检查容器状态docker ps看到mysql-master的STATUS是Uphealthy更好就说明启动成功了。如果这一步报错大概率是配置文件里写了不支持的参数或者data目录权限不对。Windows下一般不用管权限Linux下如果遇到Permission denied给目录加一下权限就行chmod -R 755 D:/docker/mysql-master3.3 Slave配置与容器启动接下来写Slave的配置文件在D:/docker/mysql-slave/conf/my.cnf里写入[mysqld] server-id2 log-binmysql-bin binlog_formatROW expire_logs_days7 max_binlog_size128M gtid_modeOFF enforce_gtid_consistencyOFF read_only1从库的配置跟主库很像但有两个关键区别server-id2必须和Master不同。read_only1让从库只接受主库发过来的写操作拒绝应用直接写入。注意这个参数对SUPER权限的用户不生效比如root仍然可以写所以如果真的想让从库彻底只读还需要配合super_read_only1一起设置。从库也开着log-bin不是必须的但我建议开。原因有几个从库的binlog可以作为下一次级联复制的数据源如果主库挂了你可以把从库提升为新主库这时候从库的binlog就是其他从库的数据源万一从库数据被破坏也能通过binlog做恢复。启动Slave容器docker run -d \ --name mysql-slave \ --network mysql-net \ -p 3307:3306 \ -e MYSQL_ROOT_PASSWORDroot456 \ -v D:/docker/mysql-slave/conf/my.cnf:/etc/mysql/conf.d/my.cnf \ -v D:/docker/mysql-slave/data:/var/lib/mysql \ mysql:8.0注意端口映射是-p 3307:3306宿主机访问从库用3307端口。两个容器的root密码我故意设成不同避免混淆。启动完成后同样确认docker ps里能看到mysql-slave处于Up状态。3.4 在主库创建复制账号并获取binlog坐标用宿主机上的MySQL客户端连接主库。如果宿主机没装MySQL客户端可以直接进容器里执行docker exec -it mysql-master mysql -uroot -proot123进入MySQL命令行后执行CREATE USER repl% IDENTIFIED WITH mysql_native_password BY repl123; GRANT REPLICATION SLAVE ON *.* TO repl%; FLUSH PRIVILEGES;这里重点说明一下repl账号的作用只用于主从复制不需要任何其他权限所以只授予REPLICATION SLAVE。%表示允许从任何主机连接Docker容器内部的IP是动态的用%最省事。IDENTIFIED WITH mysql_native_password这半句很重要。MySQL 8.0默认的认证插件是caching_sha2_password从库连接时如果主库要求RSA公钥传输Docker网络下可能因为SSL问题连接失败。用mysql_native_password插件可以兼容MySQL 5.7及之前的行为让从库连接更顺畅。注意MySQL 8.4版本开始mysql_native_password插件默认被移除了。如果你用的是8.0系列可以放心用如果已经升到8.4就需要改用caching_sha2_password并配置好SSL或者GET_MASTER_PUBLIC_KEY1。这篇文章基于稳定的mysql:8.0镜像。然后获取当前binlog坐标SHOW MASTER STATUS;输出大概是这样------------------------------------------------------------------------------- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | ------------------------------------------------------------------------------- | mysql-bin.000003 | 157 | | | | -------------------------------------------------------------------------------记下这两个值mysql-bin.000003和157。这就是从库要开始拉取的位置起点。为啥是这个位置因为创建repl账号、改权限这类操作也会写binlog但这些操作不需要同步到从库所以从库要从“创建完复制账号之后”的位置开始拉这个position就是那一刻的binlog偏移量。3.5 从库连接主库并启动复制在主库命令行里执行完上面的操作后退出进从库容器docker exec -it mysql-slave mysql -uroot -proot456在从库的MySQL命令行里执行把主库的binlog文件和position配置进去CHANGE MASTER TO MASTER_HOSTmysql-master, MASTER_PORT3306, MASTER_USERrepl, MASTER_PASSWORDrepl123, MASTER_LOG_FILEmysql-bin.000003, MASTER_LOG_POS157;参数含义拆开解释MASTER_HOSTmysql-master这里填的是主库的容器名。因为两个容器在同一个自定义网络mysql-net里Docker内置DNS会自动解析容器名到对应IP。MASTER_PORT3306主库容器内的MySQL监听端口默认就是3306。MASTER_LOG_FILE和MASTER_LOG_POS就是刚才SHOW MASTER STATUS输出的File和Position值。然后启动复制线程START SLAVE;查看复制状态SHOW SLAVE STATUS\G;重点关注这几行Slave_IO_Running: Yes Slave_SQL_Running: Yes Seconds_Behind_Master: 0Slave_IO_Running: Yes表示IO线程正在正常拉取binlogSlave_SQL_Running: Yes表示SQL线程正在正常回放relay logSeconds_Behind_Master: 0表示从库和主库之间没有延迟。三个都满足主从复制就算搭建完成了。如果Slave_IO_Running显示Connecting或者No先看Last_IO_Errno和Last_IO_Error字段最常见的错误是主库的repl账号密码不对、MASTER_LOG_POS位置不对或者两个容器的网络不通。排查看第4部分。3.6 验证主从复制是否真正生效在从库命令行里创建一个测试数据库CREATE DATABASE test_db;然后去主库命令行看看这个库是不是也出现了SHOW DATABASES;看到test_db已经在主库上了说明从库的DDL语句成功同步到了主库。这就验证了一个关键事实主从复制是双向流动的吗不是。从库写入的数据会同步到主库吗不会。复制是单向的只从主库流向从库。你在从库执行写操作主库完全不知道。这也是为什么生产环境要在从库开启read_only——如果不小心在从库写了数据主从数据就不一致了。再做一次更细的验证在主库的test_db里建一张表并插入数据USE test_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); INSERT INTO t_user (name) VALUES (zhangsan), (lisi), (wangwu);然后回到从库查询SELECT * FROM test_db.t_user;能查到zhangsan、lisi、wangwu这三条记录说明DML也同步成功了。如果LOCAL环境验证到这里就可以收工了。但我们在生产线上还有一个更关键的问题如何确认某一时刻之前主从数据完全一致这里可以用CHECKSUM TABLE比较两边表的校验值。两边分别执行CHECKSUM TABLE test_db.t_user;如果两边返回的checksum值一样说明表和数据的物理内容完全一致。3.7 切换binlog测试和重启容器验证为了验证复制不会因为binlog滚动而中断我在主库里多插入一些数据让binlog超过max_binlog_size产生切换USE test_db; INSERT INTO t_user (name) SELECT CONCAT(user, id) FROM t_user; -- 多执行几次让数据量涨上去执行完后再看主库的SHOW MASTER STATUSFile字段可能已经变成了mysql-bin.000004。这时再看从库状态Seconds_Behind_Master依然应该是0说明即使binlog文件切换了从库也能自动跟踪新文件继续同步。再测试一下容器重启后复制是否自动恢复。重启两个容器docker restart mysql-master mysql-slave重启完等几十秒重新进从库执行SHOW SLAVE STATUS\G正常情况下Slave_IO_Running和Slave_SQL_Running依然是Yes不需要手动重新执行START SLAVE。这里的原因是Slave的IO线程和SQL线程状态默认是ON的话MySQL进程启动时会自动恢复复制线程。如果重启之后变成了No多半是配置文件里的skip-slave-start参数被设置了去掉就行。4. 常见问题与排查技巧实录4.1 主从复制常见错误速查表这一部分是在实际部署中踩过坑之后的总结直接列成一个速查表方便大家遇到问题的时候对着看。错误现象可能原因解决方案Slave_IO_Running: Connecting主库repl账号密码错误、主库地址错误、网络不通检查Last_IO_Error用telnet mysql-master 3306测试连通性重新执行CHANGE MASTER TOSlave_IO_Running: No主库的binlog被清掉、position无效重新SHOW MASTER STATUS获取新坐标重新CHANGE MASTER TOSlave_SQL_Running: Norelay log中某条SQL在从库执行失败比如表已存在、字段不匹配查看Last_SQL_Error若确认错误无关紧要执行SET GLOBAL SQL_SLAVE_SKIP_COUNTER 1; START SLAVE;跳过复制延迟越来越大从库性能不足、主库写入压力大、网络带宽受限检查Seconds_Behind_Master考虑升级从库配置、使用并行复制Authentication plugin caching_sha2_password cannot be loaded从库MySQL版本太老不识别主库新插件主库重建repl账号指定mysql_native_password或者从库升级4.2 ERROR 2002 (HY000): Cant connect to local MySQL server这个错误字面意思是“通过socket文件/tmp/mysql.sock连接本地MySQL失败”出现的场景一般是宿主机上直接用mysql命令连容器里的MySQL。原因很简单宿主机上的mysql客户端默认走unix socket连接但MySQL根本不在宿主机本地而是在容器里。解决办法有两种显式指定TCP连接mysql -h127.0.0.1 -P3306 -uroot -proot123直接在容器里执行docker exec -it mysql-master mysql -uroot -proot123这个坑踩过的基本都是因为装了MySQL客户端但不知道连接方式我第一次在Windows上装Docker Desktop也遇到过。记住一个原则容器里的MySQL只能用TCP从外部访问进去容器内部才能用socket。4.3 docker exec 进不去容器或者中文乱码有时候docker exec -it mysql-master mysql -uroot -p会直接卡住或者报错这通常是因为容器没有分配tty或者Windows的PowerShell对-it参数处理有问题。可以改成docker exec -i mysql-master mysql -uroot -p去掉-t参数只保留-i保持交互式输入。中文乱码问题的根源是客户端字符集和服务器端字符集不一致。连接时指定字符集即可docker exec -it mysql-master mysql -uroot -proot123 --default-character-setutf8mb4另外MySQL 8.0的默认字符集已经是utf8mb4如果你建库的时候手动指定了latin1之类的就很容易出现中文显示成乱码的现象。建库的时候统一用utf8mb4。4.4 主库数据已经存在如何让从库先同步历史数据这是一类高频问题主库不是一个空库里面已经跑了很多业务数据这时候再搭建从库不能直接CHANGE MASTER TO因为从库当前是空的如果从当前binlog坐标开始同步那坐标之前的数据就丢失了。正确思路是先用备份工具把主库的全量数据导入从库让两边数据起点一致再从备份时间点开启复制。具体步骤是在主库执行FLUSH TABLES WITH READ LOCK锁定所有表防止备份期间数据改变。执行SHOW MASTER STATUS记录当前的binlog文件和position。用mysqldump导出全量数据docker exec mysql-master mysqldump -uroot -proot123 --all-databases --master-data2 backup.sql--master-data2会在备份文件头部自动写入CHANGE MASTER TO语句里面包含了主库当前的binlog坐标省得你手动记录。解锁主库表UNLOCK TABLES;把backup.sql导入从库docker exec -i mysql-slave mysql -uroot -proot456 backup.sql在从库执行备份文件里的CHANGE MASTER TO如果用了--master-data2的话也可以去掉注释直接执行START SLAVE验证状态。实际生产中我很少用mysqldump来做这个初始化因为数据量大时太慢。更常用的是xtrabackup做物理备份在从库直接恢复数据文件速度快一个量级。但本地实验、数据量不大时mysqldump完全够用。4.5 从库误写入导致主从数据不一致怎么修假设有人不小心在从库执行了一条INSERT主库没这条数据。这时候从库的SQL线程再去重放binlog里的同一条INSERT可能会因为主键冲突直接报错然后Slave_SQL_Running变成No。处理思路分几步先停止复制线程STOP SLAVE;对比主从数据定位多出来的那条记录。在从库删除误插入的数据DELETE FROM test_db.t_user WHERE idxxx;重新启动复制START SLAVE;如果数据不一致范围比较大手工清理不现实最稳妥的方法还是重新从主库做一次全量备份并恢复到从库也就是4.4节那套流程。这也说明一件事从库的read_only1不是摆设生产上一律要开。4.6 MySQL 8.0和5.7的差异提醒网上很多教程还在用MySQL 5.7的语法比如SET GLOBAL slow_query_log ON之类的这些在8.0里大部分还能用但有一些细微差别需要注意8.0默认的认证插件是caching_sha2_password老客户端可能连不上需要改mysql_native_password。8.0移除了query_cache相关的所有功能如果你照着老教程配置query_cache_type会直接报错。8.0对GROUP BY的检测更严格没启用ONLY_FULL_GROUP_BY之前可能没问题的SQL在8.0默认配置下会直接报错。回到主从复制上8.0的binlog_format默认就是ROWlog_bin默认开启这一点比5.7更友好。如果用5.7或更早版本记得手动检查这两个参数是否配置正确。4.7 Docker Desktop相关问题的排查思路这部分专门给Windows和macOS上用Docker Desktop的朋友。很多人卡在最前面Docker Desktop启动不了报Virtualization support not detected或者Docker Desktop failed to start because...。原因不外乎三个一是Windows的Hyper-V和虚拟机平台没有开启。到“启用或关闭Windows功能”里把“虚拟机平台”和“适用于Linux的Windows子系统”两个勾选上重启电脑后再试。二是WSL2内核没有更新。Docker Desktop依赖WSL2如果WSL内核太旧会启动失败。到微软官网下载最新的WSL2内核更新包装完再重启。三是Windows 10家庭版不支持Hyper-V。这种情况可以通过安装WSL2来绕开或者直接换Linux环境。说实话日常开发如果主要是用Docker我建议有条件的直接用Linux系统或者云服务器Windows上的Docker Desktop虽然这几年已经很成熟了但偶尔还是会有磁盘占用、文件挂载性能的别扭问题。另外Docker Desktop在Windows上挂载宿主目录时文件IO性能比Linux原生环境差不少如果MySQL数据量大、写入频繁建议把data目录放到Docker Desktop管理的数据卷里而不是直接挂载Windows文件系统目录。这算是一条本地开发的通用教训。5. 主从复制的进阶扩展思路主从复制搭建完成之后下一步通常沿着几个方向演进。第一个方向是读写分离中间件。应用层直接连主库和从库是不现实的因为主从之间的延迟可能让刚写入的数据查询不到。一般会引入中间件比如MySQL Router、ProxySQL、MyCat把写请求路由到主库把读请求负载均衡到多个从库。生产上我用得比较多的是ProxySQL配置灵活支持在线变更规则还有查询缓存、读写分离的细粒度控制。第二个方向是一主多从结构。从库不只有一台可以同时挂两台、三台甚至更多。当一个从库变成瓶颈时横向扩展从库的数量就能提升读能力。但也要注意每增加一个从库主库就多一个binlog dump线程占用一份主库的IO和网络资源。从库数量超过一定阈值时主库压力反而会成为瓶颈。第三个方向是级联复制。拓扑变成“主库 - 从库1 - 从库2”从库2不从主库拉binlog而是从从库1拉。这样可以减轻主库的压力但链路变长任何中间节点的故障都会影响下游节点。适合对延迟不敏感、读规模更大的场景。第四个方向是从普通复制升级到GTID。前面说过GTID的优势自动定位binlog坐标不用手动记录File和Position主从切换更简单。对于生产环境等到对复制机制足够熟悉的时候我建议把主从复制切换成GTID模式这会显著降低日常运维的心智负担。第五个方向是备份恢复的工程化。主从复制不等于备份。如果你把从库当作备份的唯一来源那么一旦发生误删操作误删的SQL会立刻同步到从库数据照样没保住。必须配合定时全量备份比如每天凌晨mysqldump或xtrabackup、binlog增量备份按小时备份binlog文件再加上一套恢复演练流程才是真正完整的备份方案。我自己吃过亏以前以为有从库就安全了结果研发误删了一张核心表从库一秒都没落下地也删了最后只能从冷备里恢复数据。从那以后我的备份策略就变成了“主从复制 全量备份 binlog增量备份”三件套。最后再分享一个实用小技巧主从复制搭建完之后日常巡检最常用的三个命令建议直接收藏-- 主库查看当前binlog坐标 SHOW MASTER STATUS; -- 从库查看复制状态 SHOW SLAVE STATUS\G; -- 从库查看relay log配置情况 SHOW VARIABLES LIKE relay_log%;每次巡检重点看三件事从库的Slave_IO_Running和Slave_SQL_Running是否都是YesSeconds_Behind_Master的值是否稳定在合理区间从库的binlog和relay log是否在正常滚动、没有堆积。只要这三项正常说明复制链路基本健康。另一个我实际工作中经常用的小技巧是给从库配置一个监控账号配合定时任务每分钟做一次SHOW SLAVE STATUS的检查发现Slave_SQL_Running为No就立刻告警。复制链路中断不可怕可怕的是中断了自己还不知道等发现问题时主从数据已经严重不一致了。自动化告警是真能救命的东西。