Files
agent-skills/skills/g3fo-docs/references/middleware/mysql/deploy.md
T

12 KiB
Raw Blame History

MySQL 8.4 双主(Source-Source)同步部署文档(跨主机统一目录/容器名版)

AI 响应规范 (MySQL 执行策略):

  1. 强制参数: 所有 docker exec 命令必须包含 -h127.0.0.1 参数。
  2. 双重输出: 涉及 SQL 操作时,必须同时输出 Docker 执行命令 和 纯 SQL 脚本。

文档概述

你需要部署跨两台独立主机的 MySQL 8.4 双主双向复制集群,核心要求是:两台主机的容器名、目录路径统一使用 mysql(仅通过 IP 区分节点),保证两节点数据一致性,支持初始化、校验、宕机重启恢复及节点重建全流程。核心设计遵循:

  • GTID + AUTO_POSITION:重启后自动定位同步位点,减少人工干预;
  • ROW 模式 binlog:避免非确定性函数导致的数据不一致;
  • 持久化数据卷:独立 Volume 保障容器重启数据不丢失;
  • 自增键隔离:通过步长/偏移配置避免双写主键冲突。

1. 环境准备

1.1 节点信息(核心区分点)

节点 主机IP server_id auto_increment_offset 核心标识
主节点A ${NODE_A_IP} 1 1 生成主键1、3、5…
主节点B ${NODE_B_IP} 2 2 生成主键2、4、6…

1.2 系统依赖(两台主机统一执行)

# 安装 Docker & Docker Compose、MySQL 客户端
sudo apt update && sudo apt install -y docker.io docker-compose-plugin mysql-client
# 验证依赖(确保版本正常)
docker --version && docker compose version && mysql --version

1.3 目录规划(两台主机完全一致)

# 两台主机均执行以下命令,目录统一为 /data/mysql
sudo mkdir -p /data/mysql/{data,backup,logs}
sudo chown 999:999 /data/mysql -R  # MySQL 容器默认UID/GID为999,避免权限问题

2. 双主节点部署

2.1 Docker Compose 配置(两台主机完全一致)

创建 /data/mysql/docker-compose.yml,内容如下(无节点差异):

version: '3.8'
networks:
  g3fo:
    driver: bridge

services:
  mysql:
    image: mysql:8.4.7
    container_name: mysql  # 两台主机容器名统一为mysql
    restart: always
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    ports:
      - 3306:3306  # 端口统一,通过主机IP区分节点
    networks:
      - g3fo
    volumes:
      - /data/mysql/data:/var/lib/mysql
      - /data/mysql/backup:/backup
      - /data/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
      - /data/mysql/logs:/var/log/mysql
      - /etc/localtime:/etc/localtime
      - /etc/timezone:/etc/timezone
    environment:
      - TZ=Asia/Hong_Kong
      - MYSQL_ROOT_PASSWORD=afe123456  # 两台主机统一密码,生产建议修改
    privileged: true  # 保障文件权限操作

2.2 MySQL 配置文件(my.cnf)

节点A(${NODE_A_IP}):/data/mysql/my.cnf

[mysqld]
# ========== 节点唯一标识(核心区分点) ==========
server_id=1                # 全局唯一,不可与节点B重复
report_host=${NODE_A_IP}  # 当前主机IP,便于复制状态识别
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

# ========== 基础配置(两台主机一致) ==========
character-set-server=utf8mb4
max_connections=1000
skip_name_resolve=ON       # 关闭DNS解析,提升连接效率

# ========== 复制核心配置(两台主机一致) ==========
log_bin=mysql-bin                  # 开启binlog
binlog_format=ROW                  # 强制ROW模式,保证复制一致性
binlog_row_image=FULL              # 记录完整行数据
binlog_expire_logs_seconds=604800  # 7天自动清理binlog
gtid_mode=ON                       # 开启GTID模式
enforce_gtid_consistency=ON        # 强制GTID事务一致性
log_slave_updates=ON               # 从库同步的事务写入自身binlog(双向复制必需)
skip_slave_start=OFF               # 重启自动启动复制线程
relay_log_recovery=ON              # 重启自动恢复中继日志
max_binlog_size=1G                 # 单个binlog最大1G

