基于 Docker 快速构建 MySQL InnoDB Cluster 高可用集群与 Router 读写分离

集群规划

三台服务器 的相同路径下(如 /data/mysql-cluster)建立如下结构:

bash 复制代码
mkdir -p /data/mysql-cluster/{conf,scripts}
cd /data/mysql-cluster

结构如下:

复制代码
/data/mysql-cluster/
├── docker-compose.yml
├── conf/
│   └── my.cnf
├── mysql_data/       # 启动后自动生成,无需手动创建
└── scripts/          # 存放脚本(仅服务器 A 需要存放具体脚本文件)
    ├── reboot_cluster.js
    └── check_health.js

节点概述

节点IP 主机名 运行服务
172.16.0.101 mgr-node1 Mysql、Router
172.16.0.102 mgr-node2 Mysql、Router
172.16.0.103 mgr-node3 Mysql、Router

Mysql配置文件

mgr-node1

ini 复制代码
cd /data/mysql-cluster
cat > conf/my.cnf << EOF
[client]
port = 3306
socket = /var/log/mysql/mysqld.sock

[mysqld]
# ==============================================================================
# 1. 基础与网络配置 (Basic & Network)
# ==============================================================================
user = mysql
port = 3306
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/log/mysql/mysqld.sock
pid-file = /var/log/mysql/mysqld.pid
log_error = /var/log/mysql/error.log
slow_query_log_file = /var/log/mysql/slow.log

character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
authentication_policy = mysql_native_password
autocommit = 1

max_connections = 1024
max_user_connections = 1024
back_log = 512

# ==============================================================================
# 2. 内存与缓存调优 (Memory & Buffer Pool)
# ==============================================================================
innodb_buffer_pool_size = 4G
innodb_buffer_pool_instances = 4
innodb_buffer_pool_load_at_startup = 1
innodb_buffer_pool_dump_at_shutdown = 1

# ==============================================================================
# 3. 磁盘 I/O 与日志调优 (I/O & Logging)
# ==============================================================================
log_bin = bilog
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
binlog_checksum = CRC32
binlog_expire_logs_seconds = 604800

replica_parallel_type = LOGICAL_CLOCK
replica_parallel_workers = 8
replica_preserve_commit_order = ON

innodb_log_buffer_size = 16M
innodb_redo_log_capacity = 1G
innodb_page_cleaners = 4

# ==============================================================================
# 4. MGR & 复制核心配置 (MGR & Replication)
# ==============================================================================
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
log_replica_updates = ON
binlog_transaction_dependency_tracking = WRITESET

plugin_load_add='group_replication.so'
loose-group_replication_group_name = "8f6d7d5e-1b7d-4d3a-9c6e-2f4b8a7c91de"
loose-group_replication_local_address = "mgr-node1:33061"
loose-group_replication_group_seeds = "mgr-node1:33061,mgr-node2:33061,mgr-node3:33061"
loose-group_replication_single_primary_mode = OFF    ##在单主模式中为ON,在多主模式中为OFF
loose-group_replication_start_on_boot = OFF #默认关闭, 防止重启后自动创建新的Group
loose-group_replication_bootstrap_group = OFF #默认关闭, 防止自动创建新的Group
EOF

mgr-node2

ini 复制代码
cd /data/mysql-cluster
cat > conf/my.cnf << EOF
[client]
port = 3306
socket = /var/log/mysql/mysqld.sock

[mysqld]
# ==============================================================================
# 1. 基础与网络配置 (Basic & Network)
# ==============================================================================
user = mysql
port = 3306
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/log/mysql/mysqld.sock
pid-file = /var/log/mysql/mysqld.pid
log_error = /var/log/mysql/error.log
slow_query_log_file = /var/log/mysql/slow.log

character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
authentication_policy = mysql_native_password
autocommit = 1

max_connections = 1024
max_user_connections = 1024
back_log = 512

# ==============================================================================
# 2. 内存与缓存调优 (Memory & Buffer Pool)
# ==============================================================================
innodb_buffer_pool_size = 4G
innodb_buffer_pool_instances = 4
innodb_buffer_pool_load_at_startup = 1
innodb_buffer_pool_dump_at_shutdown = 1

