MySQL: 高可用架构设计与MMM方案深度解析

高可用架构核心概念


1 ) 定义与衡量标准

  • 高可用性(HA):通过缩短系统维护与故障导致的停机时间提升应用可用性

  • 量化指标(N个九标准):

    可用级别年不可用时间计算公式
    99.999%≤5.26分钟365×24×60×(1-0.99999)
    99.99%≤52.6分钟365×24×60×(1-0.9999)
    99.9%≤526分钟365×24×60×(1-0.999)
  • 实现挑战:

    • 绝对100%高可用不可达(因硬件故障、主从延迟、复制中断、锁阻塞等问题)
    • 高可用等级与成本正相关,需结合业务需求选择合理级别(如过度追求五个九将导致资源浪费)

2 ) 选择原则

  • 业务需求与成本平衡,避免过度设计(如非必要场景强求五个九导致资源浪费)

高可用实现路径


1 ) 减少不可用时间

措施关键操作
监控报警精准监控磁盘空间/慢查询,避免漏报误报
备份恢复验证定期测试远程备份文件完整性
配置优化主从环境强制从库read_only=ONsuper_read_only = ON
数据归档迁移历史数据至Archive引擎表(需启用独立表空间)

1.1 常见故障原因:

  • 服务器磁盘空间耗尽
    • 典型案例:备份文件/查询日志暴增占满磁盘
    • 解决方案:
      /* 定期归档历史数据示例 */
      CREATE TABLE archive.orders_2023 LIKE prod.orders;
      INSERT INTO archive.orders_2023 SELECT * FROM prod.orders WHERE order_date < '2023-01-01';
      DELETE FROM prod.orders WHERE order_date < '2023-01-01';
      
      /* 启用InnoDB独立表空间(必需)*/
      SET GLOBAL innodb_file_per_table = ON;
      
  • 低效SQL查询
  • 表结构设计缺陷(如索引缺失)
  • 数据不一致或人为操作失误
  • 监控系统缺陷
    • 核心原则:避免错报(狼来效应)和漏报(重大故障无警报)
    • 示例代码(监控磁盘空间脚本):
      #!/bin/bash
      THRESHOLD=90  # 磁盘使用率阈值
      USAGE=$(df -h /var/lib/mysql | awk 'NR==2 {print $5}' | tr -d '%')
      if [ $USAGE -gt $THRESHOLD ]; then
        echo "警告:MySQL磁盘使用率 ${USAGE}%!" | mail -s "磁盘告警" admin@example.com
      fi
      
  • 配置错误
    • 关键配置:从服务器强制设为只读
      /* 主从环境从库只读配置 */
      SET GLOBAL read_only = ON;
      SET GLOBAL super_read_only = ON; -- MySQL 5.7+
      
  • 备份失效
    • 风险点:远程传输损坏、备份文件不可用
    • 必须措施:周期性恢复测试

1.2 预防措施:

  • 完善监控与报警:

    • 杜绝错报/漏报:错报削弱信任(如“狼来了”效应),漏报导致故障未被及时处理
    • 监控重点:磁盘空间、慢查询、表结构变更、数据一致性异常、人为操作
    • 示例监控项:磁盘使用率、主从延迟、线程阻塞
  • 备份恢复测试:

    • 定期验证备份文件完整性(尤其远程备份)
      • 网络传输损坏是常见隐患,确保灾难时可快速还原
    • 防止因网络传输损坏导致还原失败
      -- 示例:备份恢复测试流程  
      mysqldump -u root -p --all-databases --master-data=2 > full_backup.sql  
      mysql -u root -p < full_backup.sql  -- 恢复验证  
      
  • 正确配置数据库:

    • 主从环境中从服务器必须设为 read_only ,避免主从不一致引发故障
      -- 配置从库只读  
      SET GLOBAL read_only = ON;  
      
  • 数据归档与清理:

    • 历史数据迁移至归档表(如 ARCHIVE 引擎)
    • 前提:使用InnoDB独立表空间,否则无法收缩系统表空间
       -- 归档引擎节省空间
       CREATE TABLE archive_table (
           id INT PRIMARY KEY,
           log_data TEXT,
           # 其他字段
       ) ENGINE=ARCHIVE;
      
       -- 迁移历史数据到归档表  
       INSERT INTO archive_table SELECT * FROM main_table WHERE create_time < '2025-01-01';  
       DELETE FROM main_table WHERE create_time < '2020-01-01';  
      
  • 主从只读配置示例:

    -- 从库配置只读模式(my.cnf)  
    [mysqld]  
    read_only = ON  
    super_read_only = ON  -- 防止特权用户写操作
    