# ========== 数据可靠性(两台主机一致) ==========
innodb_flush_log_at_trx_commit=1   # 事务提交立即刷盘(ACID)
sync_binlog=1                      # 每次事务同步binlog到磁盘

# ========== 双主自增键隔离(核心区分点) ==========
auto_increment_increment=2         # 自增步长2(两台主机一致)
auto_increment_offset=1            # 节点A起始值1(生成1、3、5…)

# ========== 日志配置(两台主机一致) ==========
log_error = /var/log/mysql/mysql_error.log  # 错误日志
log_error_verbosity = 2                    # 记录错误+警告
slow_query_log = 1                         # 开启慢查询日志
slow_query_log_file = /var/log/mysql/mysql_slow.log
long_query_time = 2                        # 慢查询阈值2秒

# ========== 读写权限(两台主机一致) ==========
read_only=OFF
super_read_only=OFF

节点B(${NODE_B_IP}):/data/mysql/my.cnf

[mysqld]
# ========== 节点唯一标识(核心区分点) ==========
server_id=2                # 与节点A区分
report_host=${NODE_B_IP}  # 当前主机IP
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

# ========== 基础配置(与节点A一致) ==========
character-set-server=utf8mb4
max_connections=1000
skip_name_resolve=ON

# ========== 复制核心配置(与节点A一致) ==========
log_bin=mysql-bin
binlog_format=ROW
binlog_row_image=FULL
binlog_expire_logs_seconds=604800
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
skip_slave_start=OFF
relay_log_recovery=ON
max_binlog_size=1G

# ========== 数据可靠性(与节点A一致) ==========
innodb_flush_log_at_trx_commit=1
sync_binlog=1

# ========== 双主自增键隔离(核心区分点) ==========
auto_increment_increment=2
auto_increment_offset=2            # 节点B起始值2(生成2、4、6…)

# ========== 日志配置(与节点A一致) ==========
log_error = /var/log/mysql/mysql_error.log
log_error_verbosity = 2
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql_slow.log
long_query_time = 2

# ========== 读写权限(与节点A一致) ==========
read_only=OFF
super_read_only=OFF

2.3 启动容器(两台主机统一执行)

# 进入目录并启动容器
cd /data/mysql && docker compose up -d

# 验证启动状态(确保容器状态为Up)
docker ps | grep mysql

3. 双主复制初始化

3.1 创建复制专用用户(两台主机统一执行)

# 无需区分节点,直接执行(创建repl用户,允许跨主机访问)
docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "
CREATE USER 'repl'@'%' IDENTIFIED BY 'afe123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
"

3.2 配置双向复制(核心:仅IP参数不同)

步骤1:节点A(${NODE_A_IP})配置为节点B的从库

docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "
STOP REPLICA;
RESET REPLICA ALL;
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST = '${NODE_B_IP}',  # 节点B的IP(核心区分点)
  SOURCE_PORT = 3306,
  SOURCE_USER = 'repl',
  SOURCE_PASSWORD = 'afe123456',
  SOURCE_AUTO_POSITION = 1,       # GTID自动定位(无需手动找位点)
  SOURCE_CONNECT_RETRY = 10,
  SOURCE_RETRY_COUNT = 86400,
  GET_SOURCE_PUBLIC_KEY = 1;      # 适配MySQL 8.0+密码认证
START REPLICA;
"

步骤2:节点B(${NODE_B_IP})配置为节点A的从库

docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "
STOP REPLICA;
RESET REPLICA ALL;
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST = '${NODE_A_IP}',  # 节点A的IP(核心区分点)
  SOURCE_PORT = 3306,
  SOURCE_USER = 'repl',
  SOURCE_PASSWORD = 'afe123456',
  SOURCE_AUTO_POSITION = 1,
  SOURCE_CONNECT_RETRY = 10,
  SOURCE_RETRY_COUNT = 86400,
  GET_SOURCE_PUBLIC_KEY = 1;
START REPLICA;
"

4. 同步状态校验

4.1 核心校验命令(两台主机统一执行)

A. Docker 执行模式 (包含 -h127.0.0.1):

# 查看复制状态
docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "SHOW REPLICA STATUS\G"

B. 纯 SQL 模式:

SHOW REPLICA STATUS\G

4.2 关键校验字段(必须全部满足)

