ITPub博客

首页 > 应用开发 > IT综合 > MySQL主从集群部署指南:原理分析与Docker实践

MySQL主从集群部署指南:原理分析与Docker实践

投稿 IT综合 作者:bhluo 时间:2025-12-15 17:36:10 0 删除 编辑

# MySQL主从集群部署指南:原理分析与Docker实践


在现代数据库架构中,MySQL主从复制是实现高可用性和负载均衡的核心技术。通过在主数据库与从数据库之间建立复制关系,不仅能够提供数据冗余备份,还能有效分散读操作压力。本文将深入解析主从复制原理,并通过Docker容器化部署展示完整实现流程。


## 主从复制原理深度解析


MySQL主从复制基于二进制日志(Binary Log)实现,其核心流程包含三个关键阶段:


```sql

-- 主从复制核心流程示意

-- 1. 主库写入阶段

-- 主库将数据更改记录到二进制日志

-- 格式:Statement-Based(SQL语句)或 Row-Based(行数据)

-- 配置示例:

SHOW MASTER STATUS;

-- 输出:

-- +------------------+----------+--------------+------------------+

-- | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |

-- +------------------+----------+--------------+------------------+

-- | mysql-bin.000001 |      107 |              |                  |

-- +------------------+----------+--------------+------------------+


-- 2. 从库读取阶段

-- 从库I/O线程连接主库,读取二进制日志

-- 配置复制通道:

CHANGE MASTER TO

    MASTER_HOST='master_host',

    MASTER_USER='replica_user',

    MASTER_PASSWORD='password',

    MASTER_LOG_FILE='mysql-bin.000001',

    MASTER_LOG_POS=107;


-- 3. 从库应用阶段

-- 从库SQL线程读取中继日志并执行SQL

-- 查看复制状态:

SHOW SLAVE STATUS\G

-- 关键字段:

-- Slave_IO_Running: Yes    -- I/O线程状态

-- Slave_SQL_Running: Yes   -- SQL线程状态

-- Seconds_Behind_Master: 0 -- 复制延迟秒数

```


## Docker环境准备与网络配置


通过Docker搭建主从集群环境,确保网络连通性和数据持久化:


```yaml

# docker-compose-mysql-cluster.yaml

version: '3.8'


services:

  # MySQL主节点

  mysql-master:

    image: mysql:8.0

    container_name: mysql-master

    restart: unless-stopped

    environment:

      MYSQL_ROOT_PASSWORD: master_root_password

      MYSQL_REPLICATION_USER: replica_user

      MYSQL_REPLICATION_PASSWORD: replica_password

      MYSQL_DATABASE: app_db

      TZ: Asia/Shanghai

    ports:

      - "3306:3306"

    volumes:

      - ./master-data:/var/lib/mysql

      - ./master-config:/etc/mysql/conf.d

      - ./master-logs:/var/log/mysql

      - ./scripts:/docker-entrypoint-initdb.d

    networks:

      - mysql-cluster-net

    command: 

      - --server-id=1

      - --log-bin=mysql-bin

      - --binlog-format=ROW

      - --gtid-mode=ON

      - --enforce-gtid-consistency=ON

      - --log-slave-updates=ON

      - --binlog-do-db=app_db


  # MySQL从节点1

  mysql-slave-1:

    image: mysql:8.0

    container_name: mysql-slave-1

    restart: unless-stopped

    depends_on:

      - mysql-master

    environment:

      MYSQL_ROOT_PASSWORD: slave_root_password

      TZ: Asia/Shanghai

    ports:

      - "3307:3306"

    volumes:

      - ./slave1-data:/var/lib/mysql

      - ./slave1-config:/etc/mysql/conf.d

      - ./slave1-logs:/var/log/mysql

    networks:

      - mysql-cluster-net

    command:

      - --server-id=2

      - --relay-log=mysql-relay-bin

      - --read->

      - --super-read->


  # MySQL从节点2

  mysql-slave-2:

    image: mysql:8.0

    container_name: mysql-slave-2

    restart: unless-stopped

    depends_on:

      - mysql-master

    environment:

      MYSQL_ROOT_PASSWORD: slave_root_password

      TZ: Asia/Shanghai

    ports:

      - "3308:3306"

    volumes:

      - ./slave2-data:/var/lib/mysql

      - ./slave2-config:/etc/mysql/conf.d

      - ./slave2-logs:/var/log/mysql

    networks:

      - mysql-cluster-net

    command:

      - --server-id=3

      - --relay-log=mysql-relay-bin

      - --read->

      - --super-read->


  # 监控工具

  mysql-monitor:

    image: phpmyadmin/phpmyadmin

    container_name: mysql-monitor

    restart: unless-stopped

    depends_on:

      - mysql-master

      - mysql-slave-1

      - mysql-slave-2

    environment:

      PMA_ARBITRARY: 1

      PMA_HOST: mysql-master

    ports:

      - "8080:80"

    networks:

      - mysql-cluster-net


networks:

  mysql-cluster-net:

    driver: bridge

    ipam:

      config:

        - subnet: 172.20.0.0/24


volumes:

  master-data:

  slave1-data:

  slave2-data:

  master-config:

  slave1-config:

  slave2-config:

```