# ==============================================================================
# 3. 磁盘 I/O 与日志调优 (I/O & Logging)
# ==============================================================================
log_bin = bilog
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
binlog_checksum = CRC32
binlog_expire_logs_seconds = 604800

replica_parallel_type = LOGICAL_CLOCK
replica_parallel_workers = 8
replica_preserve_commit_order = ON

innodb_log_buffer_size = 16M
innodb_redo_log_capacity = 1G
innodb_page_cleaners = 4

# ==============================================================================
# 4. MGR & 复制核心配置 (MGR & Replication)
# ==============================================================================
server_id = 2
gtid_mode = ON
enforce_gtid_consistency = ON
log_replica_updates = ON
binlog_transaction_dependency_tracking = WRITESET

plugin_load_add='group_replication.so'
loose-group_replication_group_name = "8f6d7d5e-1b7d-4d3a-9c6e-2f4b8a7c91de"
loose-group_replication_local_address = "mgr-node2:33061"
loose-group_replication_group_seeds = "mgr-node1:33061,mgr-node2:33061,mgr-node3:33061"
loose-group_replication_single_primary_mode = OFF    ##在单主模式中为ON,在多主模式中为OFF
loose-group_replication_start_on_boot = OFF #默认关闭, 防止重启后自动创建新的Group
loose-group_replication_bootstrap_group = OFF #默认关闭, 防止自动创建新的Group
EOF

mgr-node3

ini 复制代码
cd /data/mysql-cluster
cat > conf/my.cnf << EOF
[client]
port = 3306
socket = /var/log/mysql/mysqld.sock

[mysqld]
# ==============================================================================
# 1. 基础与网络配置 (Basic & Network)
# ==============================================================================
user = mysql
port = 3306
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/log/mysql/mysqld.sock
pid-file = /var/log/mysql/mysqld.pid
log_error = /var/log/mysql/error.log
slow_query_log_file = /var/log/mysql/slow.log

character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
authentication_policy = mysql_native_password
autocommit = 1

max_connections = 1024
max_user_connections = 1024
back_log = 512

# ==============================================================================
# 2. 内存与缓存调优 (Memory & Buffer Pool)
# ==============================================================================
innodb_buffer_pool_size = 4G
innodb_buffer_pool_instances = 4
innodb_buffer_pool_load_at_startup = 1
innodb_buffer_pool_dump_at_shutdown = 1

# ==============================================================================
# 3. 磁盘 I/O 与日志调优 (I/O & Logging)
# ==============================================================================
log_bin = bilog
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
binlog_checksum = CRC32
binlog_expire_logs_seconds = 604800

replica_parallel_type = LOGICAL_CLOCK
replica_parallel_workers = 8
replica_preserve_commit_order = ON

innodb_log_buffer_size = 16M
innodb_redo_log_capacity = 1G
innodb_page_cleaners = 4

# ==============================================================================
# 4. MGR & 复制核心配置 (MGR & Replication)
# ==============================================================================
server_id = 3
gtid_mode = ON
enforce_gtid_consistency = ON
log_replica_updates = ON
binlog_transaction_dependency_tracking = WRITESET

plugin_load_add='group_replication.so'
loose-group_replication_group_name = "8f6d7d5e-1b7d-4d3a-9c6e-2f4b8a7c91de"
loose-group_replication_local_address = "mgr-node3:33061"
loose-group_replication_group_seeds = "mgr-node1:33061,mgr-node2:33061,mgr-node3:33061"
loose-group_replication_single_primary_mode = OFF    ##在单主模式中为ON,在多主模式中为OFF
loose-group_replication_start_on_boot = OFF #默认关闭, 防止重启后自动创建新的Group
loose-group_replication_bootstrap_group = OFF #默认关闭, 防止自动创建新的Group
EOF

Docker编排文件

  • 数据全部采用同级 ./mysql_data 相对路径挂载。
  • 部分系统启动报错, 需要设置目录权限(MySQL 容器内部通常会使用 uid:gid = 999:999)。

mgr-node1

yaml 复制代码
export node1=172.16.0.101
export node2=172.16.0.102
export node3=172.16.0.103
export root_pwd="mysq1&R00t_Passw0rd!"