2 ) 消除单点故障(SPOF)

核心挑战:

  • 单纯增加从库仅解决从库单点,主库仍是单点故障源
  • 需解决:主从切换/故障转移机制、新主库选举、应用层路由更新

单点类型:IDC机房、网络交换机、服务器、MySQL服务

增加系统冗余,方案对比

方案原理缺陷
共享存储(SAN)双机挂载同一存储,主宕机后备机接管存储成为新单点;随机IO性能差;恢复时间长
磁盘镜像(DRBD)Linux内核级块设备复制,主备数据实时同步故障转移慢;备机无法提供读服务;成本高;磁盘BUG导致双节点数据损坏
多写集群(PXC)全节点同步提交事务(任一节点失败则集体回滚)性能受最差节点制约;仅支持InnoDB;写入性能低于单机
NDB集群全节点内存存储,任意节点可读写数据必须全内存存储;内存不足时性能骤降;生产环境限制多
主从复制+管理组件主故障时自动选举新主并重配从库(如MMM/MHA)需解决VIP漂移、数据一致性等问题

PXC集群同步机制补充:
使用wsrep API实现全局事务ID(GTID),事务在所有节点验证通过后才提交

/* 查看PXC集群状态 */
SHOW STATUS LIKE 'wsrep%';

MMM架构深度解析


MMM(MySQL Multi-Master Replication Manager) 是基于Perl开发的工具集,用于管理主-备模式的主主复制拓扑(非双主同时写入)

MMM 基于主动-被动式主主复制拓扑:

  • 同一时间仅一个主节点(Active Master)可写
  • 备用主节点(Passive Master)为read_only模式
  • 关键组件:
    • mmm_monitor:独立监控服务(单点风险)
    • mmm_agent:各节点部署的代理

典型拓扑:

[监控服务器]  
    │  
    ├─[主节点1] (写VIP + 读VIP) → Active  
    ├─[主节点2] (读VIP)          → Passive  
    └─[从节点]  (读VIP)          → Read-Only  

1 ) 核心机制

检测
检测
检测
写VIP
读VIP
读VIP
主库故障
重构链路
监控节点
Active主库
Passive备库
从库集群
应用
VIP漂移至备库
从库同步新主

来换个角度看

检测状态
检测状态
检测状态
写VIP
读VIP
读VIP
主库故障
重构复制链路
监控服务器
主库
备库
从库
应用写请求
应用读请求
迁移写VIP至备库
从库同步新主库

简单来看

写入
只读
只读
监控
监控
监控
Active Master
写VIP
Passive Master
读VIP1
Slave
读VIP2
Monitor

2 )工作模式:

  • 活动主库(Active Master)处理读写
  • 备用主库(Passive Master)仅提供读服务(read_only=ON

3 )关键核心能力:

  • 拓扑监控:
    • 监控主主复制链路状态(延迟、中断)
  • 自动故障转移:
    • 活动主库故障时,提升备用主库
    • 迁移写VIP(Virtual IP)至新主库
    • 并重置其他从库同步新主
  • 读负载均衡:
    • 为每台服务器分配读VIP
    • 延迟过高时迁移读VIP至正常节点
  • 虚拟IP管理:
    • 写VIP(Writer VIP):在主库间漂移
    • 读VIP(Reader VIP):可在所有节点(主/从)间漂移,实现读负载均衡

4 )故障转移流程:

  1. 监控服务器检测主节点宕机。
  2. 写VIP迁移至被动节点。
  3. 所有从库执行CHANGE MASTER指向新主。
  4. 应用通过写VIP无缝访问新主库。

局限性:监控服务器自身是单点,但宕机不影响集群运行(仅故障转移功能暂停)。