## 主库配置与初始化脚本


创建主库的初始化配置和用户权限设置:


```bash

#!/bin/bash

# init-master.sh - 主库初始化脚本


echo "开始初始化MySQL主库..."


# 等待MySQL服务启动

while ! mysqladmin ping -h"localhost" --silent; do

    echo "等待MySQL启动..."

    sleep 2

done


echo "MySQL主库已启动,开始配置复制..."


# 创建复制用户

mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" << EOF

-- 创建复制用户

CREATE USER 'replica_user'@'%' IDENTIFIED WITH mysql_native_password BY '${MYSQL_REPLICATION_PASSWORD}';

GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'replica_user'@'%';


-- 创建监控用户

CREATE USER 'monitor_user'@'%' IDENTIFIED BY 'monitor_password';

GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO 'monitor_user'@'%';


-- 创建应用用户

CREATE USER 'app_user'@'%' IDENTIFIED BY 'app_password';

GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'%';


-- 刷新权限

FLUSH PRIVILEGES;


-- 查看主库状态

SHOW MASTER STATUS\G

EOF


echo "主库初始化完成"


# 创建测试数据库和数据

mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" << EOF

-- 使用应用数据库

USE app_db;


-- 创建测试表

CREATE TABLE IF NOT EXISTS users (

    id INT AUTO_INCREMENT PRIMARY KEY,

    username VARCHAR(50) NOT NULL UNIQUE,

    email VARCHAR(100) NOT NULL,

    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_username (username),

    INDEX idx_email (email)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE TABLE IF NOT EXISTS orders (

    id INT AUTO_INCREMENT PRIMARY KEY,

    user_id INT NOT NULL,

    order_number VARCHAR(50) NOT NULL UNIQUE,

    amount DECIMAL(10,2) NOT NULL,

    status ENUM('pending', 'processing', 'completed', 'cancelled') DEFAULT 'pending',

    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,

    INDEX idx_user_id (user_id),

    INDEX idx_order_number (order_number),

    INDEX idx_status (status)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


-- 插入测试数据

INSERT INTO users (username, email) VALUES

('test_user1', 'user1@example.com'),

('test_user2', 'user2@example.com'),

('test_user3', 'user3@example.com');


INSERT INTO orders (user_id, order_number, amount, status) VALUES

(1, 'ORD001', 100.50, 'completed'),

(2, 'ORD002', 200.75, 'processing'),

(3, 'ORD003', 150.25, 'pending');

EOF


echo "测试数据创建完成"

```


## 从库配置与复制设置


配置从库并建立与主库的复制关系:


```bash

#!/bin/bash

# init-slave.sh - 从库初始化脚本


SLAVE_ID=$1

MASTER_HOST=$2


echo "开始初始化从库 ${SLAVE_ID},连接主库 ${MASTER_HOST}..."


# 等待从库MySQL服务启动

while ! mysqladmin ping -h"localhost" --silent; do

    echo "等待从库MySQL启动..."

    sleep 2

done


echo "从库MySQL已启动,开始配置复制..."


# 获取主库二进制日志位置

MASTER_INFO=$(mysql -h"${MASTER_HOST}" -ureplica_user -preplica_password -e "SHOW MASTER STATUS\G")


MASTER_LOG_FILE=$(echo "${MASTER_INFO}" | grep "File:" | awk '{print $2}')

MASTER_LOG_POS=$(echo "${MASTER_INFO}" | grep "Position:" | awk '{print $2}')


echo "主库二进制日志位置:${MASTER_LOG_FILE} @ ${MASTER_LOG_POS}"


# 配置从库复制

mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" << EOF

-- 停止复制(如果已启动)

STOP SLAVE;


-- 重置复制(清除现有配置)

RESET SLAVE ALL;


-- 配置主库连接

CHANGE MASTER TO

    MASTER_HOST='${MASTER_HOST}',

    MASTER_USER='replica_user',

    MASTER_PASSWORD='replica_password',

    MASTER_PORT=3306,

    MASTER_LOG_FILE='${MASTER_LOG_FILE}',

    MASTER_LOG_POS=${MASTER_LOG_POS},

    MASTER_CONNECT_RETRY=10,

    MASTER_RETRY_COUNT=86400,

    MASTER_HEARTBEAT_PERIOD=60,

    MASTER_SSL=0;


-- 启动复制

START SLAVE;


-- 查看复制状态

SHOW SLAVE STATUS\G

EOF


echo "从库 ${SLAVE_ID} 复制配置完成"


# 检查复制状态

sleep 5


mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" << EOF

SHOW SLAVE STATUS\G

EOF


echo "从库初始化完成"

```


## 自动化部署与管理脚本


创建完整的集群管理脚本:


```python

#!/usr/bin/env python3

# mysql_cluster_manager.py


import subprocess

import time

import json

import yaml

from datetime import datetime

from typing import Dict, List, Optional

import mysql.connector

from mysql.connector import Error


class MySQLClusterManager:

    def __init__(self, config_file='cluster-config.yaml'):

        self.config = self.load_config(config_file)

        self.cluster_state = {}

    

    def load_config(self, config_file):

        """加载集群配置"""

        with open(config_file, 'r') as f:

            config = yaml.safe_load(f)

        

        default_config = {

            'cluster': {

                'name': 'mysql-cluster',

                'network': 'mysql-cluster-net'

            },

            'master': {

                'host': 'mysql-master',

                'port': 3306,

                'root_password': 'master_root_password',

                'replica_user': 'replica_user',

                'replica_password': 'replica_password'

            },

            'slaves': [

                {

                    'name': 'mysql-slave-1',

                    'host': 'mysql-slave-1',

                    'port': 3306

                },

                {

                    'name': 'mysql-slave-2', 

                    'host': 'mysql-slave-2',

                    'port': 3306

                }

            ]

        }

        

        # 合并配置

        if config:

            default_config.update(config)

        

        return default_config

    

    def deploy_cluster(self):

        """部署整个集群"""

        print("开始部署MySQL主从集群...")

        

        # 1. 启动Docker容器

        print("启动Docker容器...")

        subprocess.run(['docker-compose', '-f', 'docker-compose-mysql-cluster.yaml', 'up', '-d'], 

                      check=True)

        

        # 2. 等待服务启动

        print("等待MySQL服务启动...")

        self.wait_for_mysql(self.config['master']['host'], self.config['master']['port'])

        

        # 3. 初始化主库

        print("初始化主库...")

        self.init_master()

        

        # 4. 初始化从库

        print("初始化从库...")

        for slave in self.config['slaves']:

            self.wait_for_mysql(slave['host'], slave['port'])

            self.init_slave(slave['host'])

        

        # 5. 验证集群状态

        print("验证集群状态...")

        cluster_status = self.check_cluster_status()

        

        print("\n=== 集群部署完成 ===")

        print(f"主库: {self.config['master']['host']}:{self.config['master']['port']}")

        print(f"从库数量: {len(self.config['slaves'])}")

        print(f"集群状态: {cluster_status['overall_status']}")

        

        return cluster_status

    

    def wait_for_mysql(self, host, port, timeout=120):

        """等待MySQL服务启动"""

        start_time = time.time()

        

        while time.time() - start_time < timeout:

            try:

                conn = mysql.connector.connect(

                    host=host,

                    port=port,

                    user='root',

                    password=self.config['master']['root_password']

                )jc.nexgo.vip\rf.nexgo.vip\qp.nexgo.vip\

                conn.close()

                print(f"✓ MySQL服务 {host}:{port} 已就绪")

                return True

            except Error:

                print(f"等待 {host}:{port} 启动...")

                time.sleep(2)

        

        raise TimeoutError(f"MySQL服务 {host}:{port} 启动超时")

    

    def init_master(self):

        """初始化主库配置"""

        master_config = self.config['master']

        

        try:

            conn = mysql.connector.connect(

                host=master_config['host'],

                port=master_config['port'],

                user='root',

                password=master_config['root_password']

            )

            

            cursor = conn.cursor()

            

            # 创建复制用户

            cursor.execute(f"""

                CREATE USER IF NOT EXISTS '{master_config['replica_user']}'@'%' 

                IDENTIFIED WITH mysql_native_password BY '{master_config['replica_password']}'

            """)

            

            cursor.execute(f"""

                GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* 

                TO '{master_config['replica_user']}'@'%'

            """)

            

            # 创建监控用户

            cursor.execute("""

                CREATE USER IF NOT EXISTS 'cluster_monitor'@'%' 

                IDENTIFIED BY 'monitor_password_123'

            """)

            

            cursor.execute("""

                GRANT SELECT, PROCESS, REPLICATION CLIENT, SHOW DATABASES 

                ON *.* TO 'cluster_monitor'@'%'

            """)

            

            conn.commit()

            cursor.close()

            conn.close()

            

            print("✓ 主库用户权限配置完成")

            

        except Error as e:

            print(f"主库初始化失败: {e}")

            raise

    

    def init_slave(self, slave_host):

        """初始化从库并配置复制"""

        master_config = self.config['master']

        

        # 获取主库二进制日志位置

        try:

            conn = mysql.connector.connect(

                host=master_config['host'],

                port=master_config['port'],

                user=master_config['replica_user'],

                password=master_config['replica_password']

            )

            

            cursor = conn.cursor(dictionary=True)

            cursor.execute("SHOW MASTER STATUS")

            master_status = cursor.fetchone()

            

            cursor.close()

            conn.close()

            

            if not master_status:

                raise Exception("无法获取主库二进制日志状态")

            

        except Error as e:

            print(f"获取主库状态失败: {e}")

            raise

        

        # 配置从库复制

        try:

            conn = mysql.connector.connect(

                host=slave_host,

                port=3306,

                user='root',

                password=self.config['master']['root_password']

            )

            

            cursor = conn.cursor()

            

            # 停止并重置现有复制

            cursor.execute("STOP SLAVE")

            cursor.execute("RESET SLAVE ALL")

            

            # 配置主库连接

            change_master_sql = f"""

            CHANGE MASTER TO

                MASTER_HOST='{master_config['host']}',

                MASTER_USER='{master_config['replica_user']}',

                MASTER_PASSWORD='{master_config['replica_password']}',

                MASTER_PORT={master_config['port']},

                MASTER_LOG_FILE='{master_status['File']}',

                MASTER_LOG_POS={master_status['Position']},

                MASTER_CONNECT_RETRY=10,

                MASTER_RETRY_COUNT=86400,

                MASTER_HEARTBEAT_PERIOD=60

            """

            

            cursor.execute(change_master_sql)

            

            # 启动复制

            cursor.execute("START SLAVE")

            

            conn.commit()

            cursor.close()

            conn.close()

            

            print(f"✓ 从库 {slave_host} 复制配置完成")

            

        except Error as e:

            print(f"从库 {slave_host} 配置失败: {e}")

            raise

    

    def check_cluster_status(self):

        """检查集群状态"""

        cluster_status = {

            'master': {},

            'slaves': [],

            'overall_status': 'healthy',

            'timestamp': datetime.now().isoformat()

        }

        

        # 检查主库状态

        try:

            conn = mysql.connector.connect(

                host=self.config['master']['host'],

                port=self.config['master']['port'],

                user='root',

                password=self.config['master']['root_password']

            )

            

            cursor = conn.cursor(dictionary=True)

            

            # 检查主库状态

            cursor.execute("SHOW MASTER STATUS")

            master_status = cursor.fetchone()

            

            cluster_status['master'] = {

                'host': self.config['master']['host'],

                'status': 'online',

                'binary_log': master_status['File'] if master_status else None,

                'position': master_status['Position'] if master_status else None

            }

            

            cursor.close()

            conn.close()

            

        except Error as e:

            cluster_status['master']['status'] = 'offline'

            cluster_status['master']['error'] = str(e)

            cluster_status['overall_status'] = 'degraded'

        

        # 检查从库状态

        for slave in self.config['slaves']:

            slave_status = {

                'name': slave['name'],

                'host': slave['host'],

                'status': 'unknown'

            }

            

            try:

                conn = mysql.connector.connect(

                    host=slave['host'],

                    port=slave['port'],

                    user='root',

                    password=self.config['master']['root_password']

                )

                

                cursor = conn.cursor(dictionary=True)

                

                # 检查从库复制状态

                cursor.execute("SHOW SLAVE STATUS")

                slave_info = cursor.fetchone()

                

                if slave_info:

                    slave_status.update({

                        'status': 'online',

                        'io_running': slave_info['Slave_IO_Running'],

                        'sql_running': slave_info['Slave_SQL_Running'],

                        'seconds_behind_master': slave_info['Seconds_Behind_Master'],

                        'last_error': slave_info['Last_Error'],

                        'master_host': slave_info['Master_Host'],

                        'master_log_file': slave_info['Master_Log_File'],

                        'read_master_log_pos': slave_info['Read_Master_Log_Pos']

                    })

                    

                    # 检查复制是否正常

                    if (slave_info['Slave_IO_Running'] != 'Yes' or 

                        slave_info['Slave_SQL_Running'] != 'Yes'):

                        slave_status['replication_status'] = 'error'

                        cluster_status['overall_status'] = 'degraded'

                    else:

                        slave_status['replication_status'] = 'healthy'

                

                cursor.close()

                conn.close()

                

            except Error as e:

                slave_status['status'] = 'offline'

                slave_status['error'] = str(e)

                cluster_status['overall_status'] = 'degraded'

            

            cluster_status['slaves'].append(slave_status)

        

        return cluster_status

    

    def promote_slave_to_master(self, slave_host):

        """将从库提升为主库(故障转移)"""

        print(f"开始将从库 {slave_host} 提升为主库...")

        

        # 1. 停止所有从库复制

        for slave in self.config['slaves']:

            if slave['host'] != slave_host:

                self.stop_replication(slave['host'])

        

        # 2. 停止目标从库复制并重置

        self.stop_replication(slave_host)

        

        try:

            conn = mysql.connector.connect(

                host=slave_host,

                port=3306,

                user='root',

                password=self.config['master']['root_password']

            )

            

            cursor = conn.cursor()

            

            # 重置从库身份

            cursor.execute("RESET SLAVE ALL")

            

            # 启用二进制日志

            cursor.execute("SET GLOBAL log_bin = ON")

            cursor.execute("SET GLOBAL binlog_format = 'ROW'")

            

            conn.commit()

            cursor.close()

            conn.close()

            

            print(f"✓ 从库 {slave_host} 已提升为主库")

            

            # 更新配置

            old_master = self.config['master']['host']

            self.config['master']['host'] = slave_host

            

            # 重新配置其他从库连接新的主库

            for slave in self.config['slaves']:

                if slave['host'] != slave_host:

                    self.init_slave(slave['host'])

            

            return {

                'success': True,

                'message': f'故障转移完成,新主库: {slave_host}',

                'old_master': old_master,

                'new_master': slave_host

            }

            

        except Error as e:

            return {

                'success': False,

                'message': f'故障转移失败: {e}'

            }


def main():

    manager = MySQLClusterManager('cluster-config.yaml')

    

    # 部署集群

    status = manager.deploy_cluster()

    

    # 打印状态报告

    print("\n集群状态报告:")

    print(json.dumps(status, indent=2, default=str))

    

    # 定时监控

    import schedule

    import time

    

    def monitor_job():

        status = manager.check_cluster_status()

        print(f"\n[{datetime.now()}] 集群监控检查:")

        print(f"  整体状态: {status['overall_status']}")

        print(f"  从库数量: {len(status['slaves'])}")

        

        for slave in status['slaves']:

            if slave.get('replication_status') == 'healthy':

                print(f"  ✓ {slave['name']}: 延迟 {slave.get('seconds_behind_master', 'N/A')}秒")

            else:

                print(f"  ✗ {slave['name']}: {slave.get('error', '复制异常')}")

    

    # 每5分钟监控一次

    schedule.every(5).minutes.do(monitor_job)

    

    print("\n集群监控已启动,按Ctrl+C退出...")

    

    while True:

        schedule.run_pending()

        time.sleep(1)


if __name__ == '__main__':

    main()

```