cd /data/mysql-cluster
cat > docker-compose.yml << EOF
services:
  mgr-node1:
    image: mysql:8.0
    container_name: mgr-node1
    hostname: mgr-node1
    restart: always
    extra_hosts:
      - "mgr-node1:${node1}"
      - "mgr-node2:${node2}"
      - "mgr-node3:${node3}"
    environment:
      MYSQL_ROOT_PASSWORD: "${root_pwd}"
      MYSQL_ROOT_HOST: "%"
    ports:
      - "3306:3306"
      - "33061:33061"
    volumes:
      - ./conf/my.cnf:/etc/mysql/my.cnf
      - ./mysql_data/data:/var/lib/mysql
      - ./mysql_data/logs:/var/log/mysql
      - ./scripts:/scripts

  mysql-router1:
    image: mysql/mysql-router:8.0
    container_name: mysql-router1
    restart: always
    extra_hosts:
      - "mgr-node1:${node1}"
      - "mgr-node2:${node2}"
      - "mgr-node3:${node3}"
    ports:
      - "6446:6446"
      - "6447:6447"
    environment:
      MYSQL_HOST: "mgr-node1"  # 数据库的 IP 或容器名
      MYSQL_PORT: "3306"
      MYSQL_USER: "root"
      MYSQL_PASSWORD: "${root_pwd}"
      MYSQL_INNODB_CLUSTER_MEMBERS: "3"
      
      MYSQL_NEVER_FAIL_ROUTER_BOOTSTRAP: "true"
      MYSQL_ROUTER_BOOTSTRAP_HOST: "mgr-node1"
      MYSQL_ROUTER_BOOTSTRAP_USER: "root"
      MYSQL_ROUTER_BOOTSTRAP_PASSWORD: "${root_pwd}"
      MYSQL_CREATE_ROUTER_USER: "root"
EOF

mgr-node2

yaml 复制代码
export node1=172.16.0.101
export node2=172.16.0.102
export node3=172.16.0.103
export root_pwd="mysq1&R00t_Passw0rd!"

cd /data/mysql-cluster
cat > docker-compose.yml << EOF
services:
  mgr-node2:
    image: mysql:8.0
    container_name: mgr-node2
    hostname: mgr-node2
    restart: always
    extra_hosts:
      - "mgr-node1:${node1}"
      - "mgr-node2:${node2}"
      - "mgr-node3:${node3}"
    environment:
      MYSQL_ROOT_PASSWORD: "${root_pwd}"
      MYSQL_ROOT_HOST: "%"
    ports:
      - "3306:3306"
      - "33061:33061"
    volumes:
      - ./conf/my.cnf:/etc/mysql/my.cnf
      - ./mysql_data/data:/var/lib/mysql
      - ./mysql_data/logs:/var/log/mysql
      
  mysql-router2:
    image: mysql/mysql-router:8.0
    container_name: mysql-router2
    restart: always
    extra_hosts:
      - "mgr-node1:${node1}"
      - "mgr-node2:${node2}"
      - "mgr-node3:${node3}"
    ports:
      - "6446:6446"
      - "6447:6447"
    environment:
      MYSQL_HOST: "mgr-node1"  # 数据库的 IP 或容器名
      MYSQL_PORT: "3306"
      MYSQL_USER: "root"
      MYSQL_PASSWORD: "${root_pwd}"
      MYSQL_INNODB_CLUSTER_MEMBERS: "3"
      
      MYSQL_NEVER_FAIL_ROUTER_BOOTSTRAP: "true"
      MYSQL_ROUTER_BOOTSTRAP_HOST: "mgr-node1"
      MYSQL_ROUTER_BOOTSTRAP_USER: "root"
      MYSQL_ROUTER_BOOTSTRAP_PASSWORD: "${root_pwd}"
      MYSQL_CREATE_ROUTER_USER: "root"
EOF

mgr-node3

yaml 复制代码
export node1=172.16.0.101
export node2=172.16.0.102
export node3=172.16.0.103
export root_pwd="mysq1&R00t_Passw0rd!"

