ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

MySQL 8.0主从复制实战:从零搭建高可用数据库架构

MySQL 8.0主从复制实战:从零搭建高可用数据库架构

1. 项目概述与核心价值

最近在帮一个朋友的公司做数据库架构优化,他们业务量上来了,单台MySQL服务器开始有点力不从心,经常在业务高峰期出现响应延迟。考虑到数据安全和高可用性,我建议他们先从最经典、最稳定的MySQL主从复制架构入手。这次我选用了MySQL 8.0版本,相比5.7,它在复制性能、数据一致性和管理便捷性上都有不少提升,比如默认的caching_sha2_password认证插件、更强的JSON支持以及更完善的复制监控。对于很多中小型团队来说,自己动手部署一套主从复制,不仅是解决当前性能瓶颈的性价比之选,更是深入理解数据库高可用架构的绝佳实践。这篇文章,我就把这次从零开始部署MySQL 8.0主从复制的完整过程、踩过的坑以及一些确保稳定性的经验技巧,毫无保留地分享出来。无论你是运维工程师、后端开发,还是对数据库架构感兴趣的技术爱好者,跟着这篇指南,你都能在自己的测试环境甚至生产环境,搭建出一套可靠的主从复制系统。

2. 环境规划与前期准备

部署主从复制不是简单地安装两个MySQL实例然后改改配置就行,前期的规划直接决定了后续的稳定性和运维复杂度。一个清晰的规划能避免很多后期调整的麻烦。

2.1 服务器与网络规划

我这次使用的是两台CentOS 7.9的虚拟机,配置都是4核8G。在实际生产环境中,主库(Master)的配置通常需要比从库(Slave)更高,因为要处理所有的写操作和部分实时读请求。

  • 主库 (Master): IP: 192.168.1.100, 主机名: mysql-master
  • 从库 (Slave): IP: 192.168.1.101, 主机名: mysql-slave

网络要求

  1. 双向通信:主从服务器之间必须能互相ping通,并且需要确保防火墙开放了MySQL的默认端口3306。我习惯先用telnet命令测试一下:telnet 192.168.1.100 3306telnet 192.168.1.101 3306
  2. 时间同步:这是至关重要却常被忽视的一点。如果主从服务器系统时间偏差过大,会导致复制延迟监控不准,甚至在某些依赖时间戳的场合引发数据一致性问题。务必使用NTP服务确保时间同步。可以使用chronyd服务:
    # 安装并启动chronyd(CentOS 7+) yum install -y chrony systemctl start chronyd systemctl enable chronyd # 检查同步状态 chronyc sources -v
  3. 主机名解析:最好在/etc/hosts文件中做好主机名映射,避免依赖不可靠的DNS解析。
    # 在两台服务器的 /etc/hosts 文件中添加 192.168.1.100 mysql-master 192.168.1.101 mysql-slave

2.2 MySQL 8.0安装与基础配置

我选择通过MySQL官方的Yum仓库来安装,这样便于后续的版本升级和管理。如果你追求极致的控制,编译安装也是可以的,但步骤会繁琐一些。

安装步骤(在主从服务器上执行相同操作)

  1. 下载并安装MySQL官方的Yum仓库。
    wget https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm sudo rpm -ivh mysql80-community-release-el7-7.noarch.rpm
  2. 安装MySQL服务器和客户端。
    sudo yum install -y mysql-community-server mysql-community-client
  3. 启动MySQL服务并设置开机自启。
    sudo systemctl start mysqld sudo systemctl enable mysqld
  4. 获取初始临时密码。MySQL 8.0首次启动后,root用户的密码会写在日志文件中。
    sudo grep 'temporary password' /var/log/mysqld.log
  5. 使用临时密码登录,并立即修改密码。MySQL 8.0有密码强度策略,需要设置一个包含大小写字母、数字和特殊字符的强密码。
    mysql -uroot -p # 输入查到的临时密码 ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourNewStrongPassword123!'; FLUSH PRIVILEGES;

