Windows 环境下使用 Docker 搭建 MySQL 8 一主两从指南

本文档基于 Windows + Docker Desktop + MySQL 8 官方镜像 的真实踩坑过程整理,
适合:后端开发 / 学习 MySQL 主从 / 本地模拟生产环境。


一、目标架构

  • 1 个主库(mysql-master)
  • 2 个从库(mysql-slave1 / mysql-slave2)
  • 使用 GTID + AUTO_POSITION
  • Docker Compose 一键启动
mysql-master   (server-id=1)
     │
     ├── mysql-slave1 (server-id=2)
     └── mysql-slave2 (server-id=3)

二、核心原则(非常重要)

Windows 下必须遵守的 3 条原则

  1. 不要挂载 conf.d(否则 MySQL 会忽略配置)
  2. MySQL 配置全部写在 command 里
  3. 修改配置后必须 docker compose down -v

原因:

  • Windows bind mount 的目录权限是 777
  • MySQL 8 会直接忽略 world-writable 的 cnf 文件
  • 导致:server_id=1gtid_mode=OFF 永远改不掉

三、完整 docker-compose.yml(最终可用版)

version: "3.8"

services:
  mysql-master:
    image: mysql:8.0
    container_name: mysql-master
    restart: always
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    command:
      [
        "mysqld",
        "--default-authentication-plugin=mysql_native_password",
        "--server-id=1",
        "--log-bin=mysql-bin",
        "--binlog-format=ROW",
        "--gtid-mode=ON",
        "--enforce-gtid-consistency=ON"
      ]
    volumes:
      - mysql-master-data:/var/lib/mysql
    networks:
      - mysql-net

  mysql-slave1:
    image: mysql:8.0
    container_name: mysql-slave1
    restart: always
    ports:
      - "3307:3306"
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    depends_on:
      - mysql-master
    command:
      [
        "mysqld",
        "--default-authentication-plugin=mysql_native_password",
        "--server-id=2",
        "--gtid-mode=ON",
        "--enforce-gtid-consistency=ON",
        "--relay-log=relay-bin",
        "--log-slave-updates=ON",
        "--read-only=ON"
      ]
    volumes:
      - mysql-slave1-data:/var/lib/mysql
    networks:
      - mysql-net

  mysql-slave2:
    image: mysql:8.0
    container_name: mysql-slave2
    restart: always
    ports:
      - "3308:3306"
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    depends_on:
      - mysql-master
    command:
      [
        "mysqld",
        "--default-authentication-plugin=mysql_native_password",
        "--server-id=3",
        "--gtid-mode=ON",
        "--enforce-gtid-consistency=ON",
        "--relay-log=relay-bin",
        "--log-slave-updates=ON",
        "--read-only=ON"
      ]
    volumes:
      - mysql-slave2-data:/var/lib/mysql
    networks:
      - mysql-net

networks:
  mysql-net:
    driver: bridge

volumes:
  mysql-master-data:
  mysql-slave1-data:
  mysql-slave2-data:

四、启动步骤(顺序不能错)

docker compose down -v
docker compose up -d

-v 必须加,否则 MySQL 会继续使用旧数据目录,配置不会生效

等待日志出现:

ready for connections

五、登录注意事项(MySQL 8 坑点)

错误方式

mysql -uroot -p123456

报错:

Access denied for user 'root'@'localhost'

正确方式(强制 TCP)

docker exec -it mysql-slave1 mysql -uroot -p123456 -h127.0.0.1

原因:

  • MySQL 8 官方镜像默认只有 root@'%'
  • socket 登录等于 root@localhost

六、验证基础配置

在 slave 上:

SHOW VARIABLES LIKE 'server_id';
SHOW VARIABLES LIKE 'gtid_mode';

期望结果:

server_id = 2/3
gtid_mode = ON

七、创建复制用户(主库)

CREATE USER IF NOT EXISTS 'repl'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;

八、配置从库复制(GTID)

在 slave1 / slave2:

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='mysql-master',
  SOURCE_USER='repl',
  SOURCE_PASSWORD='123456',
  SOURCE_AUTO_POSITION=1;

START REPLICA;

九、常见错误 & 解决方案汇总

1. ERROR 1200: The server is not configured as replica

原因

  • server_id 未生效
  • 配置文件被忽略

解决

  • 不挂载 conf.d
  • 使用 command
  • docker compose down -v

2. ERROR 1777: GTID_MODE = OFF

原因

  • gtid_mode 未真正生效

解决

  • 确认 command 中包含 --gtid-mode=ON
  • 删除 volume 重新启动

3. Replica_IO_Running: Connecting

原因

  • master 尚未 ready
  • 复制用户不存在

解决

STOP REPLICA;
RESET REPLICA ALL;
-- 重新 CHANGE REPLICATION SOURCE TO

4. conf.d 提示被忽略

World-writable config file ... is ignored

原因:Windows 挂载权限问题

解决:完全不用 conf.d


十、最终成功标志

SHOW REPLICA STATUS\G

看到:

Replica_IO_Running: Yes
Replica_SQL_Running: Yes

十一、实锤验证复制

主库:

CREATE DATABASE test_db;
USE test_db;
CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(20));
INSERT INTO t1 VALUES (1,'ok');

从库:

SELECT * FROM test_db.t1;

十二、总结

  • 这是 Windows Docker 下 MySQL 主从的正确姿势
  • 避开了 90% 网络教程的坑
  • 可直接作为:
    • 本地开发环境
    • 学习主从复制
    • 面试演示项目