5 )架构依赖与资源规划

  1. 服务器角色:

    • 主库×2:硬件配置完全一致(CPU/内存/I/O/MySQL版本/配置)。
      -- 检查主库配置一致性(排除server_id等差异项)  
      SELECT * FROM information_schema.GLOBAL_VARIABLES  
      WHERE VARIABLE_NAME IN ('innodb_buffer_pool_size','sync_binlog','innodb_flush_log_at_trx_commit');  
      
    • 从库×N:数量不宜过多(切换复杂度递增)。
    • 监控服务器×1:独立部署,避免单点(宕机不影响服务,但丧失自动切换能力)。
  2. 网络资源:

    • 物理IP:每服务器1个
    • 虚拟IP:写VIP×1 + 读VIP×N(N=服务器总数)

6 )基于上面参考下面的部署规范

组件数量要求
主库服务器2台硬件/配置/MySQL版本严格一致(防切换后性能下降)
从库服务器≥0台建议≤3台(过多增加切换复杂度)
监控服务器1台建议独立部署(可管理多个MMM集群)
IP资源2N+1个N=节点数(每节点1物理IP + 1读VIP,写VIP独占1个)
数据库账号3个监控账号(REPLICATION CLIENT)、代理账号(SUPER)、复制账号

换个角度看

组件数量要求
主库服务器2硬件/配置完全相同(CPU、内存、MySQL版本)
从库服务器≥0建议≤3台(过多增加切换复杂度)
监控服务器1独立部署(单点风险)
IP地址资源2N+1N=服务器总数(每台1物理IP + 1读VIP,写VIP共享)
数据库账号3权限如下:
- 监控账号1REPLICATION CLIENT
- 代理账号1SUPER, REPLICATION CLIENT, PROCESS
- 复制账号1REPLICATION SLAVE

数据库账号拆分下来看:

账号类型权限要求用途
monitor_userREPLICATION CLIENT, PROCESS监控服务状态检测
agent_userSUPER, REPLICATION SLAVE代理执行切换操作
repl_userREPLICATION SLAVE主从复制账号

7 ) 拓扑结构准备

  • 主节点 1:192.168.3.100(物理 IP),写 VIP:192.168.3.90
  • 主节点 2(主备):192.168.3.101(物理 IP),读 VIP:192.168.3.91
  • 从节点1:192.168.3.102(物理 IP),读 VIP:192.168.3.92
  • 监控节点:部署在 192.168.3.102 (部署在从1),(建议生产环境独立部署)

8 ) 实战配置

8.1 主主复制与主从复制配置

步骤一: 创建复制账号
在主节点1执行:

CREATE USER 'repl'@'192.168.3.%' IDENTIFIED BY '123456';  
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.3.%';  

注意:账号需同步至所有节点

步骤二: 数据备份与初始化

使用 mysqldump 备份主节点1数据:

mysqldump --all-databases --master-data=2 -u root -p > /root/all.sql  

同步备份文件至其他节点:

scp /root/all.sql root@192.168.3.101:/root/  
scp /root/all.sql root@192.168.3.102:/root/  

在各节点恢复数据:

mysql -u root -p < /root/all.sql  

步骤三: 配置复制链路

主主复制(节点1 ↔ 节点2):
这是双主复制,在节点1指向节点2:

CHANGE MASTER TO  
  MASTER_HOST='192.168.3.101',  
  MASTER_USER='repl',  
  MASTER_PASSWORD='123456',  
  MASTER_LOG_FILE='mysql-bin.000001',  
  MASTER_LOG_POS=154; -- 根据备份文件实际位置修改  
START SLAVE;  

在节点2指向节点1:

CHANGE MASTER TO  
  MASTER_HOST='192.168.3.100',  
  MASTER_USER='repl',  
  MASTER_PASSWORD='123456',  
  MASTER_LOG_FILE='mysql-bin.000001',  
  MASTER_LOG_POS=154;  
START SLAVE;  
  • 主从复制(节点3 → 节点1),在节点3执行:
CHANGE MASTER TO  
  MASTER_HOST='192.168.3.100',  
  MASTER_USER='repl',  
  MASTER_PASSWORD='123456',  
  MASTER_LOG_FILE='mysql-bin.000001',  
  MASTER_LOG_POS=154;  
START SLAVE;  

8.2 MMM工具集安装与配置