注意:很多新手在这里会卡住,因为MySQL 8.0的密码策略默认是MEDIUM,要求密码长度至少8位,包含大小写字母、数字和特殊字符。如果只是想测试,可以临时修改策略:SET GLOBAL validate_password.policy=LOW;,但生产环境强烈不建议。

基础安装完成后,我们先不急着配置主从,而是统一一下基础配置文件/etc/my.cnf中的一些通用设置,比如字符集、默认存储引擎等,确保主从库的基础环境一致。

3. 主库(Master)配置详解

主库的配置核心是开启二进制日志(Binary Log)并为其设置一个唯一的服务器ID。二进制日志记录了所有对数据库的更改操作,是从库进行数据同步的数据来源。

3.1 配置文件修改

编辑主库的/etc/my.cnf文件,在[mysqld]部分添加或修改以下参数:

[mysqld] # 服务器唯一ID,主从不能相同,通常主库设为1 server-id = 1 # 启用二进制日志,并指定日志文件的前缀 log-bin = mysql-bin # 设置二进制日志格式为ROW,这是MySQL 8.0的推荐格式,能提供最安全的数据一致性 binlog_format = ROW # 指定需要复制的数据库,如果有多个,可以写多行。这里以`testdb`为例。 binlog-do-db = testdb # (可选)指定不需要复制的数据库,如系统库。生产环境建议复制除`mysql`、`sys`、`performance_schema`、`information_schema`外的所有库。 binlog-ignore-db = mysql binlog-ignore-db = sys binlog-ignore-db = performance_schema binlog-ignore-db = information_schema # 设置二进制日志过期时间,避免磁盘被占满(单位:天) expire_logs_days = 7 # 控制每个二进制日志文件的最大大小(单位:字节) max_binlog_size = 100M

参数解读

  • server-id:这是复制的基石,必须唯一。网络上有不少教程忘记设置这个,导致复制无法启动。
  • binlog_format=ROW:这是关键选择。STATEMENT格式记录SQL语句,可能因为函数(如NOW())或触发器导致主从不一致。ROW格式记录每行数据的变化,更安全,但日志量稍大。MIXED是混合模式,MySQL自行判断。对于数据一致性要求高的场景,ROW是唯一选择。
  • binlog-do-dbbinlog-ignore-db:用于过滤数据库。注意,使用过滤参数时要格外小心。如果跨库操作(比如USE db1; UPDATE db2.table ...),过滤规则可能会产生意想不到的结果。对于大多数需要全库复制的场景,我更倾向于不设置过滤,而是在从库上使用replicate-do-db进行过滤。

修改完成后,重启MySQL服务使配置生效:

sudo systemctl restart mysqld

3.2 创建复制专用账户

为了让从库能够连接主库并读取二进制日志,我们需要在主库上创建一个专门用于复制的用户。这个用户的权限不需要太大,够用就行,遵循最小权限原则。

登录主库MySQL,执行以下SQL:

-- 创建用户`repl`,并允许其从`192.168.1.101`(从库IP)登录。如果从库IP不固定,可以用`%`,但安全性降低。 CREATE USER 'repl'@'192.168.1.101' IDENTIFIED WITH 'caching_sha2_password' BY 'ReplPassword123!'; -- 授予复制权限。`REPLICATION SLAVE`权限允许从库连接并读取二进制日志。 GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.101'; -- 刷新权限 FLUSH PRIVILEGES;

实操心得:MySQL 8.0默认使用caching_sha2_password认证插件,比旧的mysql_native_password更安全。如果从库是MySQL 5.7等旧版本,连接可能会失败,需要将主库用户认证插件改为mysql_native_password,或者确保从库支持新插件。为了省事,在纯8.0环境就用默认的。

3.3 获取主库状态信息

在开始配置从库之前,我们需要记录下主库当前二进制日志的状态,从库需要从这个精确的位置开始同步。

在主库执行:

SHOW MASTER STATUS;

你会看到类似下面的输出:

+------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000001 | 157 | testdb | mysql | | +------------------+----------+--------------+------------------+-------------------+

务必记下FilePosition的值(这里是mysql-bin.000001157)。在配置从库时,你需要告诉它从哪个日志文件的哪个位置开始读。如果此时主库还有写操作,这个位置会变,所以最好在业务低峰期操作,或者短暂锁表(FLUSH TABLES WITH READ LOCK;)后快速获取状态并解锁(UNLOCK TABLES;)。

4. 从库(Slave)配置与数据同步

从库的配置核心是指定主库是谁,以及从哪里开始同步。

4.1 从库基础配置

编辑从库的/etc/my.cnf文件:

[mysqld] # 服务器唯一ID,必须与主库不同 server-id = 2 # 启用中继日志,从库用中继日志来接收和应用主库的二进制日志 relay-log = mysql-relay-bin # 允许从库在应用完中继日志后,将其删除(默认是关闭的,建议开启) relay_log_purge = ON # (可选)指定从库需要复制的数据库,与主库的`binlog-do-db`对应。如果主库没过滤,这里也可以过滤。 # replicate-do-db = testdb # 设置从库为只读,防止应用误操作写入从库导致数据不一致 read_only = ON

重要提示read_only = ON对拥有SUPER权限的用户(如root)无效。如果想彻底禁止写,可以考虑设置super_read_only = ON,但这可能会影响一些管理操作。

修改后重启从库MySQL服务:

sudo systemctl restart mysqld

4.2 配置主从复制链路

这是最关键的一步,告诉从库去连接哪个主库,用什么账号,从哪个位置开始同步。

登录从库MySQL,执行以下命令:

-- 停止从库复制线程(如果是新从库,本来也是停止的,但执行一下更保险) STOP SLAVE; -- 配置主库连接信息 CHANGE MASTER TO MASTER_HOST='192.168.1.100', -- 主库IP MASTER_USER='repl', -- 主库创建的复制账号 MASTER_PASSWORD='ReplPassword123!', -- 复制账号密码 MASTER_LOG_FILE='mysql-bin.000001', -- 主库`SHOW MASTER STATUS`看到的File MASTER_LOG_POS=157; -- 主库`SHOW MASTER STATUS`看到的Position -- 启动从库复制线程 START SLAVE;

CHANGE MASTER TO命令的各个参数必须准确无误。特别是MASTER_LOG_FILEMASTER_LOG_POS,如果填错,从库会从错误的位置开始同步,导致数据缺失或重复。

4.3 初始化数据同步(全量备份与恢复)

如果主库是一个已经运行了一段时间、有大量数据的数据库,那么仅仅配置复制链路是不够的。因为从库是空的,它需要先获得一份主库数据的完整副本,然后才能从指定的日志位置开始增量同步。

标准做法是使用mysqldump进行逻辑备份。这是最通用、兼容性最好的方法。

  1. 在主库上进行全量备份

    # 备份所有数据库(排除系统库) mysqldump -uroot -p --all-databases \ --master-data=2 \ # 这个参数会在备份文件中记录备份时刻的二进制日志位置,非常关键! --single-transaction \ # 开启事务,确保备份数据的一致性,对InnoDB表有效 --routines \ # 备份存储过程和函数 --events \ # 备份事件 --triggers \ # 备份触发器 > /tmp/full_backup.sql

    --master-data=2参数会将CHANGE MASTER TO语句以注释的形式写入备份文件开头,并记录准确的FilePosition。这样在从库恢复后,可以直接用这个位置信息配置复制,无需再回主库查询。

  2. 将备份文件传输到从库

    scp /tmp/full_backup.sql root@192.168.1.101:/tmp/
  3. 在从库上恢复数据

    mysql -uroot -p < /tmp/full_backup.sql

    恢复过程可能较长,取决于数据库大小。

  4. 从备份文件中获取复制起始点

    head -n 50 /tmp/full_backup.sql | grep "CHANGE MASTER TO"

    你会看到类似-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=156;的注释。这个位置就是备份开始时主库的二进制日志位置。

  5. 在从库上重新配置复制链路: 由于我们已经恢复了数据,需要让从库从这个备份时刻的位置开始同步,而不是之前记录的Position=157

    STOP SLAVE; CHANGE MASTER TO MASTER_HOST='192.168.1.100', MASTER_USER='repl', MASTER_PASSWORD='ReplPassword123!', MASTER_LOG_FILE='mysql-bin.000002', -- 使用备份文件中的日志文件 MASTER_LOG_POS=156; -- 使用备份文件中的位置 START SLAVE;

