MySQL 高可用与复制架构实践
前言
MySQL 作为互联网行业中应用最广泛的关系型数据库,其高可用性和数据一致性一直是架构设计的核心关注点。在实际生产环境中,单机 MySQL 无法满足高并发、高可用的需求,因此主从复制、双主架构、读写分离等技术方案应运而生。
本文将系统性地梳理 MySQL 复制架构的演进脉络,从主从复制延迟问题入手,深入探讨多线程复制(MTS)、GTID 主主复制、Keepalived 高可用架构以及 MySQL Proxy 读写分离等关键技术,并结合实际生产案例,给出完整的配置实践和故障处理方案。
一、主从复制延迟问题分析
在 MySQL 主从复制架构中,延迟问题一直是业界面临的主要挑战。主从延迟受网络、磁盘 I/O 等多种客观因素影响,但最核心的原因在于:当 Master 端写入过于繁忙时,Slave 端无法及时从 relay log 中读取并应用最新的变更记录。
对于数据实时性要求较高的业务场景,主从延迟意味着 Slave 上的数据并非最新,此时基于主从的读写分离方案就显得力不从心——业务读取 Slave 时可能拿到过时数据。因此,在实际架构中,Slave 通常只承担对实时性要求不高的查询任务(如报表统计、数据分析等)。
从 MySQL 5.6 版本开始,官方引入了多线程主从复制机制。众所周知,MySQL 采用单进程多线程的工作模式,线程数量直接影响其处理效率。在早期版本中,主从复制仅由单线程执行,这使得复制效率除了受客观因素影响外,还受到单线程处理能力的制约。
然而,MySQL 5.6 的多线程复制并不完善:它基于库级别(DATABASE)进行并行复制,即一个线程处理一个数据库的复制任务。在绝大多数生产环境中,大量操作集中在单个数据库中,因此多线程机制形同虚设,退化为实质上的单线程复制。
为此,MySQL 5.7 对多线程复制进行了彻底改进:引入基于逻辑时钟(类似 CPU 的时间片调度机制)的并行复制策略,同时在半同步复制(semi-sync)中支持通过 Performance Schema 表监控复制线程状态。这些改进使得 MySQL 自身导致的复制延迟大幅降低。
1.1 多线程复制配置(MySQL 5.7)
在 Slave 端执行以下配置:
-- 查看当前并行线程数(默认为 0,即单线程)
mysql> SHOW GLOBAL VARIABLES LIKE 'slave_parallel_workers';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| slave_parallel_workers | 0 |
+------------------------+-------+
-- 根据实际情况设置并行线程数
mysql> SET GLOBAL slave_parallel_workers = 8;
-- 查看并行复制类型(默认为 DATABASE,即库级别并行)
mysql> SHOW GLOBAL VARIABLES LIKE '%slave_parallel_type%';
+---------------------+----------+
| Variable_name | Value |
+---------------------+----------+
| slave_parallel_type | DATABASE |
+---------------------+----------+
-- 修改为基于逻辑时钟的并行方式(需先停止 Slave)
mysql> STOP SLAVE;
mysql> SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK';
mysql> START SLAVE;配置完成后,可通过 SHOW FULL PROCESSLIST 查看 Slave 上的线程列表:
mysql> SHOW FULL PROCESSLIST;
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+-----------------------+
| 16 | root | localhost | NULL | Query | 0 | starting | SHOW FULL PROCESSLIST |
| 29 | system user | | NULL | Connect | 3 | Waiting for master to send event | NULL |
| 30 | system user | | NULL | Connect | 3 | Slave has read all relay log; waiting for more updates | NULL |
| 31 | system user | | NULL | Connect | 3 | Waiting for an event from Coordinator | NULL |
| 32 | system user | | NULL | Connect | 3 | Waiting for an event from Coordinator | NULL |
| 33 | system user | | NULL | Connect | 3 | Waiting for an event from Coordinator | NULL |
| 34 | system user | | NULL | Connect | 3 | Waiting for an event from Coordinator | NULL |
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+-----------------------+可以看到 Coordinator 线程与多个 Worker 线程协同工作。在 Slave 的 datadir 目录下,会生成与 slave_parallel_workers 设置数量一致的线程相关文件。
1.2 持久化配置
将配置写入 /etc/my.cnf,使重启后永久生效:
slave_parallel_type = LOGICAL_CLOCK
slave_parallel_workers = 16
master_info_repository = TABLE
relay_log_info_repository = TABLE
relay_log_recovery = ON注意:如果在运行过程中停止 Coordinator 和 Worker 线程,可能导致数据处于不一致状态。通常重启服务后会自动恢复一致性,但如果使用了非事务存储引擎或执行过 DDL,则需格外小心。
二、GTID 复制原理与主主搭建
2.1 GTID 工作原理
GTID(Global Transaction Identifier,全局事务标识符)是 MySQL 5.6 版本引入的一项重要特性,它为每一个事务分配全局唯一的 ID,极大简化了主从复制的管理和故障切换。其工作流程如下:
- 事务产生:Master 更新数据时,在事务执行前生成 GTID,并与事务一同记录到 binlog 中。
- 日志传输:Slave 的 I/O 线程将变更的 binlog 写入本地的 relay log。
- GTID 比对:SQL 线程从 relay log 中提取 GTID,然后与 Slave 端 binlog 中已记录的 GTID 进行比对。
- 去重跳过:如果该 GTID 的事务已经执行过,Slave 直接忽略。
- 事务执行:如果该 GTID 未被执行,Slave 从 relay log 中读取并执行该事务,并将 GTID 记录到本地 binlog。
- 索引选择:解析过程中会优先使用主键,无主键则使用二级索引,都没有则进行全表扫描。
2.2 GTID 的优势
- 唯一性:一个事务对应一个唯一 ID,一个 GTID 在同一个服务器上只会执行一次,天然支持幂等。
- 简化配置:GTID 复制模式不需要手动指定二进制日志文件名和位置(即不再依赖
MASTER_LOG_FILE和MASTER_LOG_POS)。 - 故障切换自动化:减少人工干预,降低服务故障恢复时间。当 Master 宕机后,可自动从多个备机中提升新主机,无需手动计算位点。
2.3 GTID 主主复制搭建
2.3.1 my.cnf 配置
在两台 MySQL 服务器上分别添加以下配置:
[mysqld]
port = 3306
socket = /var/lib/mysql/mysql.sock
datadir = /data/mysql/data
log-bin = /data/mysql/binlog/mysql-bin
server_id = 161 # 两台服务器需保持不同
auto_increment_offset = 1 # 另一台配置为 2
auto_increment_increment= 2
log-slave-updates = ON # 从库的变更也记录到 binlog
enforce_gtid_consistency= ON # 强制 GTID 一致性
gtid-mode = ON # 开启 GTID 模式
binlog-format = ROW
skip-name-resolve
explicit_defaults_for_timestamp
max_connections = 2000
wait_timeout = 3600
connect_timeout = 800
max_allowed_packet = 64M
character-set-server = utf8
# InnoDB 配置
innodb_flush_method = O_DIRECT
innodb_log_files_in_group = 2
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_file_per_table = 1
innodb_buffer_pool_size = 20G
# 其他优化
join_buffer_size = 64M
read_buffer_size = 2M
sort_buffer_size = 2M
table_open_cache = 1800
thread_cache_size = 384
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
[mysqld_safe]
log-error = /var/log/mysqld.log
pid-file = /var/run/mysqld/mysqld.pid特别说明:auto_increment_offset和auto_increment_increment是双主架构的关键配置。以两节点为例,offset 分别设为 1 和 2,increment 设为 2,这样两台服务器的自增 ID 就不会冲突(一台生成 1、3、5...,另一台生成 2、4、6...)。
2.3.2 创建复制账号
在 MasterA 上创建复制账号:
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.116.103.90' IDENTIFIED BY 'your_password';
mysql> FLUSH PRIVILEGES;在 MasterB 上创建复制账号:
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.8.99.105' IDENTIFIED BY 'your_password';
mysql> FLUSH PRIVILEGES;2.3.3 配置主从关系
MasterA 指向 MasterB:
mysql> CHANGE MASTER TO
-> MASTER_HOST = '10.183.103.232',
-> MASTER_USER = 'repl',
-> MASTER_PASSWORD = 'your_password',
-> MASTER_PORT = 3306,
-> MASTER_AUTO_POSITION = 1;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G;MasterB 指向 MasterA:
mysql> CHANGE MASTER TO
-> MASTER_HOST = '10.8.99.105',
-> MASTER_USER = 'repl',
-> MASTER_PASSWORD = 'your_password',
-> MASTER_PORT = 3306,
-> MASTER_AUTO_POSITION = 1;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G;确认两台服务器的 Slave_IO_Running 和 Slave_SQL_Running 均为 Yes,即表示主主复制搭建完成。
MASTER_AUTO_POSITION = 1表示启用 GTID 自动定位,不再需要手动指定MASTER_LOG_FILE和MASTER_LOG_POS。如果需要手动定位,仍可使用传统方式:CHANGE MASTER TO MASTER_HOST = '10.140.134.197', MASTER_USER = 'repl', MASTER_PASSWORD = 'your_password', MASTER_PORT = 3307, MASTER_LOG_FILE = 'mysql-bin.000003', MASTER_LOG_POS = 3456;
2.4 使用 Xtrabackup 搭建 GTID 主从
对于已有大量数据的场景,推荐使用 Percona Xtrabackup 进行物理备份恢复后再建立主从关系。
操作步骤:
- 全量备份(在 Master 端执行):
innobackupex --defaults-file=/etc/my.cnf \
--user=root --password='your_password' \
--socket=/var/lib/mysql/mysql.sock \
/data/Xtrabackup- 重放 redo log(确保数据一致性):
innobackupex --defaults-file=/etc/my.cnf \
--user=root --password='your_password' \
--apply-log \
--socket=/var/lib/mysql/mysql.sock \
/data/Xtrabackup/2017-05-18_10-07-06- 将备份目录同步至 Slave 服务器 data_dir 下,并修改权限:
chown -R mysql:mysql /data/mysql- 启动 MySQL 服务:
systemctl start mysqld- 从备份信息中获取已执行的 GTID:
cat /data/Xtrabackup/2017-05-18_10-07-06/xtrabackup_info | grep binlog_pos
# 输出示例:binlog_pos = GTID of the last change '132028ab-abc5-11e6-b2f0-000c29a60c3d:1-45675'- 在 Slave 上设置已执行的 GTID 集合(需先确认
SHOW MASTER STATUS中Executed_Gtid_Set为空,否则先执行RESET MASTER):
mysql> SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
mysql> SET @@SESSION.SQL_LOG_BIN = 0;
mysql> SET @@GLOBAL.GTID_PURGED = '132028ab-abc5-11e6-b2f0-000c29a60c3d:1-45675';
mysql> SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;- 配置并启动复制:
mysql> CHANGE MASTER TO
-> MASTER_HOST = 'your_master_ip',
-> MASTER_PORT = 3306,
-> MASTER_USER = 'repl',
-> MASTER_PASSWORD = 'your_password',
-> MASTER_AUTO_POSITION = 1;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G;三、GTID 复制错误处理实战
在 GTID 主主复制环境中,偶尔会遇到复制中断的情况(如主键冲突、索引重复等),此时需要跳过特定的错误事务。以下介绍几种常用的处理方案。
3.1 方法一:注入空事务跳过特定 GTID
这是最精确、最常用的方法,通过注入空事务来标记某个 GTID 已执行。
步骤一:定位出错的 GTID
通过 SHOW SLAVE STATUS\G 分析:
mysql> SHOW SLAVE STATUS\G;
-- 关注以下字段:
-- Retrieved_Gtid_Set: 6bbaffcd-67bc-11e7-9897-b083fee3a6c4:821732,
-- 7747baa0-67af-11e7-a17a-14187749f08f:1-158507467
-- Executed_Gtid_Set: 6bbaffcd-67bc-11e7-9897-b083fee3a6c4:1-821732对比 Retrieved_Gtid_Set(已接收)与 Executed_Gtid_Set(已执行),找出未执行的 GTID。
如果 GTID 出现间隙分段且持续跳动,无法判断跳过点,可以直接从 relay log 中查找:
mysqlbinlog --base64-output=decode-rows -vv relay-bin.002226 | grep -C 20 'CREATE UNIQUE INDEX'输出示例:
# at 295633394
#180302 9:58:27 server id 100 end_log_pos 295633286 CRC32 0xf9a0d370 GTID last_committed=210240 sequence_number=210241
SET @@SESSION.GTID_NEXT= '7747baa0-67af-11e7-a17a-14187749f08f:157539539'/*!*/;
# at 295633459
#180302 9:58:27 server id 100 end_log_pos 295633454 CRC32 0xee0146fc Query thread_id=22555406 exec_time=0 error_code=0
use `xfdrippay`/*!*/;
SET TIMESTAMP=1519955907/*!*/;
CREATE UNIQUE INDEX acup ON user_contract_relation(appid,channel,userid,planid)
/*!*/;步骤二:执行空事务跳过
-- 1. 停止 Slave
mysql> STOP SLAVE;
-- 2. 设置要跳过的 GTID(从 Retrieved_Gtid_Set 获取)
mysql> SET @@SESSION.GTID_NEXT = '7747baa0-67af-11e7-a17a-14187749f08f:157539539';
-- 3. 提交空事务
mysql> BEGIN; COMMIT;
-- 4. 恢复 GTID 为自动模式
mysql> SET SESSION GTID_NEXT = AUTOMATIC;
-- 5. 重新启动 Slave
mysql> START SLAVE;3.2 方法二:使用 pt-slave-restart 工具
Percona Toolkit 中的 pt-slave-restart 可以自动监控并跳过指定的复制错误:
# 跳过所有 1062 错误(主键冲突)
pt-slave-restart --user=root --password='your_password' \
--socket=/var/lib/mysql/mysql.sock \
--error-numbers=1062
# 跳过包含特定表名的错误
pt-slave-restart --user=root --password='your_password' \
--socket=/var/lib/mysql/mysql.sock \
--error-text="test.t1"注意:老版本的pt-slave-restart不支持 GTID 模式。在 GTID 模式下,SET GLOBAL SQL_SLAVE_SKIP_COUNTER无法使用,需升级到支持 GTID 的新版本,或使用方法一的手动注入方式。
3.3 方法三:reset master 方式(不推荐)
此方法通过重置 Master 状态来跳过一段 GTID 范围:
mysql> STOP SLAVE;
mysql> RESET MASTER;
mysql> SET @@GLOBAL.GTID_PURGED = '8f9e146f-0a18-11e7-810a-0050568833c8:1-4';
mysql> START SLAVE;这些命令的含义是:忽略 8f9e146f-0a18-11e7-810a-0050568833c8:1-4 这段 GTID 事务,从 GTID 序号 5 开始继续复制。
不推荐原因:RESET MASTER会清空 binlog。在双主架构中,必须确保另一台已完全完成同步,然后需要重新搭建复制关系(STOP SLAVE->RESET SLAVE->START SLAVE),操作复杂度高且风险大。
3.4 其他工具与参数
使用 slave_exec_mode 参数(动态修改,仅跳过 duplicate-key 和 no-key-found 错误):
mysql> SET GLOBAL slave_exec_mode = 'IDEMPOTENT';使用 slave_skip_errors 参数(需重启,非 Dynamic 变量):
[mysqld]
slave_skip_errors = 10623.5 常见错误:函数创建权限
在从库同步时,可能遇到以下错误:
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,
or READS SQL DATA in its declaration and binary logging is enabled
(you *might* want to use the less safe log_bin_trust_function_creators variable)解决方法:
mysql> SET GLOBAL log_bin_trust_function_creators = 1;四、MySQL + Keepalived 双主高可用架构
4.1 架构概述
在生产环境中,单纯的 MySQL 主主复制仍然存在单点故障风险:如果某一台 MySQL 宕机,应用程序需要手动切换数据库连接地址。为此,引入 Keepalived 实现虚拟 IP(VIP)漂移,配合双主架构,可以构建自动故障转移的高可用方案。
架构要点:
- 两台 MySQL 互为主从(双主),均可读写
- Keepalived 在两台服务器上运行,提供 VIP(如
10.215.182.113) - 正常情况下,VIP 绑定在优先级较高的 MasterA 上
- 当 MasterA 的 MySQL 服务异常时,Keepalived 检测脚本触发 VIP 漂移到 MasterB
- 应用程序始终连接 VIP,无需感知后端 MySQL 的切换
4.2 安装 MySQL 与 Keepalived
安装 MySQL 5.7.18:
wget https://downloads.mysql.com/archives/get/file/mysql-5.7.18-1.el7.x86_64.rpm-bundle.tar
# 卸载系统自带的 MariaDB
rpm -qa | grep mariadb | xargs rpm -e --nodeps
# 按顺序安装 RPM 包
rpm -ivh mysql-community-common-5.7.18-1.el7.x86_64.rpm
rpm -ivh mysql-community-libs-5.7.18-1.el7.x86_64.rpm
rpm -ivh mysql-community-libs-compat-5.7.18-1.el7.x86_64.rpm
rpm -ivh mysql-community-devel-5.7.18-1.el7.x86_64.rpm
rpm -ivh mysql-community-client-5.7.18-1.el7.x86_64.rpm
rpm -ivh mysql-community-server-5.7.18-1.el7.x86_64.rpm安装 Keepalived:
yum install -y keepalived4.3 MySQL 初始化
# 创建所需目录
mkdir -p /data/mysql/3306/binlog
mkdir -p /data/mysql/3306/tmp
mkdir -p /data/mysql/3306/slowlog
# 启动 MySQL
systemctl start mysqld
# 获取初始临时密码
grep 'temporary password' /var/log/mysqld.log
# 登录并修改密码
mysql -p'临时密码'
mysql> ALTER USER root@'localhost' IDENTIFIED BY 'new_password';
mysql> FLUSH PRIVILEGES;4.4 Keepalived 配置
Master 端配置(优先级 100)
global_defs {
notification_email {
user@example.com
}
notification_email_from user@example.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_Mysql
}
vrrp_instance HA_1 {
state BACKUP # 两台均设为 BACKUP,配合非抢占模式
interface bond1
virtual_router_id 221
priority 100 # 主节点优先级较高
advert_int 2
nopreempt # 非抢占模式
authentication {
auth_type PASS
auth_pass mycat
}
virtual_ipaddress {
10.215.182.113/24
}
}
virtual_server 10.215.182.113 3306 {
delay_loop 2
lb_algo wlc
lb_kind DR
nat_mask 192.168.37.35
protocol TCP
persistence_timeout 60
real_server 10.116.103.90 3306 {
weight 1
notify_down /etc/keepalived/mysqlcheck/mysql.sh
TCP_CHECK {
connect_port 3306
connect_timeout 3
nb_get_retry 2
delay_before_retry 1
}
}
}Backup 端配置(优先级 90)
global_defs {
notification_email {
user@example.com
}
notification_email_from user@example.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_Mysql
}
vrrp_instance HA_1 {
state BACKUP
interface ens2f0
virtual_router_id 221
priority 90
advert_int 2
authentication {
auth_type PASS
auth_pass mycat
}
virtual_ipaddress {
10.215.182.113
}
}
virtual_server 10.215.182.113 3306 {
delay_loop 2
lb_algo wlc
lb_kind DR
nat_mask 192.168.37.35
protocol TCP
persistence_timeout 60
real_server 10.84.178.157 3306 {
weight 1
notify_down /etc/keepalived/mysqlcheck/mysql.sh
TCP_CHECK {
connect_port 3306
connect_timeout 3
nb_get_retry 2
delay_before_retry 1
}
}
}MySQL 健康检测脚本(/etc/keepalived/mysqlcheck/mysql.sh):
#!/bin/sh
# 当检测到 MySQL 不可用时,停止本机 Keepalived 以实现 VIP 漂移
/usr/bin/systemctl stop keepalived.servicechmod u+x /etc/keepalived/mysqlcheck/mysql.sh启动 Keepalived
systemctl start keepalived
# 不重启重新加载配置
systemctl reload keepalived4.5 配置说明
- 非抢占模式:两台 Keepalived 的
state均设为BACKUP,配合nopreempt参数。这样即使高优先级节点恢复后,也不会立即抢回 VIP,避免频繁切换导致的连接中断。 - 健康检测:通过
TCP_CHECK检测 MySQL 3306 端口。当检测失败时,触发notify_down脚本停止本地 Keepalived,VIP 自动漂移到备用节点。 - virtual_router_id:主备必须一致(同一虚拟路由组)。
五、MySQL Proxy 读写分离
在双主高可用架构基础上,引入读写分离可以进一步提升系统的吞吐能力。MySQL Proxy 是官方提供的轻量级中间件,通过 Lua 脚本实现 SQL 语句的路由。
5.1 基本配置
/opt/mysql_proxy/bin/mysql-proxy \
--daemon \
--log-level=debug \
--log-file=/data/log/mysql/mysql-proxy.log \
--plugins=proxy \
-b 127.0.0.1:3306 \
-r 127.0.0.1:3307 \
--proxy-lua-script=/opt/mysql_proxy/lib/mysql-proxy/lua/rw-splitting.lua \
--plugins=admin \
--admin-username=admin \
--admin-password=admin \
--admin-lua-script=/opt/mysql_proxy/lib/mysql-proxy/lua/admin.lua参数说明:
| 参数 | 说明 |
|---|---|
--daemon | 以守护进程方式运行 |
--plugins=proxy | 加载代理插件 |
-b | 后端写库(Master)地址 |
-r | 后端读库(Slave)地址 |
--proxy-lua-script | 读写分离 Lua 脚本路径 |
--plugins=admin | 加载管理插件 |
--admin-username / --admin-password | 管理接口认证 |
5.2 压力测试工具 mysqlslap
MySQL 自带的 mysqlslap 可用于模拟并发负载,验证读写分离效果:
mysqlslap --help关键参数:
| 参数 | 说明 |
|---|---|
--concurrency=10,50,100 | 并发连接线程数,支持逗号分隔多个值 |
--engines | 测试的存储引擎 |
--iterations | 测试运行的次数 |
--auto-generate-sql | 自动生成 SQL 脚本进行测试 |
--auto-generate-sql-load-type | 测试类型:read、write、update、mixed |
--number-of-queries | 总共运行的查询数 |
--debug-info | 额外输出 CPU 及内存相关信息 |
--number-int-cols | 测试表中 INT 类型字段数量 |
--number-char-cols | 测试表中 CHAR 类型字段数量 |
--create-schema | 指定测试的数据库(Schema) |
--query | 使用自定义 SQL 或存储过程执行测试 |
示例:
mysqlslap \
--user=root --password='your_password' \
--host=127.0.0.1 --port=3306 \
--concurrency=50,100 \
--iterations=10 \
--auto-generate-sql \
--auto-generate-sql-load-type=mixed \
--number-of-queries=10000 \
--create-schema=testdb \
--debug-info六、备份策略与自动化
在任何高可用方案中,备份都是最后一道防线。推荐使用 Percona Xtrabackup 实现物理热备份,它支持 InnoDB 的非阻塞备份,不会影响业务运行。
6.1 Xtrabackup 安装
# 安装依赖
rpm -ivh libev-4.15-3.el7.x86_64.rpm
rpm -ivh perl-DBD-MySQL-4.023-5.el7.x86_64.rpm
rpm -ivh perl-Digest-MD5-2.52-3.el7.x86_64.rpm
# 安装 Xtrabackup
rpm -ivh percona-xtrabackup-24-2.4.7-1.el7.x86_64.rpm6.2 备份策略设计
推荐的备份策略为:
- 每天凌晨 04:00:执行一次全量备份
- 每隔 6 小时:在上一次备份基础上执行增量备份
- 保留 2 天:自动清理超过 2 天的备份文件
6.3 备份脚本核心逻辑
#!/bin/bash
# 备份工具:percona-xtrabackup-24-2.4.7
# 备份策略:
# (1) 每天凌晨 04:00 进行全量备份
# (2) 每隔 6 小时增量备份一次
# (3) 自动清理 2 天前的备份
xtrabackup_path="/usr/bin/innobackupex"
mysql_cnf_path="/etc/my.cnf"
thread_cnt=4
# 全量备份
if [[ "$1" == "full" ]]; then
${xtrabackup_path} \
--defaults-file=${mysql_cnf_path} \
--user=xtrabackup \
--password='your_password' \
--socket=/var/lib/mysql/mysql.sock \
--parallel=${thread_cnt} \
--no-timestamp \
${current_bak_path}
echo "NULL|${current_bak_path}|full" >> ${inc_base_list}
fi
# 增量备份
if [[ "$1" == "inc" ]]; then
prev_backup_dir=$(sed '/^$/d' ${inc_base_list} | tail -1 | awk -F '|' '{print $2}')
${xtrabackup_path} \
--defaults-file=${mysql_cnf_path} \
--user=xtrabackup \
--password='your_password' \
--socket=/var/lib/mysql/mysql.sock \
--parallel=${thread_cnt} \
--no-timestamp \
--incremental ${current_bak_path} \
--incremental-basedir=${prev_backup_dir}
echo "${prev_backup_dir}|${current_bak_path}|inc" >> ${inc_base_list}
fi
# 清理 2 天前的备份
rm -rf ${backup_base_dir}/$(date -d '2 days ago' +'%F')_*
sed -i "/$(date -d '2 days ago' +'%F')/d" ${inc_base_list}6.4 计划任务
# 配置 crontab
00 04 * * * /data/scripts/mysqlbackup.sh full >/dev/null 2>&1
00 */6 * * * /data/scripts/mysqlbackup.sh inc >/dev/null 2>&1总结
本文从 MySQL 主从复制延迟问题出发,系统性地梳理了以下关键技术要点:
- 多线程复制(MTS):MySQL 5.7 基于
LOGICAL_CLOCK的并行复制机制,通过slave_parallel_workers和slave_parallel_type参数配置,显著降低了主从延迟。 - GTID 主主复制:GTID 为每个事务提供全局唯一标识,简化了复制配置和故障切换。双主架构中需注意
auto_increment_offset和auto_increment_increment的设置以避免自增 ID 冲突。 - GTID 错误处理:通过注入空事务跳过特定 GTID 是推荐的标准操作方式,
pt-slave-restart工具可实现自动化跳过。 - Keepalived 高可用:通过 VIP 漂移实现 MySQL 双主的自动故障转移,配合健康检测脚本和 TCP_CHECK 机制,确保服务连续性。
- 读写分离:MySQL Proxy 结合 Lua 脚本可实现灵活的路由策略,mysqlslap 工具可用于性能验证。
- 备份策略:Xtrabackup 的「全量 + 增量」备份方案是最佳实践,通过 crontab 实现自动化,确保数据安全。
将这些技术组合使用,可以构建一套完整、稳定、可运维的 MySQL 高可用复制架构,满足绝大多数互联网业务的需求。在实际落地时,建议先在测试环境中充分验证,尤其要模拟主从切换、故障恢复等场景,确保各项参数和脚本的正确性。
暂无评论