步骤一:依赖安装与YUM源配置

  • 在所有节点配置EPEL源:
    wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm  
    rpm -ivh epel-release-latest-7.noarch.rpm  
    
  • 编辑源文件启用Remi仓库:
    sed -i 's/enabled=0/enabled=1/g' /etc/yum.repos.d/remi.repo  
    

步骤二:安装MMM组件

  • 数据库节点:安装代理包
    yum install -y mysql-mmm-agent  
    
  • 监控节点(部署在192.168.3.102):额外安装监控包
    yum install -y mysql-mmm-monitor  
    

步骤三:创建MMM专用账号
在主节点1执行:

-- 监控账号(健康检查)  
CREATE USER 'mmm_monitor'@'192.168.3.%' IDENTIFIED BY '123456';  
GRANT REPLICATION CLIENT ON *.* TO 'mmm_monitor'@'192.168.3.%';  

-- 代理账号(故障切换)  
CREATE USER 'mmm_agent'@'192.168.3.%' IDENTIFIED BY '123456';  
GRANT SUPER, REPLICATION CLIENT, PROCESS ON *.* TO 'mmm_agent'@'192.168.3.%';  

步骤四:配置文件同步

  • 通用配置(/etc/mysql-mmm/mmm_common.conf):

    active_master_role      writer  
    cluster_interface       eth0  
    
    replication_user        repl  
    replication_password    123456  
    
    agent_user              mmm_agent  
    agent_password          123456  
    
    host db1 {  
      ip   192.168.3.100  
      mode master  
      peer db2    # 关键:双主必须互指
    }  
    host db2 {  
      ip   192.168.3.101  
      mode master  
      peer db1    # 关键:双主必须互指
    }  
    host db3 {  
      ip   192.168.3.102  
      mode slave  # 从机角色 
    }  
    
    role writer {  
      hosts   db1, db2           # 写 VIP 绑定到双主
      ips     192.168.3.90       # 写 VIP
      mode    exclusive          # 独占模式(同一时间仅一个节点持有)
    }  
    role reader {  
      hosts   db1, db2, db3      # 读 VIP 绑定到所有节点(包括主)
      ips     192.168.3.91, 192.168.3.92, 192.168.3.93  
      mode    balanced           # 负载均衡模式
    }  
    
    # 虚拟 IP 配置 下面的不需要显示配置
    # ip 192.168.3.90  # 写 VIP 
    # ip 192.168.3.91  # 读 VIP(db1)
    # ip 192.168.3.92  # 读 VIP(db2)
    # ip 192.168.3.93  # 读 VIP(db3)
    
  • 或参与下面的配置

    active_master_role      writer 
     
    <host default>
      cluster_interface     eth0 
      pid_path              /var/run/mysql-mmm/mmm_agentd.pid 
      bin_path              /usr/libexec/mysql-mmm
      replication_user      repl_user 
      replication_password  123456 
      agent_user            mmm_agent
      agent_password        123456 
    </host>
     
    <host db1>
      ip      192.168.3.100
      mode    master 
      peer    db2 
    </host>
     
    <host db2>
      ip      192.168.3.101
      mode    master 
      peer    db1 
    </host>
     
    <host db3>
      ip      192.168.3.102
      mode    slave 
    </host>
     
    <role writer>
      hosts   db1, db2 
      ips     192.168.3.90
      mode    exclusive
    </role>
     
    <role reader>
      hosts   db1, db2, db3 
      ips     192.168.3.91, 192.168.3.92, 192.168.3.93
      mode    balanced 
    </role>
    
  • 代理节点配置(/etc/mysql-mmm/mmm_agent.conf):

    • 按节点角色修改:
      this db1  # 节点1配置  
      this db2  # 节点2配置  
      this db3  # 节点3配置  
      
    • 注意,上述配置,每个节点单独配置自己的
    • 如: db1 只配置 this db1 即可,其他同此逻辑
  • 监控节点配置(/etc/mysql-mmm/mmm_mon.conf):

    ip 127.0.0.1  
    pid_path /var/run/mmm_mond.pid  
    checker_hosts 192.168.3.100,192.168.3.101,192.168.3.102  
    auto_set_online 60  
    monitor_user mmm_monitor  
    monitor_password 123456  
    
  • 或参考下面的配置

    include mmm_common.conf 
    <monitor>
      ip                  127.0.0.1
      pid_path            /var/run/mysql-mmm/mmm_mond.pid
      bin_path            /usr/libexec/mysql-mmm 
      status_path         /var/lib/mysql-mmm/mmm_mond.status
      ping_ips            192.168.3.100, 192.168.3.101, 192.168.3.102, 192.168.3.1
      auto_set_online     60
    </monitor>
     
    <checker>
      network_checker     /usr/libexec/mysql-mmm/checker 192.168.3.100,192.168.3.101,192.168.3.102
    </checker>
     
    <handler>
      restart_mysql       service mysqld restart
    </handler>
    
  • 同步至所有节点:

    scp /etc/mysql-mmm/mmm_common.conf root@192.168.3.101:/etc/mysql-mmm/  
    scp /etc/mysql-mmm/mmm_common.conf root@192.168.3.102:/etc/mysql-mmm/  
    