## 监控与维护脚本


创建集群监控和维护工具:


```bash

#!/bin/bash

# mysql_cluster_monitor.sh


# 集群监控脚本


MASTER_HOST="mysql-master"

MASTER_PORT=3306

MASTER_USER="cluster_monitor"

MASTER_PASSWORD="monitor_password_123"


SLAVES=("mysql-slave-1" "mysql-slave-2")

CHECK_INTERVAL=60  # 检查间隔(秒)

ALERT_THRESHOLD=30 # 复制延迟告警阈值(秒)


# 发送告警函数

send_alert() {

    local severity=$1

    local message=$2

    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')

    

    echo "[$timestamp] [$severity] $message"

    

    # 发送邮件告警(如果配置)

    # echo "$message" | mail -s "MySQL集群告警" admin@example.com

    

    # 发送到Slack(如果配置)

    # curl -X POST -H 'Content-type: application/json' \

    #      --data "{\"text\":\"[$severity] $message\"}" \

    #      YOUR_SLACK_WEBHOOK_URL

}


# 检查主库状态

check_master() {

    echo "检查主库状态..."

    

    if ! mysqladmin ping -h"$MASTER_HOST" -P"$MASTER_PORT" \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" --silent; then

        send_alert "CRITICAL" "主库 $MASTER_HOST 无法连接"

        return 1

    fi

    

    # 检查主库负载

    local master_stats=$(mysql -h"$MASTER_HOST" -P"$MASTER_PORT" \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" \

        -e "SHOW GLOBAL STATUS LIKE 'Threads_connected';" \

        -e "SHOW PROCESSLIST;" 2>/dev/null)

    

    echo "主库连接正常"

    return 0

}


# 检查从库状态

check_slave() {

    local slave_host=$1

    echo "检查从库 $slave_host 状态..."

    

    # 检查从库连接

    if ! mysqladmin ping -h"$slave_host" -P3306 \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" --silent; then

        send_alert "CRITICAL" "从库 $slave_host 无法连接"

        return 1

    fi

    

    # 获取从库复制状态

    local slave_status=$(mysql -h"$slave_host" -P3306 \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" \

        -e "SHOW SLAVE STATUS\G" 2>/dev/null)

    

    if [ -z "$slave_status" ]; then

        send_alert "WARNING" "从库 $slave_host 未配置复制"

        return 1

    fi

    

    # 解析关键状态

    local io_running=$(echo "$slave_status" | grep "Slave_IO_Running:" | awk '{print $2}')

    local sql_running=$(echo "$slave_status" | grep "Slave_SQL_Running:" | awk '{print $2}')

    local seconds_behind=$(echo "$slave_status" | grep "Seconds_Behind_Master:" | awk '{print $2}')

    local last_error=$(echo "$slave_status" | grep "Last_Error:" | awk '{print $2}')

    

    # 检查复制状态

    if [ "$io_running" != "Yes" ] || [ "$sql_running" != "Yes" ]; then

        send_alert "CRITICAL" "从库 $slave_host 复制异常: IO=$io_running, SQL=$sql_running"

        send_alert "CRITICAL" "错误信息: $last_error"

        return 1

    fi

    

    # 检查复制延迟

    if [ "$seconds_behind" != "NULL" ] && [ "$seconds_behind" -gt "$ALERT_THRESHOLD" ]; then

        send_alert "WARNING" "从库 $slave_host 复制延迟: ${seconds_behind}秒"

    fi

    

    echo "从库 $slave_host 状态正常,延迟: ${seconds_behind:-0}秒"

    return 0

}


# 检查数据一致性

check_data_consistency() {

    echo "检查数据一致性..."

    

    # 检查主从数据行数一致性

    local master_count=$(mysql -h"$MASTER_HOST" -P"$MASTER_PORT" \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" \

        -e "SELECT COUNT(*) FROM app_db.users;" 2>/dev/null | tail -1)

    

    for slave in "${SLAVES[@]}"; do

        local slave_count=$(mysql -h"$slave" -P3306 \

            -u"$MASTER_USER" -p"$MASTER_PASSWORD" \

            -e "SELECT COUNT(*) FROM app_db.users;" 2>/dev/null | tail -1)

        

        if [ "$master_count" != "$slave_count" ]; then

            send_alert "CRITICAL" "数据不一致: 主库users表=$master_count行, 从库$slave=$slave_count行"

            return 1

        fi

    done

    

    echo "数据一致性检查通过"

    return 0

}


# 备份检查

check_backups() {

    echo "检查备份状态..."

    

    local backup_dir="/var/backups/mysql"

    local today=$(date +%Y%m%d)

    

    if [ -f "$backup_dir/full_backup_${today}.sql.gz" ]; then

        echo "今日备份已存在"

    else

        send_alert "WARNING" "今日全量备份未找到"

    fi

    

    # 检查备份文件大小

    local latest_backup=$(ls -t "$backup_dir"/full_backup_*.sql.gz 2>/dev/null | head -1)

    if [ -n "$latest_backup" ]; then

        local backup_size=$(du -h "$latest_backup" | cut -f1)

        echo "最新备份: $latest_backup ($backup_size)"

    fi\pq.nexgo.vip\cf.nexgo.vip\xf.nexgo.vip\

}


# 性能检查

check_performance() {

    echo "检查性能指标..."

    

    # 检查慢查询

    local slow_queries=$(mysql -h"$MASTER_HOST" -P"$MASTER_PORT" \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" \

        -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';" 2>/dev/null | tail -1 | awk '{print $2}')

    

    if [ "$slow_queries" -gt 100 ]; then

        send_alert "WARNING" "慢查询数量较多: $slow_queries"

    fi

    

    # 检查连接数

    local max_connections=$(mysql -h"$MASTER_HOST" -P"$MASTER_PORT" \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" \

        -e "SHOW VARIABLES LIKE 'max_connections';" 2>/dev/null | tail -1 | awk '{print $2}')

    

    local current_connections=$(mysql -h"$MASTER_HOST" -P"$MASTER_PORT" \

        -u"$MASTER_USER" -p"$MASTER_PASSWORD" \

        -e "SHOW GLOBAL STATUS LIKE 'Threads_connected';" 2>/dev/null | tail -1 | awk '{print $2}')

    

    local connection_usage=$((current_connections * 100 / max_connections))

    

    if [ "$connection_usage" -gt 80 ]; then

        send_alert "WARNING" "连接数使用率过高: ${connection_usage}% (${current_connections}/${max_connections})"

    fi

    

    echo "性能检查完成"

}


# 生成监控报告

generate_report() {

    local report_file="/var/log/mysql-cluster/report_$(date +%Y%m%d_%H%M%S).log"

    

    {

        echo "=== MySQL集群监控报告 ==="

        echo "生成时间: $(date)"

        echo ""

        

        echo "1. 主库状态:"

        check_master

        echo ""

        

        echo "2. 从库状态:"

        for slave in "${SLAVES[@]}"; do

            check_slave "$slave"

        done

        echo ""

        

        echo "3. 数据一致性:"

        check_data_consistency

        echo ""

        

        echo "4. 备份状态:"

        check_backups

        echo ""

        

        echo "5. 性能指标:"

        check_performance

        echo ""

        

        echo "=== 报告结束 ==="

    } > "$report_file"

    

    echo "监控报告已生成: $report_file"

}


# 主监控循环

main() {

    echo "开始MySQL集群监控..."

    echo "检查间隔: ${CHECK_INTERVAL}秒"

    echo "告警阈值: ${ALERT_THRESHOLD}秒延迟"

    echo ""

    

    while true; do

        local timestamp=$(date '+%Y-%m-%d %H:%M:%S')

        echo "[$timestamp] 开始集群检查..."

        

        # 执行检查

        check_master

        

        for slave in "${SLAVES[@]}"; do

            check_slave "$slave"

        done

        

        # 每小时执行一次完整检查

        if [ "$(date +%M)" = "00" ]; then

            check_data_consistency

            check_backups

            check_performance

            

            # 每天生成一次完整报告

            if [ "$(date +%H)" = "00" ]; then

                generate_report

            fi

        fi

        

        echo "检查完成,等待 ${CHECK_INTERVAL} 秒..."

        echo ""

        sleep "$CHECK_INTERVAL"

    done

}


# 执行主函数

main

```