4.4 检查复制状态

配置并启动复制后,必须检查从库的复制状态,确认一切正常。

在从库上执行:

SHOW SLAVE STATUS\G

使用\G是为了以垂直格式显示,更易读。你需要重点关注以下几列:

  • Slave_IO_Running: 必须为Yes。表示从库的IO线程(负责从主库拉取二进制日志)运行正常。
  • Slave_SQL_Running: 必须为Yes。表示从库的SQL线程(负责执行中继日志中的事件)运行正常。
  • Last_IO_Error: 应该为空。如果有错误信息,说明IO线程连接主库或读取日志出了问题。
  • Last_SQL_Error: 应该为空。如果有错误信息,说明SQL线程执行SQL语句时出错(如主键冲突)。
  • Seconds_Behind_Master: 复制延迟的秒数。理想情况下是0。如果这个值持续很大,说明从库处理速度跟不上主库,需要排查性能瓶颈。
  • Master_Log_File/Read_Master_Log_Pos: 从库IO线程当前读取到的主库二进制日志文件和位置。
  • Relay_Master_Log_File/Exec_Master_Log_Pos: 从库SQL线程当前执行到的主库二进制日志文件和位置。当Exec_Master_Log_Pos追上Read_Master_Log_Pos,且Seconds_Behind_Master为0时,表示复制完全同步。

一个健康的复制状态输出,Slave_IO_RunningSlave_SQL_Running都应该是Yes,且没有错误信息。

5. 主从复制监控与日常运维

部署完成只是第一步,持续的监控和正确的运维才能保证复制架构长期稳定运行。

5.1 监控复制状态与延迟

除了手动执行SHOW SLAVE STATUS,在生产环境中应该建立自动化监控。

  1. 脚本监控:可以编写一个Shell脚本,定期检查SHOW SLAVE STATUS的关键字段,如果发现Slave_IO_RunningSlave_SQL_Running不是Yes,或者Last_IO_Error/Last_SQL_Error有值,就通过邮件、钉钉、企业微信等渠道告警。
  2. 监控系统集成:像Prometheus这样的监控系统,可以通过mysqld_exporter来采集MySQL的各类指标,包括复制状态和延迟,并在Grafana中绘制成直观的图表。
  3. 性能视图:MySQL 8.0的performance_schema库中提供了replication_connection_statusreplication_applier_status等表,可以用SQL更灵活地查询复制信息。

5.2 常见问题排查与修复

复制过程中难免会遇到错误,下面是一些典型场景及处理方法。

问题1:主键冲突错误 (Last_SQL_Error: Could not execute... Duplicate entry...)

原因:最可能的原因是从库被意外写入了数据(比如有人直接用root账号在从库做了INSERT操作),导致与主库同步过来的数据产生冲突。

解决

  1. 临时跳过错误(慎用!仅用于紧急恢复,且必须确认跳过的操作不会影响业务逻辑):
    STOP SLAVE; SET GLOBAL sql_slave_skip_counter = 1; -- 跳过1个事件 START SLAVE;
    或者,在my.cnf中配置slave_skip_errors(不推荐,可能掩盖真正的问题)。
  2. 根治方法:重建从库。如果冲突频繁或数据已混乱,最彻底的办法是停止从库,清空数据,重新用mysqldump或物理备份工具(如Percona XtraBackup)从主库做一次全量同步。

