高可用架构核心概念
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=ON 和 super_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 ) 核心机制
来换个角度看
简单来看
2 )工作模式:
- 活动主库(Active Master)处理读写
- 备用主库(Passive Master)仅提供读服务(
read_only=ON)
3 )关键核心能力:
- 拓扑监控:
- 监控主主复制链路状态(延迟、中断)
- 自动故障转移:
- 活动主库故障时,提升备用主库
- 迁移写VIP(Virtual IP)至新主库
- 并重置其他从库同步新主
- 读负载均衡:
- 为每台服务器分配读VIP
- 延迟过高时迁移读VIP至正常节点
- 虚拟IP管理:
- 写VIP(Writer VIP):在主库间漂移
- 读VIP(Reader VIP):可在所有节点(主/从)间漂移,实现读负载均衡
4 )故障转移流程:
- 监控服务器检测主节点宕机。
- 写VIP迁移至被动节点。
- 所有从库执行
CHANGE MASTER指向新主。 - 应用通过写VIP无缝访问新主库。
局限性:监控服务器自身是单点,但宕机不影响集群运行(仅故障转移功能暂停)。
5 )架构依赖与资源规划
-
服务器角色:
- 主库×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:硬件配置完全一致(CPU/内存/I/O/MySQL版本/配置)。
-
网络资源:
- 物理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+1 | N=服务器总数(每台1物理IP + 1读VIP,写VIP共享) |
| 数据库账号 | 3 | 权限如下: |
| - 监控账号 | 1 | REPLICATION CLIENT |
| - 代理账号 | 1 | SUPER, REPLICATION CLIENT, PROCESS |
| - 复制账号 | 1 | REPLICATION SLAVE |
数据库账号拆分下来看:
| 账号类型 | 权限要求 | 用途 |
|---|---|---|
monitor_user | REPLICATION CLIENT, PROCESS | 监控服务状态检测 |
agent_user | SUPER, REPLICATION SLAVE | 代理执行切换操作 |
repl_user | REPLICATION 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
- 在节点3检查主库指向:
-
手动触发 VIP 迁移参考:
- $
mmm_control move_role writer db2
- $
MMM架构优劣分析
| 优点 | 缺点 |
|---|---|
| 开源灵活:Perl 开发,支持自定义扩展 | 版本兼容差:仅支持基于日志点的复制(不支持 GTID) |
| 透明故障转移:VIP 漂移对应用无感知 | 多线程复制不支持:高并发下主从延迟严重 |
| 自动延迟监控:延迟超阈值迁移读 VIP | 读负载均衡缺失:需额外依赖 LVS/F5 设备 |
| 数据重同步自动化:减少 DBA 干预 | 脑裂风险:依赖第三方脚本实现防护(如 arping) |
| 多集群统一监控:降低运维成本 | 监控节点单点:需自行实现高可用 |
核心优势:
- 开源灵活:Perl开发,支持源码级定制扩展
- 故障透明转移:VIP漂移实现应用无感知切换
- 自动化运维:从库自动重配至新主库
- 延迟处理:自动迁移高延迟节点的读VIP
- 多集群管理:单监控节点可管理多个集群
- 自动重同步:从库自动指向新主,减少人工干预
- 集中监控:单监控节点可管理多集群
关键缺陷:
| 问题类型 | 具体表现 | 解决方案 |
|---|---|---|
| 技术陈旧 | 仅支持日志点复制(不支持GTID) | 改用MHA/Galera Cluster |
| 数据一致性风险 | 强制提升备库可能导致事务丢失 | 启用GTID复制 |
| 监控单点 | 监控节点无高可用保障 | 为监控节点配置Keepalived |
| 负载均衡缺失 | 需额外LVS/F5实现读VIP负载 | 集成ProxySQL: |
- 版本兼容差:
- 仅支持基于日志点的复制(不支持GTID)
- 不兼容多线程复制(从库SQL线程单点瓶颈)
- 数据一致性风险:
- 强制提升备库为主,可能导致事务丢失或重复执行
- 故障切换时简单采用新主当前日志点重建复制,繁忙系统可能导致数据丢失
/* 切换时潜在数据丢失场景 */ -- 原主库已提交事务 COMMIT; -- 切换时选择非最新备库 SET GLOBAL read_only=OFF; -- 新主库缺失已提交事务 SELECT * FROM lost_transaction; ```
- 监控单点:监控节点无高可用方案,需自行实现监控高可用
- 负载均衡缺失:需额外引入LVS/F5实现读VIP负载均衡
- 维护成本高,需定制化开发:
- 脑裂防护脚本
- 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按策略分配到健康节点
生产建议
- 替代方案:优先选用 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'), // 从库健康检查
]);
}
}
关键要点总结
- 高可用本质是成本与可用性的平衡,需按业务需求选择等级(如金融选五个九,内部系统选三个九)
- 根治不可用诱因比盲目堆砌冗余更重要(监控、备份、配置、归档四维度闭环)
- 主从复制是MySQL高可用基石,但需MMM/MHA等工具解决主库单点与切换逻辑
- MMM适用主动-被动式主主架构,通过VIP漂移和代理机制实现透明故障转移
- 生产环境决策:
- 慎用共享存储/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复制
728

被折叠的 条评论
为什么被折叠?