## 故障恢复与数据同步


处理常见故障场景的恢复方案:


```sql

-- 1. 从库复制中断恢复

-- 查看从库错误

SHOW SLAVE STATUS\G


-- 跳过特定错误(谨慎使用)

STOP SLAVE;

SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;

START SLAVE;


-- 2. 重新同步从库数据

-- 在主库创建数据快照

mysqldump -h master_host -u root -p \

    --single-transaction \

    --master-data=2 \

    --databases app_db > master_snapshot.sql


-- 在从库恢复

STOP SLAVE;

source master_snapshot.sql;

START SLAVE;


-- 3. GTID故障恢复

-- 查看GTID状态

SELECT @@GLOBAL.GTID_EXECUTED;

SHOW SLAVE STATUS\G


-- 重置GTID

STOP SLAVE;

RESET MASTER;

SET @@GLOBAL.GTID_PURGED='';

START SLAVE;


-- 4. 半同步复制监控

-- 检查半同步状态

SHOW STATUS LIKE 'Rpl_semi_sync%';


-- 启用半同步复制(主库)

INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

SET GLOBAL rpl_semi_sync_master_enabled = 1;


-- 从库

INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

SET GLOBAL rpl_semi_sync_slave_enabled = 1;


-- 5. 延迟复制配置

-- 设置从库延迟(秒)

CHANGE MASTER TO MASTER_DELAY = 3600;  -- 延迟1小时


-- 查看延迟状态

SHOW SLAVE STATUS\G

-- 关注:SQL_Remaining_Delay 字段

```


## 总结与最佳实践


基于上述部署实践,总结MySQL主从集群的关键要点:


1. **架构设计**:

   - 根据业务需求选择复制模式(异步/半同步)

   - 合理规划从库数量,避免过多从库影响主库性能

   - 考虑读写分离架构,将读操作分散到从库


2. **监控维护**:

   - 建立完善的监控告警机制

   - 定期检查复制延迟和数据一致性

   - 实施定期备份和恢复演练


3. **故障处理**:

   - 制定清晰的故障切换流程

   - 准备数据不一致的修复方案

   - 建立回滚机制应对升级失败


4. **性能优化**:

   - 合理配置二进制日志格式和保留策略

   - 优化网络连接和缓冲区设置

   - 监控并调整从库的并行复制线程


通过Docker容器化部署,MySQL主从集群的搭建和维护变得更加简单高效。这种方案特别适合开发测试环境和中小规模生产部署,能够以较低成本实现数据库的高可用架构。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3105266/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    941795