问题2:复制延迟持续增大 (Seconds_Behind_Master 一直很高)

原因

  • 硬件瓶颈:从库服务器性能(CPU、磁盘IO、网络)远低于主库。
  • 单线程应用:传统的MySQL复制,SQL线程是单线程的,如果主库并发写很高,从库可能应用不过来。特别是对大表无主键更新、长事务等操作。
  • 从库有查询压力:如果从库承担了大量读请求,可能会与应用日志的SQL线程争抢资源。

解决

  1. 检查硬件:对比主从服务器的iostatvmstat等指标,看是否存在明显的磁盘IO或CPU瓶颈。
  2. 优化从库配置:确保innodb_buffer_pool_size设置合理,为从库配置更好的磁盘(如SSD)。
  3. 使用多线程复制:MySQL 5.6+支持基于库级别的并行复制,8.0更是支持了WRITESET等更高效的并行复制方式。可以在从库配置:
    slave_parallel_workers = 4 # 设置并行工作线程数,通常设置为CPU核心数 slave_parallel_type = LOGICAL_CLOCK # MySQL 8.0推荐使用LOGICAL_CLOCK
  4. 分离读写:确保业务程序不会在从库上执行写操作。检查read_onlysuper_read_only是否生效。
  5. 优化SQL:检查主库是否有慢查询,导致产生巨大的二进制日志事件。优化这些SQL能从源头减少延迟。

问题3:主库二进制日志被过早清除

原因:从库因为网络中断、宕机等原因停止复制一段时间,而主库的expire_logs_daysmax_binlog_size设置较小,导致从库尚未同步的二进制日志已经被删除。

解决

  1. 预防:合理设置主库的expire_logs_days(例如7-14天),并监控从库延迟。确保从库延迟时间小于二进制日志保留时间。
  2. 修复:如果已经发生,从库会报Could not find first log file name in binary log index file之类的错误。此时唯一的办法是重新建立复制:在主库做一次全量备份,在从库恢复,然后从新的位置开始复制。这再次凸显了监控复制延迟的重要性。

5.3 主从切换与故障转移

主从架构的一个核心价值是提供故障转移能力。当主库宕机时,需要快速将某个从库提升为新的主库。

手动切换步骤

  1. 确认主库故障:确保原主库确实无法恢复或恢复时间不可接受。
  2. 选择新主库:选择一个数据最接近原主库(延迟最小)、状态健康的从库。
  3. 提升从库为主库
    • 在新主库上执行:STOP SLAVE;停止复制。
    • 执行:RESET SLAVE ALL;清除所有复制信息,使其完全独立。
    • 执行:SET GLOBAL read_only = OFF;关闭只读模式。
    • 如果原主库的二进制日志还能访问,可以尝试让其他从库指向这个新主库,但这通常比较复杂。
  4. 修改应用配置:将应用程序的数据库连接地址从原主库IP改为新主库IP。
  5. 其他从库指向新主库:如果还有其他从库,需要让它们从新的主库开始复制。这需要在新主库上创建新的复制账号,并在其他从库上执行CHANGE MASTER TO命令,指向新主库。同时,需要在新主库上执行SHOW MASTER STATUS获取新的日志位置。

重要提示:手动切换存在数据丢失风险(最后一次主从同步到故障时刻之间的数据可能丢失)。对于要求高可用的系统,建议考虑基于MHA(Master High Availability)、Orchestrator等工具的半自动/自动故障转移方案,或者直接使用MySQL Group Replication、InnoDB Cluster等原生高可用方案。

6. 进阶配置与性能优化

基础的主从复制搭建完成后,可以根据业务需求进行一些进阶配置,以提升复制效率或满足特定场景。

6.1 半同步复制配置

默认的复制是异步的,主库提交事务后,不等从库确认就返回给客户端,存在极小概率的数据丢失风险。半同步复制要求主库在提交事务时,至少有一个从库确认收到了该事务的二进制日志,才返回成功给客户端。