8.3 集群启动与故障转移测试

步骤一: 启动代理与监控服务

  • 所有数据库节点启动代理:
    service mysql-mmm-agent start
    # 或 systemctl start mysql-mmm-agent
    
  • 监控节点启动监控服务:
    service mysql-mmm-monitor start
    # 或 systemctl start mysql-mmm-monitor
    

步骤二: 验证集群状态

  • 在监控节点执行:
    mmm_control show  
    
  • 预期输出:
    db1(192.168.3.100) master/ONLINE. Roles: writer(192.168.3.90), reader(192.168.3.93)  
    db2(192.168.3.101) master/ONLINE. Roles: reader(192.168.3.91)  
    db3(192.168.3.102) slave/ONLINE. Roles: reader(192.168.3.92)  
    

步骤三: 故障转移测试

  • 模拟主节点1宕机:

    service mysqld stop  # 在节点1执行
    # 或 systemctl stop mysqld
    
  • 检查切换结果:

    mmm_control show  
    
  • 预期输出:

    db1(192.168.3.100) master/HARD_OFFLINE. Roles:  
    db2(192.168.3.101) master/ONLINE. Roles: writer(192.168.3.90), reader(192.168.3.91)  
    db3(192.168.3.102) slave/ONLINE. Roles: reader(192.168.3.92), reader(192.168.3.93)  
    
  • 预期现象:

    • 写VIP(192.168.3.90)漂移至db2
    • db3自动重指向db2为复制源
    • db1状态标记为OFFLINE
  • 验证从节点同步:

    • 在节点3检查主库指向:
      SHOW SLAVE STATUS\G  
      
    • 确认 Master_Host 已切换至 192.168.3.101
  • 手动触发 VIP 迁移参考:

    • $ mmm_control move_role writer db2

MMM架构优劣分析


优点缺点
开源灵活:Perl 开发,支持自定义扩展版本兼容差:仅支持基于日志点的复制(不支持 GTID)
透明故障转移:VIP 漂移对应用无感知多线程复制不支持:高并发下主从延迟严重
自动延迟监控:延迟超阈值迁移读 VIP读负载均衡缺失:需额外依赖 LVS/F5 设备
数据重同步自动化:减少 DBA 干预脑裂风险:依赖第三方脚本实现防护(如 arping)
多集群统一监控:降低运维成本监控节点单点:需自行实现高可用

核心优势:

  1. 开源灵活:Perl开发,支持源码级定制扩展
  2. 故障透明转移:VIP漂移实现应用无感知切换
  3. 自动化运维:从库自动重配至新主库
  4. 延迟处理:自动迁移高延迟节点的读VIP
  5. 多集群管理:单监控节点可管理多个集群
  6. 自动重同步:从库自动指向新主,减少人工干预
  7. 集中监控:单监控节点可管理多集群

关键缺陷:

问题类型具体表现解决方案
技术陈旧仅支持日志点复制(不支持GTID)改用MHA/Galera Cluster
数据一致性风险强制提升备库可能导致事务丢失启用GTID复制
监控单点监控节点无高可用保障为监控节点配置Keepalived
负载均衡缺失需额外LVS/F5实现读VIP负载集成ProxySQL:
  1. 版本兼容差:
    • 仅支持基于日志点的复制(不支持GTID)
    • 不兼容多线程复制(从库SQL线程单点瓶颈)
  2. 数据一致性风险:
    • 强制提升备库为主,可能导致事务丢失或重复执行
    • 故障切换时简单采用新主当前日志点重建复制,繁忙系统可能导致数据丢失
         /* 切换时潜在数据丢失场景 */
         -- 原主库已提交事务
         COMMIT; 
         
         -- 切换时选择非最新备库
         SET GLOBAL read_only=OFF; 
         
         -- 新主库缺失已提交事务 
         SELECT * FROM lost_transaction; 
         ```
      
  3. 监控单点:监控节点无高可用方案,需自行实现监控高可用
  4. 负载均衡缺失:需额外引入LVS/F5实现读VIP负载均衡
  5. 维护成本高,需定制化开发:
    • 脑裂防护脚本
    • VIP冲突检测机制
      # VIP冲突检测示例
      arping -c 3 -I eth0 192.168.3.90 | grep "reply from"
      

运维要点

  • 定期检查双主配置一致性
    /* 快速比对关键配置 */
    SELECT VARIABLE_NAME, VARIABLE_VALUE 
    FROM information_schema.GLOBAL_VARIABLES 
    WHERE VARIABLE_NAME IN ('innodb_buffer_pool_size','sync_binlog','innodb_flush_log_at_trx_commit');
    
  • 监控服务器单点问题:故障时无法触发切换但服务仍可用

透明故障转移,通过虚拟IP漂移机制实现读写分离透明化:

  • 写VIP自动绑定活跃主库
  • 读VIP按策略分配到健康节点
应用
写VIP 192.168.3.90
读VIP池
Active Master
Master
Slave1
Slave2

生产建议

  • 替代方案:优先选用 MHA(Master High Availability) 或 Galera Cluster
  • 必要改造:
    • 为监控节点配置Keepalived高可用
    • 通过ProxySQL实现读VIP负载均衡
-- ProxySQL 读负载均衡示例  
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES  
(10, '192.168.3.91', 3306),  
(10, '192.168.3.92', 3306);  
 
LOAD MYSQL SERVERS TO RUNTIME;

总结

对于现代 MySQL 高可用需求,优先考虑 MHA(Master High Availability) 或 Galera Cluster,两者均支持 GTID 和并行复制,且具备更完善的事务一致性保障机制。MMM 适用于旧版本 MySQL(5.5 及以下)的过渡方案,需严格评估事务丢失风险

高可用架构需预防故障(监控、备份、配置优化)与冗余设计(消除单点)并重
MMM通过主主复制+VIP切换提供快速故障转移,但需警惕数据丢失风险与监控单点问题
生产环境中,建议结合业务场景选择PXC、MHA或云数据库高可用方案,并严格验证备份恢复流程

关键补充说明


1 ) VIP 漂移原理

  • 使用 arping 广播更新 MAC 地址映射,确保应用无感知切换。
  • 检测脚本示例:
    #!/bin/bash
    VIP=192.168.3.90
    if ! ip addr show | grep -q $VIP; then 
      arping -c 3 -U -I eth0 $VIP
    fi
    

2 ) 主从切换数据一致性风险

  • 根本原因:异步复制导致事务丢失或重复提交。
  • 规避方案:
    -- 启用半同步复制(部分解决)
    INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
    SET GLOBAL rpl_semi_sync_master_enabled = 1;
    

注:生产环境建议使用 MHA 或 ProxySQL 替代 MMM,以支持 GTID 和更完善的读写分离

关键配置注意事项


1 ) 网络规范

  • 所有节点需统一网卡名称(如eth0)
  • 禁用NetworkManager:systemctl disable NetworkManager

2 ) 复制安全加固

-- 启用半同步复制降低数据丢失风险 
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled=1;

3 ) VIP漂移超时控制

# /etc/mysql-mmm/mmm_mon.conf
ping_interval        5
ping_timeout         2 
auto_set_online      60 

4 ) 日志审计强化

# /etc/my.cnf
[mysqld]
log_error_verbosity=3 
log_slave_updates=ON 
expire_logs_days=7 

架构选型建议:在需要GTID、多线程复制或强一致性场景,应考虑MHA或Galera Cluster替代方案。
MMM适用于对数据一致性要求不高的读写分离场景。

NestJS工程实例


1 ) 数据库连接配置

// src/database.providers.ts 
import { createPool } from 'mysql2/promise';
 
export const databaseProviders = [
  {
    provide: 'DB_WRITER',
    useFactory: async () => {
      return createPool({
        host: '192.168.3.90', // 写 VIP 
        user: 'app_user',
        password: 'secure_pass',
        database: 'main_db',
        waitForConnections: true,
        connectionLimit: 10,
      });
    },
  },
  {
    provide: 'DB_READER',
    useFactory: async () => {
      return createPool({
        host: '192.168.3.91', // 读 VIP(可轮询多个读IP)
        user: 'app_user',
        password: 'secure_pass',
        database: 'main_db',
        waitForConnections: true,
        connectionLimit: 20,
      });
    },
  },
];

// database.module.ts
@Module({
  imports: [
    TypeOrmModule.forRoot({
      name: 'writeDB',
      host: '192.168.3.90', // 写 VIP
      type: 'mysql',
      ...
    }),
    TypeOrmModule.forRoot({
      name: 'readDB',
      host: '192.168.3.91', // 读 VIP
      type: 'mysql',
      ...
    }),
  ],
})

数据库高可用处理示例

// database.providers.ts (NestJS连接池配置)  
import { Provider } from '@nestjs/common';  
import { createPool } from 'mysql2/promise';  
 
export const DatabaseProvider: Provider = {  
  provide: 'DATABASE',  
  useFactory: async () => {  
    return createPool({  
      host: '192.168.3.90',  // 写VIP  
      readOnlyHosts: [        // 读VIP列表  
        '192.168.3.91',  
        '192.168.3.92'  
      ],  
      user: 'app_user',  
      password: 'secure_pass',  
      database: 'main_db',  
      waitForConnections: true,  
      connectionLimit: 20,  
      queueLimit: 0  
    });  
  }  
};  

关键说明:应用层通过读写VIP连接数据库,故障转移由MMM自动完成,无需修改应用配置。

2 ) 应用层高可用对接方案

// database.module.ts
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
 
@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useFactory: () => ({
        type: 'mysql',
        host: process.env.DB_WRITE_VIP, // 写请求指向VIP 
        port: 3306,
        username: 'app_user',
        password: process.env.DB_PASSWORD,
        database: 'main_db',
        retryDelay: 5000, // 故障时重试间隔 
        extra: {
          connectionLimit: 10,
          readReplicas: [
            { host: process.env.DB_READ_VIP1 },
            { host: process.env.DB_READ_VIP2 }
          ]
        }
      })
    })
  ],
  controllers: [HealthController]
})
export class DatabaseModule {}
 
// health.controller.ts 
import { Controller, Get } from '@nestjs/common';
import { HealthCheckService, DNSHealthIndicator } from '@nestjs/terminus';
 
@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private dns: DNSHealthIndicator 
  ) {}
 
  @Get()
  check() {
    return this.health.check([
      () => this.dns.pingCheck('write_vip', process.env.DB_WRITE_VIP),
      () => this.dns.pingCheck('read_vip1', process.env.DB_READ_VIP1)
    ]);
  }
}

3 ) VIP切换通知示例

// VIP切换回调服务(NestJS)  
import { Controller, Post, Body } from '@nestjs/common';  
 
@Controller('vip-switch')  
export class VipController {  
  @Post()  
  handleVipChange(@Body() switchEvent: { newMasterIp: string }) {  
    // 更新数据库连接池配置  
    updateDatabaseConfig(switchEvent.newMasterIp);  
    // 通知集群内应用节点  
    notifyAppNodes(switchEvent.newMasterIp);  
  }  
 
  private updateDatabaseConfig(newMasterIp: string) {  
    // 动态切换数据源配置  
    const dataSource = getDataSource();  
    dataSource.setOptions({ host: newMasterIp });  
  }  
 
  private notifyAppNodes(newMasterIp: string) {  
    // 通过消息队列广播VIP变更  
    messageQueue.publish('DB_MASTER_SWITCH', { newMasterIp });  
  }  
}  

4 )实现读负载均衡(代码示例)

// database.providers.ts  
import { Injectable } from '@nestjs/common';  
import { createPool, Pool } from 'mysql2/promise';  
 
@Injectable()  
export class DatabaseService {  
  private readPools: Pool[];  
  private writePool: Pool;  
 
  constructor() {  
    // 写连接池(指向写VIP)  
    this.writePool = createPool({  
      host: '192.168.3.90',  
      user: 'app_user',  
      password: 'secure_pass',  
      database: 'app_db',  
      connectionLimit: 10,  
    });  
 
    // 读连接池(指向读VIP列表)  
    this.readPools = [  
      '192.168.3.91',  
      '192.168.3.92',  
      '192.168.3.93',  
    ].map(host => createPool({ host, ... }));  
  }  
 
  // 随机选择读节点  
  private getReadPool(): Pool {  
    const index = Math.floor(Math.random() * this.readPools.length);  
    return this.readPools[index];  
  }  
 
  // 执行写操作  
  async queryWrite(sql: string, params: any[]): Promise<any> {  
    const conn = await this.writePool.getConnection();  
    const [result] = await conn.query(sql, params);  
    conn.release();  
    return result;  
  }  
 
  // 执行读操作  
  async queryRead(sql: string, params: any[]): Promise<any> {  
    const pool = this.getReadPool();  
    const [result] = await pool.query(sql, params);  
    return result;  
  }  
}  

功能:自动分发读请求至读VIP节点,写请求定向至写VIP。

5 ) 状态监控

import { Injectable } from '@nestjs/common';  
import { HealthCheckService, HealthCheckResult } from '@nestjs/terminus';  

@Injectable()  
export class DBHealthService {  
  constructor(private health: HealthCheckService) {}  
  
  async checkDatabase(): Promise<HealthCheckResult> {  
    return this.health.check([  
      () => customDatabasePing('master_db'), // 主库健康检查  
      () => customDatabasePing('slave_db'),   // 从库健康检查  
    ]);  
  }  
}  

关键要点总结

  1. 高可用本质是成本与可用性的平衡,需按业务需求选择等级(如金融选五个九,内部系统选三个九)
  2. 根治不可用诱因比盲目堆砌冗余更重要(监控、备份、配置、归档四维度闭环)
  3. 主从复制是MySQL高可用基石,但需MMM/MHA等工具解决主库单点与切换逻辑
  4. MMM适用主动-被动式主主架构,通过VIP漂移和代理机制实现透明故障转移
  5. 生产环境决策:
    • 慎用共享存储/DRBD(存储层单点或性能瓶颈)
    • 避免NDB集群(内存限制严苛)
    • PXC集群适合强一致性场景,但需接受写入性能损耗

演进方向与补充


1 ) MHA高可用方案

作为MMM的替代方案,MHA(Master High Availability) 提供更优解决方案:

  • 数据一致性保障:通过对比从库日志点,选举数据最新的节点为主库
  • GTID支持:兼容现代复制机制,降低数据丢失风险
  • 无中心架构:无需独立监控节点,通过脚本管理故障转移
    masterha_check_repl --conf=/etc/mha.conf  # 检查复制状态 
    masterha_manager --conf=/etc/mha.conf     # 启动监控 
    

优势:数据一致性保障、GTID支持、无中心架构

2 ) GTID复制配置

(MySQL 5.6+)

-- 启用GTID(所有节点)  
SET GLOBAL gtid_mode = ON;  
SET GLOBAL enforce_gtid_consistency = ON;  
 
-- 从库指向主库(替代基于日志点配置)  
CHANGE MASTER TO  
  MASTER_HOST='192.168.3.100',  
  MASTER_AUTO_POSITION=1;  

优势:切换时自动选择最新事务点,避免数据不一致

3 )GTID复制 vs 日志点复制

特性GTID复制日志点复制
一致性全局事务ID防数据丢失/重复依赖二进制日志位置
主从切换自动查找最新同步点需手动比对日志位置
MMM支持❌ 不支持✔️ 唯一支持方案

4 ) 应用层负载均衡(NestJS示例)

// 读请求随机分发至读VIP节点 
private getReadPool(): Pool {
  const hosts = ['192.168.3.91','192.168.3.92','192.168.3.93'];
  return this.readPools[Math.floor(Math.random() * hosts.length)];
}

架构选择建议:

  • 中小规模集群:MMM+ProxySQL+Keepalived
  • 大规模生产:MHA/Galera Cluster+GTID复制
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值