cd /data/mysql-cluster
cat > docker-compose.yml << EOF
services:
  mgr-node3:
    image: mysql:8.0
    container_name: mgr-node3
    hostname: mgr-node3
    restart: always
    extra_hosts:
      - "mgr-node1:${node1}"
      - "mgr-node2:${node2}"
      - "mgr-node3:${node3}"
    environment:
      MYSQL_ROOT_PASSWORD: "${root_pwd}"
      MYSQL_ROOT_HOST: "%"
    ports:
      - "3306:3306"
      - "33061:33061"
    volumes:
      - ./conf/my.cnf:/etc/mysql/my.cnf
      - ./mysql_data/data:/var/lib/mysql
      - ./mysql_data/logs:/var/log/mysql
      
  mysql-router3:
    image: mysql/mysql-router:8.0
    container_name: mysql-router3
    hostname: "${mgr-node1}-route"
    restart: always
    extra_hosts:
      - "mgr-node1:${node1}"
      - "mgr-node2:${node2}"
      - "mgr-node3:${node3}"
    ports:
      - "6446:6446"
      - "6447:6447"
    environment:
      MYSQL_HOST: "mgr-node1"  # 数据库的 IP 或容器名
      MYSQL_PORT: "3306"
      MYSQL_USER: "root"
      MYSQL_PASSWORD: "${root_pwd}"
      MYSQL_INNODB_CLUSTER_MEMBERS: "3"
      
      MYSQL_NEVER_FAIL_ROUTER_BOOTSTRAP: "true"
      MYSQL_ROUTER_BOOTSTRAP_HOST: "mgr-node1"
      MYSQL_ROUTER_BOOTSTRAP_USER: "root"
      MYSQL_ROUTER_BOOTSTRAP_PASSWORD: "${root_pwd}"
      MYSQL_CREATE_ROUTER_USER: "root"
EOF

管理集群

创建集群

  • mgr-node1执行
javascript 复制代码
// 登录
docker-compose exec mgr-node1 /bin/bash
mysqlsh root@mgr-node1 --js

// 状态检测
dba.checkInstanceConfiguration('root@mgr-node1:3306')
dba.checkInstanceConfiguration('root@mgr-node2:3306')
dba.checkInstanceConfiguration('root@mgr-node3:3306')

// 初始化实例
dba.configureInstance('root@mgr-node1:3306')
dba.configureInstance('root@mgr-node2:3306')
dba.configureInstance('root@mgr-node3:3306')

// 进入主节点创建集群, 创建一个 cluster,命名为 'mysql-cluster'	
var cluster = dba.createCluster('mysql-cluster');
// 创建成功后,查看cluster状态
cluster.status();

// 实例加入集群
cluster.addInstance('root@mgr-node2:3306');
cluster.addInstance('root@mgr-node3:3306');
// 查看cluster状态
cluster.status();

常用排错命令

bash 复制代码
# 查看id是否重名
show variables like 'server_id';
# 查看group_replication插件是否开启
show plugins;
# 查看group_replication配置细节
show variables like 'group_replication%';

运维集群

javascript 复制代码
docker-compose exec mgr-node1 /bin/bash
mysqlsh root@mgr-node1 --js

// 重启集群
dba.rebootClusterFromCompleteOutage('mysql-cluster');  

// 连接集群
var cluster = dba.getCluster('mysql-cluster');

//查看集群状态
cluster.status(); 

自动化脚本

  • 在经常操作的节点的 /opt/mysql-cluster/scripts/编写l两个 JavaScript 脚本和一个shell脚本。
  • 之前脚本之前在容器内连过集群, 保存过session

重启集群-js

JavaScript 复制代码
cd /data/mysql-cluster
cat > scripts/reboot_cluster.js << EOF
try {
    // 1. 重启处于完全停机状态的集群
    // 注意:这里会尝试通过当前连接的节点重新引导集群
    print("正在尝试重启集群 'mysql-cluster'...");
    dba.rebootClusterFromCompleteOutage('mysql-cluster');
    print("重启指令执行完毕。\n");

    // 2. 获取集群对象
    // 重启后需要重新获取该对象,否则后续 status() 可能会报错
    var cluster = dba.getCluster('mysql-cluster');

    // 3. 打印集群状态
    if (cluster) {
        print("--- [当前集群状态] ---");
        // 必须使用 print() 才能在非交互模式下看到输出
        print(cluster.status());
    } else {
        print("错误:重启后无法获取集群对象。");
    }

} catch (e) {
    // 捕捉错误并打印,防止脚本静默退出
    print("执行过程中发生错误: " + e.message);
}
EOF

集群状态-sj

JavaScript 复制代码
cd /data/mysql-cluster
cat > scripts/check_health.js << EOF
// 1. 获取集群对象
var cluster = dba.getCluster('mysql-cluster');