配置步骤

  1. 在主库和从库上安装半同步复制插件(MySQL 8.0默认已安装,只需启用):
    -- 在主库执行 INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; SET GLOBAL rpl_semi_sync_master_enabled = ON; -- 在从库执行 INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; SET GLOBAL rpl_semi_sync_slave_enabled = ON;
    可以将rpl_semi_sync_master_enabledrpl_semi_sync_slave_enabled写入my.cnf使其永久生效。
  2. 重启从库的IO线程以启用半同步连接:
    STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
  3. 检查状态
    -- 在主库查看 SHOW VARIABLES LIKE 'rpl_semi_sync%enabled'; SHOW STATUS LIKE 'Rpl_semi_sync_master_status'; -- 应为ON

注意:半同步复制会轻微增加主库的写延迟,因为需要等待从库的网络往返。可以设置超时参数rpl_semi_sync_master_timeout(单位毫秒),如果超时后没有从库确认,主库会自动降级为异步复制。

6.2 基于GTID的复制

全局事务标识符(GTID)是MySQL 5.6引入的特性,它为每个提交的事务分配一个全局唯一的ID。使用GTID复制,可以极大简化主从切换和故障恢复的步骤,你不再需要关心具体的二进制日志文件和位置。

启用GTID: 在主库和从库的my.cnf中添加:

[mysqld] gtid_mode = ON enforce_gtid_consistency = ON

重启MySQL服务后,在配置从库时,CHANGE MASTER TO命令可以简化为:

CHANGE MASTER TO MASTER_HOST='192.168.1.100', MASTER_USER='repl', MASTER_PASSWORD='ReplPassword123!', MASTER_AUTO_POSITION = 1; -- 关键参数,启用基于GTID的自动定位

启用MASTER_AUTO_POSITION = 1后,从库会自动从它最后一个执行过的GTID之后开始同步,无需手动指定MASTER_LOG_FILEMASTER_LOG_POS

6.3 复制过滤与多源复制

  • 复制过滤:如前所述,可以在主库(binlog-do-db)或从库(replicate-do-db)进行过滤。更推荐在从库过滤,因为更灵活,且不影响其他从库。过滤规则支持通配符,例如replicate-wild-do-table = testdb.article%
  • 多源复制:MySQL 5.7+支持一个从库同时从多个主库复制数据。这适用于数据分片后需要聚合查询的场景。配置时,需要为每个主库连接创建一个独立的复制通道(Channel),使用FOR CHANNEL 'channel_name'语法来管理。

7. 安全加固与备份策略

部署好主从,安全性和备份也不能落下。

  1. 网络与防火墙:严格限制3306端口的访问,只允许应用服务器和从库IP连接主库,只允许监控系统和管理员IP连接从库。
  2. 权限最小化:复制账号repl只授予REPLICATION SLAVE权限。应用账号按需授予SELECT,INSERT,UPDATE,DELETE等权限,禁止SUPER,FILE,PROCESS等敏感权限。
  3. 定期备份主从复制不是备份!它不能防止误删除(比如DROP DATABASE操作会被同步到从库)。必须建立独立的、定期的全量备份和增量备份策略。可以使用mysqldump进行逻辑备份,或者使用Percona XtraBackup进行物理热备,后者速度更快,对大型数据库更友好。
  4. 备份验证:定期测试备份文件的恢复流程,确保备份是有效的。

搭建MySQL主从复制是一个系统工程,涉及规划、部署、监控、优化和运维多个环节。这次分享的步骤是基于最经典的异步复制模式,已经能覆盖绝大多数中小规模场景的需求。在实际操作中,最关键的是理解每个参数和命令背后的含义,遇到问题多看错误日志(/var/log/mysqld.log),善用SHOW SLAVE STATUS这个诊断利器。先把这套基础架构跑稳,后续再根据业务压力,逐步考虑引入读写分离中间件、分库分表、或更高级的MGR集群,你的数据库架构演进之路就会清晰很多。

返回列表