字段 目标值 说明
Replica_IO_Running Yes IO线程正常(接收binlog)
Replica_SQL_Running Yes SQL线程正常(执行事务)
Last_SQL_Error 空 无同步错误
Seconds_Behind_Source 0 无同步延迟
Retrieved_Gtid_Set 非空 已获取对端节点GTID
Executed_Gtid_Set 包含Retrieved_Gtid_Set 已执行所有获取的事务

4.3 数据一致性验证

# 节点A(${NODE_A_IP})创建测试数据
docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "
CREATE DATABASE IF NOT EXISTS test_sync;
USE test_sync;
CREATE TABLE IF NOT EXISTS t1 (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));
INSERT INTO t1 (name) VALUES ('nodeA_test');
"

# 节点B(${NODE_B_IP})验证同步(应能查到nodeA_test数据)
docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "SELECT * FROM test_sync.t1;"

# 节点B插入数据,节点A验证同步
docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "INSERT INTO test_sync.t1 (name) VALUES ('nodeB_test');"
docker exec -it mysql mysql -uroot -pafe123456 -h${NODE_A_IP} -e "SELECT * FROM test_sync.t1;"

5. 故障处理流程

5.1 宕机重启恢复

场景:容器/主机重启后复制未自动恢复(两台主机操作逻辑一致,仅IP不同)

# 以节点A(${NODE_A_IP})为例,恢复与节点B的复制关系
docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "
STOP REPLICA;
# 重新指定复制源(利用GTID自动定位,无需手动找位点)
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST = '${NODE_B_IP}',  # 对端节点IP
  SOURCE_PORT = 3306,
  SOURCE_USER = 'repl',
  SOURCE_PASSWORD = 'afe123456',
  SOURCE_AUTO_POSITION = 1,
  GET_SOURCE_PUBLIC_KEY = 1;
START REPLICA;
# 验证状态
SHOW REPLICA STATUS\G;
"

# 节点B执行时,仅需将SOURCE_HOST改为${NODE_A_IP}即可

5.2 重建节点(单节点数据损坏/丢失)

步骤1:从正常节点全量备份(假设节点B损坏,从节点A备份)

# 节点A(${NODE_A_IP})执行备份
docker exec -it mysql mysqldump -uroot -pafe123456 -h127.0.0.1 --all-databases --master-data=2 --single-transaction > /data/mysql/backup/full_backup.sql

# 将备份文件复制到损坏的节点B(${NODE_B_IP})
scp /data/mysql/backup/full_backup.sql root@${NODE_B_IP}:/data/mysql/backup/

步骤2:停止损坏节点并恢复数据(节点B执行)

# 停止mysql容器
cd /data/mysql && docker compose down

# 清空损坏数据(保留目录结构)
sudo rm -rf /data/mysql/data/*

# 重启mysql容器(初始化空库)
docker compose up -d

# 导入备份数据
docker exec -i mysql mysql -uroot -pafe123456 < /data/mysql/backup/full_backup.sql

步骤3:重新配置双向复制

参考「3.2 配置双向复制」,重新执行节点B作为节点A从库、节点A作为节点B从库的配置命令(仅IP参数区分)。

5.3 复制冲突解决

详细的冲突定位与修复流程,请参考:MySQL 复制冲突解决指南

6. 日志管理

详细的日志轮转与安全登录配置,请参考:MySQL 日志管理指南

7. 重要提醒

7.1 核心区分点(避免配置错误)

  • 两台主机仅 server_id、report_host(IP)、auto_increment_offset 三个参数不同,其余配置完全一致;
  • 容器名、目录路径、端口均统一为 mysql//data/mysql/3306,通过主机IP区分节点。

7.2 一致性限制

  • MySQL原生异步双主复制无法保证严格的数据一致性,双写场景仍可能出现冲突;
  • 生产环境建议单写多读(指定一个节点为写节点,另一个为读节点),避免双写冲突。

总结

跨主机双主复制的核心差异仅为 server_id、主机IP、自增偏移量,其余目录/容器名/配置可完全统一;

同步故障优先通过错误日志定位冲突,修复数据而非跳过事务(避免永久不一致);

GTID模式下无需手动定位同步位点,重启/重建节点时仅需重新指定对端IP即可自动恢复复制。