
1. 项目概述为什么我们需要MariaDB主从复制在任何一个稍有规模的线上业务里数据库都是那个最核心、也最脆弱的部件。单点故障、性能瓶颈、备份时的服务中断这些风险就像悬在头顶的达摩克利斯之剑。我经历过不止一次因为一次误操作或者硬件故障导致数据库服务中断整个业务直接停摆那种压力是巨大的。所以当业务发展到一定阶段数据库的高可用和读写分离就不再是“锦上添花”而是“雪中送炭”的必需品。MariaDB作为MySQL的一个强大分支继承了其易用性和高性能同时在一些细节上做得更好。它的主从复制Master-Slave Replication功能就是构建高可用架构的基石。简单来说主从复制就是让一个数据库实例主库的数据变更自动、异步地同步到另一个或多个数据库实例从库。这听起来简单但背后的价值巨大你可以用从库来做读写分离分担主库的查询压力可以用从库做实时备份几乎不影响主库性能甚至可以在从库上进行数据统计、报表生成等重操作而主库则专注于核心交易。很多人一提到主从配置就觉得是DBA的专属领域配置复杂容易出错。其实不然只要你理解了核心原理按照清晰的步骤操作完全可以在自己的开发环境甚至生产环境中搭建起来。今天我就以一个从业者的视角结合我多次搭建和运维的经验带你从零开始手把手配置一套MariaDB主从复制环境。我们会从原理讲起覆盖环境准备、详细配置、排错验证以及最重要的——那些官方文档里不会写的实战经验和避坑指南。无论你是正在学习数据库的开发者还是需要为项目搭建可靠数据层的运维人员这篇文章都能给你提供一份可以直接“抄作业”的实操手册。2. 核心原理拆解二进制日志与三个线程的舞蹈在动手配置之前我们必须先搞清楚MariaDB主从复制到底是怎么“跑”起来的。知其然更要知其所以然这样在遇到同步延迟、数据不一致等问题时你才能快速定位根因而不是盲目地重启服务。MariaDB的主从复制本质上是基于二进制日志Binary Log的异步数据同步。整个过程可以形象地理解为“写日记”和“读日记”的过程。主库Master的角色记录所有变更主库上有一个核心组件叫二进制日志binlog。你可以把它想象成主库的一本“操作流水账”。每当主库执行了一条会修改数据的SQL语句如INSERT, UPDATE, DELETE, CREATE TABLE等这条语句或者其对应的行数据变更就会被“翻译”成特定格式的事件Event并按顺序记录到这本流水账里。binlog不是记录数据页的物理变化而是逻辑上的变更事件这使得它非常高效且灵活。主库上负责写这本流水账的线程我们一般不用关心它由MariaDB内部管理。从库Slave的角色获取并重放日志从库要同步数据需要三个核心线程协同工作I/O线程它的工作就像个“邮差”。这个线程会连接到主库并向主库请求“请把binlog里从某个位置开始的内容发给我”。然后它就持续地从主库拉取新的binlog事件并将这些事件原封不动地写入从库本地的中继日志Relay Log文件中。你可以把中继日志理解为从库本地的“待办事项清单”。SQL线程它的工作就像个“执行者”。这个线程会读取本地的中继日志解析出里面记录的SQL事件并在从库上逐一执行这些SQL从而让从库的数据状态最终和主库保持一致。一个隐藏的协调者实际上在现代版本的MariaDB/MySQL中为了提升并行复制的效率SQL线程可能演变为多个工作线程slave_parallel_workers但逻辑上我们仍可以将其视为一个执行单元。关键概念日志位置Position与GTID这是保证同步不丢、不重的关键。传统方式基于Binlog文件名和位置File Position。每个binlog文件如mysql-bin.000001都有一个内部的偏移量Position如120。从库在启动复制时需要告诉主库“请从mysql-bin.000001文件的第120个字节开始发送日志”。主从双方都记录这个位置以此来判断同步进度。现代方式基于全局事务标识GTID。这是更推荐的方式。GTID为每一个提交的事务生成一个全局唯一ID格式为server_uuid:transaction_id。例如3E11FA47-71CA-11E1-9E33-C80AA9429562:23。使用GTID后从库只需要告诉主库“我已经执行了哪些GTID对应的事务”主库就会自动发送后续的事务。这大大简化了故障切换和主从搭建的复杂度避免了因位置记错导致的数据错乱。注意虽然GTID是趋势但在一些老版本或特定场景下可能仍需使用传统方式。我们接下来的配置会以GTID方式为主进行讲解因为它更健壮。异步复制的利与弊我们配置的默认模式是异步复制。这意味着主库提交事务后只要binlog写入成功就会向客户端返回成功而不会等待从库的I/O线程拉取日志。这带来了高性能和低延迟但也带来了“数据延迟”的风险。极端情况下如果主库宕机且未同步到从库的数据丢失就会造成数据不一致。因此对于金融等强一致性场景可能需要考虑半同步复制Semi-Synchronous Replication它要求至少一个从库确认收到日志后主库才提交但这会牺牲一部分性能。理解了这套“写日志-拉日志-重放日志”的机制后面的所有配置步骤就都变成了对这个机制的参数化实现。接下来我们进入实战环节。3. 环境准备与规划兵马未动粮草先行在开始修改配置文件之前充分的准备工作能避免你掉进很多坑里。我建议你准备两台独立的服务器或虚拟机如果只是学习在本机用不同端口启动两个MariaDB实例也可以但生产环境强烈建议物理分离。3.1 系统与软件环境我以最常见的CentOS 7 / Rocky Linux 8或Ubuntu 20.04/22.04为例其他发行版大同小异。主库服务器IP:192.168.1.100 主机名:master-db从库服务器IP:192.168.1.101 主机名:slave-dbMariaDB版本强烈建议使用10.5及以上版本对GTID的支持更完善功能也更稳定。你可以通过mariadb --version命令查看。如果还没安装可以使用系统包管理器安装CentOS/Rocky:sudo yum install mariadb-server mariadbUbuntu/Debian:sudo apt install mariadb-server3.2 网络与防火墙配置主从服务器之间必须能互相访问对方的MariaDB服务端口默认3306。检查连通性在从库上执行telnet 192.168.1.100 3306在主库上执行telnet 192.168.1.101 3306看是否能连通。配置防火墙如果系统防火墙firewalld或ufw开启需要放行3306端口。firewalld:sudo firewall-cmd --permanent --add-port3306/tcp sudo firewall-cmd --reloadufw:sudo ufw allow from 192.168.1.0/24 to any port 3306(更安全只允许同网段访问)3.3 一个至关重要的前置操作主从数据一致性这是新手最容易忽略也最容易导致复制失败的一点。主从复制不是“魔法”它只能同步配置启动后新产生的数据变更。在开启主从复制之前必须保证主库和从库的初始数据完全一致。假设我们有一个正在运行的主库里面已经有业务数据了。我们需要为从库准备一份主库当前时刻的完整数据快照。有两种主流方法方法一使用mysqldump逻辑备份适用于数据量不大或全库迁移这是最常用、最清晰的方法。它在主库上执行会生成包含所有数据和表结构的SQL文件。# 在主库服务器上执行 # 1. 首先锁定所有表为只读防止备份期间数据变化。时间要尽量短 mysql -uroot -p -e FLUSH TABLES WITH READ LOCK; # 2. 查看当前的二进制日志位置或GTID记录下来这是从库开始同步的起点。 mysql -uroot -p -e SHOW MASTER STATUS; # 输出会显示File和Position如果启用了GTID则执行 SHOW MASTER STATUS\G 查看 Executed_Gtid_Set。 # 3. 在另一个终端开始备份数据库假设数据库名为 app_db mysqldump -uroot -p --single-transaction --master-data2 --routines --triggers --events app_db /tmp/master_dump.sql # 4. 备份完成后立即释放主库的锁 mysql -uroot -p -e UNLOCK TABLES;关键参数解释--single-transaction对于InnoDB表此参数会开启一个事务来确保备份数据的一致性而不是用LOCK TABLES我们之前已经锁了。这是在线备份不锁表的关键。--master-data2这个参数至关重要它会在导出的SQL文件开头以注释的形式写入备份时刻主库的二进制日志文件名和位置CHANGE MASTER TO语句。如果使用GTID它也会包含SET GLOBAL.gtid_slave_pos语句。等我们在从库导入这个文件时这些信息会自动生效。--routines --triggers --events同时导出存储过程、触发器和事件调度器。方法二使用物理备份工具如Mariabackup适用于数据量巨大对于TB级别的大库mysqldump导出和导入会非常慢。这时应该使用MariabackupPercona XtraBackup的MariaDB分支它进行的是物理文件拷贝速度更快且热备份对主库影响极小。其原理是拷贝数据文件并记录备份期间的日志位置。使用相对复杂一些需要额外步骤来准备prepare备份集。这里不展开但你需要知道有这种更专业的方案。将备份文件master_dump.sql拷贝到从库服务器然后在从库上导入# 在从库服务器上执行 mysql -uroot -p -e CREATE DATABASE app_db; # 如果备份文件不包含建库语句 mysql -uroot -p app_db /tmp/master_dump.sql至此从库已经拥有了和主库在备份时刻完全一致的数据。接下来我们开始配置主从复制的核心参数。4. 主库Master配置详解打开大门并制作钥匙主库的配置核心就两件事1. 开启二进制日志2. 创建一个专门用于复制的用户账号。我们通过修改MariaDB的配置文件来实现。4.1 配置文件修改MariaDB的主配置文件通常是/etc/my.cnf或/etc/mysql/mariadb.conf.d/50-server.cnf。找到[mysqld]段落添加或修改以下参数[mysqld] # 服务器唯一ID主从必须不同。通常用IP最后一段。 server-id 100 # 启用二进制日志并指定日志文件的前缀。日志文件会自动生成如 mysql-bin.000001, mysql-bin.000002 ... log-bin mysql-bin # 设置二进制日志的格式。推荐使用 ROW 模式它基于行记录变更更安全、更精确尤其在主从数据不一致时。 binlog_format ROW # 为每个数据库单独创建二进制日志文件可选便于管理。这里我们注释掉使用全局日志。 # binlog-do-db app_db # 启用GTID这是简化复制的关键。 gtid_strict_mode1 # 确保binlog中包含GTID信息 log-slave-updates1 # 二进制日志的过期时间按需设置防止磁盘被占满。 expire_logs_days 7 # 每个二进制日志文件的最大大小。 max_binlog_size 100Mserver-id这是整个主从架构中每个节点的唯一标识必须不同。我习惯用IP地址的最后一段。binlog_format ROW这是非常重要的选择。STATEMENT模式记录SQL语句本身在某些使用UUID()、RAND()等非确定性函数的场景下可能导致主从数据不一致。ROW模式记录每行数据的变化能绝对保证一致性是生产环境的默认选择。gtid_strict_mode1开启严格的GTID模式强制使用GTID避免意外回退到传统文件位置模式。log-slave-updates1如果这个从库未来可能作为其他从库的主库形成链式复制则需要开启此选项让它把从主库执行的事务也记录到自己的binlog中。通常建议开启。4.2 创建复制专用账户绝对不要使用root账户进行复制我们需要创建一个权限最小化的专属账户。-- 在主库的MySQL命令行中执行 CREATE USER repl192.168.1.% IDENTIFIED BY YourStrongPassword123!; GRANT REPLICATION SLAVE ON *.* TO repl192.168.1.%; FLUSH PRIVILEGES;这里创建了一个用户repl只允许从192.168.1.0/24网段登录密码需要设置得复杂一些。权限REPLICATION SLAVE是进行复制所需的最小权限。4.3 重启服务并确认状态保存配置文件后重启MariaDB服务使配置生效。sudo systemctl restart mariadb sudo systemctl status mariadb # 确认服务状态正常然后登录主库查看关键状态SHOW MASTER STATUS\G你会看到类似如下输出*************************** 1. row *************************** File: mysql-bin.000001 Position: 328 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: 0-100-1请记录下File和Position如果使用传统方式或者Executed_Gtid_Set的值。不过因为我们使用了--master-data2参数备份从库的备份文件里已经包含了这个信息所以这里可以只做验证。再检查一下GTID是否启用SHOW VARIABLES LIKE %gtid%;确保gtid_strict_mode和enforce_gtid_consistency的值为ON。5. 从库Slave配置与启动同步连接并开始工作从库的配置相对简单主要是告诉它主库是谁以及从哪里开始同步。5.1 从库基础配置编辑从库的MariaDB配置文件同样在[mysqld]段[mysqld] server-id 101 # 必须与主库不同 # 从库一般不需要开启log-bin除非它要作为其他从库的主库。 # log-bin mysql-bin # 启用中继日志 relay-log mysql-relay-bin # 中继日志的索引文件 relay-log-index mysql-relay-bin.index # 设置只读模式强烈建议。防止在从库上误写数据导致主从不一致。 read_only ON # 即使设置了read_only以下用户仍有写权限如复制线程、管理员 super_read_only ON # MariaDB 10.5 支持更严格read_only ON这个设置至关重要它将从库设置为只读模式普通用户无法执行INSERT/UPDATE/DELETE等写操作有效防止业务程序误连从库进行写操作导致的数据混乱。但复制线程SQL线程具有特权可以正常重放日志。如果从库不需要作为其他库的主库可以不开启log-bin。保存并重启从库的MariaDB服务。5.2 配置主从连接信息CHANGE MASTER TO这是最关键的一步。我们登录从库的MySQL命令行执行一条命令来建立复制链路。-- 在从库上执行 STOP SLAVE; -- 如果之前有旧的复制配置先停止 CHANGE MASTER TO MASTER_HOST192.168.1.100, MASTER_PORT3306, MASTER_USERrepl, MASTER_PASSWORDYourStrongPassword123!, MASTER_USE_GTID current_pos; -- 如果是传统位置复制则使用不推荐仅作了解 -- CHANGE MASTER TO -- MASTER_HOST192.168.1.100, -- MASTER_PORT3306, -- MASTER_USERrepl, -- MASTER_PASSWORDYourStrongPassword123!, -- MASTER_LOG_FILEmysql-bin.000001, -- 来自 SHOW MASTER STATUS 或备份文件 -- MASTER_LOG_POS328;核心参数是MASTER_USE_GTID current_pos;。这告诉从库使用GTID模式进行复制并且从“当前已经执行过的GTID之后”开始同步。因为我们之前已经导入了主库的备份备份文件里包含了SET GLOBAL.gtid_slave_pos语句所以从库的gtid_slave_pos系统变量已经设置好了指向了备份时刻的主库状态。使用GTID模式我们完全不需要手动指定复杂的MASTER_LOG_FILE和MASTER_LOG_POS大大简化了操作并降低了出错概率。5.3 启动复制并检查状态配置完成后启动复制进程START SLAVE;然后检查从库的复制状态SHOW SLAVE STATUS\G这条命令会输出非常多的信息我们需要关注以下几个关键字段字段含义正常状态Slave_IO_RunningI/O线程状态负责从主库拉取日志YesSlave_SQL_RunningSQL线程状态负责执行中继日志YesLast_IO_Error最后一次I/O线程错误空Last_SQL_Error最后一次SQL线程错误空Seconds_Behind_Master从库落后主库的秒数复制延迟0 或一个很小的数字Retrieved_Gtid_Set已从主库获取的GTID集合有值Executed_Gtid_Set已在从库执行的GTID集合应与主库的逐渐接近如果Slave_IO_Running和Slave_SQL_Running都是Yes并且Seconds_Behind_Master逐渐变为0那么恭喜你主从复制已经成功运行起来了你可以简单测试一下在主库上创建一个新表或插入一条数据然后在从库上查询应该立刻就能看到同步过来的数据。6. 实战排错与深度优化从能用走向好用配置成功只是第一步在生产环境中稳定运行才是挑战。下面分享一些我踩过坑后总结的常见问题排查方法和优化建议。6.1 常见故障排查当SHOW SLAVE STATUS\G显示异常时按以下思路排查Slave_IO_Running: Connecting或Last_IO_Error有错误网络问题检查防火墙用telnet测试主库3306端口。权限问题确认主库上repl用户的密码和主机限制是否正确。可以在从库上用mysql -urepl -p -h 192.168.1.100测试是否能连接。主库地址或端口错误仔细检查CHANGE MASTER TO语句。Slave_SQL_Running: No且Last_SQL_Error显示错误如重复键、表不存在这通常是因为从库和主库的数据在复制开始前就不一致或者在从库上进行了手动写操作。跳过错误应急如果确定这个错误可以忽略例如主库上删除了一条从库不存在的记录可以临时跳过这个事务。务必谨慎-- 先停止SQL线程 STOP SLAVE SQL_THREAD; -- 设置跳过下一个事务GTID模式 SET GLOBAL sql_slave_skip_counter 1; -- 传统模式用这个 -- 对于GTID更安全的方式是手动将错误的事务加入到gtid_slave_pos中 -- 假设错误事务的GTID是 0-100-5 SET GLOBAL gtid_slave_pos CONCAT(gtid_slave_pos, ,0-100-5); -- 重新启动SQL线程 START SLAVE SQL_THREAD;根治方法重建从库。如果出现无法调和的数据不一致最彻底的办法是停止从库重新从主库做一次全量备份并恢复然后重新配置复制。这印证了初始数据一致性的重要性。Seconds_Behind_Master延迟很大复制延迟是线上常见问题。原因1从库性能瓶颈。主库写入压力大从库的SQL线程重放速度跟不上。检查从库的CPU、IO、内存使用率。可以考虑升级从库硬件或者优化从库的SQL例如确保从库也有合适的索引。原因2长事务或大事务。主库一个事务更新了100万行产生大量的binlog从库需要同样长时间来执行。尽量避免在业务中运行超大事务。原因3单线程复制。默认SQL线程是单线程的。在MariaDB 10.0版本中可以开启并行复制。STOP SLAVE; SET GLOBAL slave_parallel_threads 4; -- 根据CPU核心数调整 START SLAVE;原因4从库有查询压力。如果业务大量读请求落在从库可能会与SQL线程争抢资源。需要监控和分析。6.2 关键监控项不能只靠肉眼检查SHOW SLAVE STATUS需要建立监控。延迟监控持续监控Seconds_Behind_Master。可以写脚本定期采集并告警。线程状态监控监控Slave_IO_Running和Slave_SQL_Running。GTID监控定期对比主库的gtid_current_pos和从库的gtid_slave_pos确保它们最终一致。日志空间监控监控主库的binlog和从库的relay log所在磁盘的空间使用率防止写满。6.3 一些重要的经验与技巧从库只读再次强调生产环境的从库一定要设置read_only ON。这是保证数据安全的第一道防线。定期校验数据一致性即使复制状态正常也可能因为磁盘静默错误、内存故障等导致主从数据出现比特位级别的差异。可以使用pt-table-checksumPercona Toolkit中的工具定期进行数据一致性校验。关于备份从库是执行备份的理想场所。在从库上执行mysqldump或mariabackup对主库完全没有性能影响。只需注意备份时可能会稍微加大从库延迟。版本一致性尽量保证主从MariaDB的大版本一致。从库的版本可以略高于主库但绝不要低于主库否则可能无法解析主库的binlog格式。连接池配置在应用程序中配置读写分离时确保写操作INSERT/UPDATE/DELETE只指向主库读操作SELECT可以指向从库。许多ORM框架或中间件如MyCat, ProxySQL可以自动实现这一点。7. 进阶半同步复制与多源复制简介当你熟悉了基础的异步复制后可以根据业务需求考虑更高级的部署模式。7.1 半同步复制Semi-Synchronous Replication异步复制的缺点是主库提交事务后不保证从库立即收到存在数据丢失窗口期。半同步复制要求主库在提交事务前必须等待至少一个从库确认收到了该事务的binlog事件并不需要从库执行完。优点提高了数据安全性保证了主库宕机时至少有一个从库拥有最新的数据。缺点增加了主库事务的响应延迟因为多了一次网络往返。配置需要在主库和从库都安装半同步插件rpl_semi_sync_master和rpl_semi_sync_slave并在配置文件中启用。这属于对数据一致性有更高要求的场景。7.2 多源复制Multi-Source Replication这是MariaDB 10.0引入的强大功能允许一个从库同时从多个不同的主库同步数据。这在需要合并多个数据源的场景下非常有用。场景例如你有多个分区的应用每个分区有自己的数据库主库但需要一个全局的报表从库来聚合所有数据。原理从库上会为每个主库连接创建独立的复制通道Channel每个通道有自己独立的I/O和SQL线程以及独立的中继日志文件。配置与普通复制类似但在执行CHANGE MASTER TO时需要指定FOR CHANNEL channel_name。管理时也需要针对不同通道进行操作如START SLAVE FOR CHANNEL channel_1;。主从复制是数据库高可用架构的起点。在此基础上你可以进一步探索MHAMaster High Availability、Galera Cluster多主同步集群或基于MaxScale/ProxySQL的读写分离与故障自动转移方案构建真正具备弹性和自愈能力的数据服务层。