// 2. 检查集群是否存在
if (cluster) {
    print("\n--- [集群当前状态] ---\n");
    print(cluster.status()); 
    print("\n----------------------\n");
} else {
    print("错误:找不到指定的集群!");
}
EOF

执行脚本-shell

bash 复制代码
cd /data/mysql-cluster
cat > cluster.sh << 'EOF'
#!/bin/bash

# 获取当前脚本所在目录
BASE_DIR=$(cd "$(dirname "$1")"; pwd)
SHELL_CONTAINER="mgr-node1"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

function check_container() {
    if ! docker ps | grep -q "$SHELL_CONTAINER"; then
        echo -e "${RED}错误: 容器 $SHELL_CONTAINER 未启动,请先执行 docker compose up -d${NC}"
        exit 1
    fi
}

function do_start() {
    echo -e "${YELLOW}正在尝试恢复/启动已有的集群...${NC}"
    docker exec -it "$SHELL_CONTAINER" mysqlsh --file /scripts/reboot_cluster.js
}

function do_status() {
    echo -e "${GREEN}--- 当前集群状态巡检 ---${NC}"
    # 这里直接调用 check_health.js 脚本
    docker exec -it "$SHELL_CONTAINER" mysqlsh --file /scripts/check_health.js
}

# 参数逻辑判断
ACTION=${1:-status} # 如果没有参数,默认为 status

case "$ACTION" in
    init)
        check_container
        do_init
        ;;
    start)
        check_container
        do_start
        ;;
    status)
        check_container
        do_status
        ;;
    *)
        echo -e "${RED}用法: $0 {init|start|status}${NC}"
        echo -e "  start  : 重启恢复集群使用"
        echo -e "  status : (默认) 查看当前集群健康状态"
        exit 1
        ;;
esac
EOF

使用脚本

bash 复制代码
# 启动集群
cd /data/mysql-cluster
./cluster.sh start

# 查看集群状态
cd /data/mysql-cluster
./cluster.sh
# 或者
./cluster.sh status

mysqlsh常用命令

复制代码
#会列出dba相关指令
dba.help();
#列出详细指令的用法
dba.help('deploySandboxInstance');
#检查节点配置实例,用于加入cluster之前
dba.checkInstanceConfiguration("root@hostname:3306");  
#节点初始化
dba.configureInstance('root@hostname:3306'); 
#重启集群
dba.rebootClusterFromCompleteOutage('myCluster');  
#会列出集群相关指令
cluster.help();       
#创建集群
var cluster = dba.createCluster('myCluster');                              
#获取当前集群实例
var cluster = dba.getCluster('myCluster');   
查看集群状态
cluster.status();             
#检查cluster节点状态 
cluster.checkInstanceState("root@hostname:3306") ;                             
#增加节点 
cluster.addInstance("root@hostname:3306") ;               
#删除节点 
cluster.removeInstance("root@hostname:3306") ;            
#强制删除节点
cluster.removeInstance('root@hostname:3306',{force:true});    
#  状态为missing的节点可以重新加入集群 
cluster.rejoinInstance("root@hostname:3306")
#解散集群 
cluster.dissolve({force:true}) ;                          
#集群描述 
cluster.describe();
相关推荐
Full Stack Developme11 小时前
SQL like 与 正则 区别
数据库·sql·mysql
秋漓11 小时前
Docker学习与使用
学习·docker·容器
%KT%11 小时前
Windows安装wsl2和docker desktop,部署qdrant向量数据库
运维·docker·容器
PythonAI实战君11 小时前
Docker Compose 部署 MySQL 中文乱码避坑指南
docker·容器
PythonAI实战君11 小时前
若依后台管理系统 - Docker Compose 阿里云部署指南
后端·docker
我是一颗柠檬11 小时前
【MySQL全面教学】MySQL多表查询与JOIN Day6(2026年)
数据库·后端·sql·mysql
小匠石钧知11 小时前
01_以RockyLinux的镜像为基础_构建自己开发学习所需的镜像
linux·docker·jdk·mariadb
今天背单词了吗98012 小时前
MySQL InnoDB引擎八大核心特性详解(高频面试题)
java·数据库·mysql
我也不曾来过112 小时前
MYSQL 使用C语言链接
数据库·mysql