主机通过binlog(详见上一章)进行主从同步,保证从机/备机与主机数据一致,每个节点都存储了所有的业务数据。
在主从复制架构中,所有的更新必须在主服务器上进行,否则会造成数据不一致问题。
mysql主从同步原理
- master服务器开启binlog后,其数据改变都记录在二进制binary-log日志
- slave连接到master,并告诉master要从哪个binlog的哪个偏移量开始执行增量同步。期间slave会监控master二进制日志的变化,如有,则开始I/O线程和SQL线程
I/O线程读取master的binary-log日志,保存在本地的中继日志relay-log里
SQL线程读取relay-log,解析成SQL语句在本地重放,使数据和master一致 - 最后I/O Thread和SQL Thread将进入睡眠状态,等待下一次被唤醒。

应用场景
- 主备架构:令一个 Slave 只进行复制,作为备用 Master,提高系统的可靠性。
- 读写分离:将读取压力分散到多个从机
- 一主多从架构:从机间负载均衡,提升性能
- 分库分表:同时缓解读取和写入压力
- 双主复制架构:适用于写压力大的场景,会存在更新冲突
- 级联复制架构:从机同时也充当主机,继续往下分从机,缓解主机的复制压力,但复制延迟会变大
读写分离
将读取压力分散到多个从机。
可以减少主机的索引,规避索引影响写入效率的问题。
读写分离会带来数据一致性问题,且过多的从机也会造成 Master 的 I/O 和网络压力。因此如果可以优化单机(如通过redis缓存等缓解读取压力等),则无需读写分离
- 主机只有1台,负责读写,或仅负责写
- 从机可以有1台或多台,负责读
- 同步延迟问题
采用读写分离必然引起同步延迟,存在如下解决方案- 同步写入从库
主从同步默认为异步,如果改为同步,则所有从库写入完成后,主库的写入请求才返回。
弊端:性能较差,当有多个从库时,严重影响请求速度 - 缓存中间件
每当发生写请求时,记录一个key,失效时间设置为主从同步的延时。发生读请求时检查key是否存在,存在则走主库,否则走从库。 - 关键业务读主库
关键业务读写操作全部指向主机,非关键业务采用读写分离。
如整个系统都对数据一致性要求不高,不处理同步延迟问题也没关系。
弊端:开发时需要特殊场景特殊处理
- 同步写入从库

分库
通常按业务模块将数据分散到不同的数据库服务器。
分库后,每一个分片都可以分别采取主备架构或读写分离。
- join查询问题
基于性能、安全性等原因通常禁止跨库join,可以通过建立全局表(数据字典)、重要表做多库同步、重要字段做冗余、HBase+ElasticSearch等方案解决 - 分布式事务
分表
- 单表数据过多时,其查询效率本身就会下降,因此分表后即使不分散到其他数据库,也能带来性能提升
- 分为 垂直分表 和 水平分表(通常行数到达千万级别后开始考虑水平分表)
基于日志节点的主从同步配置
- 确保主从库版本一致
- 主服务器开启二进制日志(binary log)并配置server-id,重启mysql生效
# master配置
[root@master] vim /etc/my.cnf
[mysqld]
.........
server-id=1 ### 服务器ID
log-bin=mysql-bin ### 开启binlog
binlog_format=mixed ### binlog模式为mixed
### 不需要canal和闪回时可以用MINIMAL减小体积
binlog_row_image=MINIMAL
### 在ROW/MIXED的注释中记录SQL语句
binlog-rows-query-log_events=1
### mysql用户名、密码等信息的库可以不同步
binlog-ignore-db=mysql
binlog-ignore-db=sys
........
- 在主库创建专用复制账户
权限修改后需重启mysql,或执行flush privileges;
生效。
[mysql] grant 权限 on 数据库名.表名 to 新用户名@登录主机 identified by "新用户密码";
[mysql] grant replication slave on *.* to 'slaver'@'%' identified by '123456';
其中%
代表任意主机,可替换为IP地址、域名、localhost(仅可本地访问)、正则(如192.168.1.%)
在mysql8之后需要先创建用户,之后才能授权,例如:
[mysql] CREATE USER 'slaver'@'%' IDENTIFIED BY '123456';
[mysql] grant replication slave on *.* to 'slaver'@'%';
- 从主库导出数据
MySQL会话结束时会自动unlock tables;
,因此mysql命令行在此期间应保持激活
[mysql] flush tables with read lock; # 加锁,防止有新的写入
[root@master] mysqldump -uroot -p123456 --all-databases --flush-logs --events --single-transaction --master-data=2 --events > /tmp/all_bak.sql
[root@master] scp /tmp/all_bak.sql 从库IP地址:/tmp/ #
[mysql] unlock tables; #解锁
- 修改从库配置文件,重启mysql生效
[root@slave] vim /etc/my.cnf
[mysqld]
.........
server-id=2 ### 服务器ID,每个服务器上配置的server-id都必须不一致
relay-log=mysql-relay-log ### 开启中继日志(relay log)
super_read_only = on ### 令所有用户(包括super权限用户)只读
skip_slave_start = 1 ### 禁用默认情况下从库mysql启动时自动触发的start slave,防止意外的同步
........
- 在从库导入主库数据
[root@slave] mysql -uroot -p123456 < /tmp/all_bak.sql
- 从库使用
change master
命令配置同步参数,并开启服务
[mysql] change master to
master_host='主库IP',
master_port=3306,
master_user='之前新建的主库用户',
master_password='之前新建的主库用户密码',
master_log_file='mysql-bin.000014', # 通过【--master-data=2】生成的内容获得,或在主库导出后执行show master logs获得
master_log_pos=599; # 同上
[mysql] start slave; # 开启主从复制
[mysql] show slave status; # 查看是否开启成功
可再次执行change master
命令以修改同步参数,执行前需先停止同步。
执行change master
时,未指定的参数将保持原值,因此修改时无需全部传入
[mysql] stop slave;
[mysql] change master to master_password='123456';
[mysql] start slave;
如有多个从机,则重复上述步骤 5~7
基于GTID的主从同步配置
-
GITD (Global Transaction ID 全局事务ID)
由事务发起的服务器ID(server_uuid)以及单调递增的事务ID(transaction_id)组合而成,在集群内全局唯一,如:3E11FA47-71CA-11E1-9E33-C80AA9429562:1。 -
原理
当一个事务在主库端执行并提交时,产生 GTID,并记录到 binlog 日志。
slave 接收进 relaylog 后,读取GTID值设置为 gtid_next 变量(下一个执行的GTID)
sql 线程从 relay log 中获取 GTID,然后对比 slave 端的 binlog 记录看是否重复。
无记录才会执行,并记录到自身的 binlog(防止重复执行)。 -
优点
无需找到 master_log_file 和 master_log_pos
master无需等待slave的ACK信息,延迟更低,性能更好
确保每个事务只执行一次,且连续递增的GTID可避免丢失
方便快速定位事务最初的提交来源 -
缺点
为生成GTID,slave需开启binlog。
在一个复制组中,必须统一开启GTID或是关闭GTID
不允许一个SQL同时更新一个事务引擎和非事务引擎的表
不支持部分语法,如create temporary table
、CREATE TABLE …SELECT
等 -
原本已做过同步的主从机要改为GTID同步,需先重置主从同步(见前文)
# 主、从库都要新增以下配置以开启GTID模式
gtid_mode = on # 开启gtid模式
enforce_gtid_consistency = on # 强制gtid一致性
[mysql] change master to
master_host='主库IP',
master_port=3306,
master_user='之前新建的主库用户',
master_password='之前新建的主库用户密码',
#master_log_file='mysql-bin.000014', # GTID复制模式无需该参数
#master_log_pos=599; # GTID复制模式无需该参数
master_auto_position=1; # 开启GTID复制模式
级联复制架构
对于既是主又是从的库,应在其步骤2配置文件中新增配置,把relay log记录写入binlog
否则只有自身的指令会记入binlog,来自master的指令不会记入
log_slave_updates=1
主主复制架构
两个主机都开启binlog
和relay log
change master
都指向对方,都执行start slave
;
log-bin=mysql-bin ### 开启binlog
relay-log=mysql-relay-log ### 开启relay log
重置主从同步
当主从机数据出现不一致时,可重置主从双方的历史信息,重新同步
- RESET MASTER;
清空binlog索引文件及其列出的日志文件,创建一个新的binlog文件
清空系统变量 gtid_purged 、gtid_executed,清空 mysql.gtid_executed 表。 - RESET SLAVE;
清除slave 复制时的master binlog的位置
清空master info, relay log info
删除所有的relay log文件,并创建一个新的relay log文件。
重置复制延迟(CHANGE MASTER TO 的 MASTER_DELAY参数指定的)为0。 - RESET SLAVE ALL;
除RESET SLAVE;
清除的内容外,还重置CHANGE MASTER TO
的所有参数
- 从库停止同步
[mysql] STOP SLAVE;
- 备份并重置主库
[mysql] FLUSH TABLES WITH READ LOCK;
[root@master] mysqldump -u root -p -databases db1 db2 > bak.sql
[mysql] RESET MASTER;
[mysql] UNLOCK TABLES;
- 从库导入数据并重置
[mysql] DROP DATABASE db1;DROP DATABASE db2;
[mysql] SOURCE /root/bak.sql;
[root@slave] RESET SLAVE;
[root@slave] START SLAVE;
异步复制
默认的复制方式。
主库提交事务后,仅通知 Dump 线程发送 Binlog 给从库,就返回COMMIT OK,并不保证从库能接收。
如此时主/从库故障,数据即丢失。例如:当主库崩溃,需要主备切换的场景。
同步复制
主库提交事务后,等待所有从库接收到 binlog 并写入 relay log,主库再返回COMMIT OK。
性能较差。
半同步复制
主库提交事务后,等待至少一个从库接收到 binlog 并写入 relay log,再返回COMMIT OK。
- 主从库必须都开启了半同步复制,否则会转换为异步复制。
- 如果等待超时,主库会自动转换为异步复制。当至少一个半同步从节点赶上来时,主库便会自动转换回半同步复制。
主库
- 安装插件
[mysql] INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
- 配置文件修改
rpl_semi_sync_master_enabled = 1 # 表示在master上开启半同步复制模式
rpl_semi_sync_master_timeout = 1000 # 表示主库在某次事务中的等待时间为10000毫秒
从库
- 安装插件
[mysql] INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
- 配置文件修改
rpl_semi_sync_slave_enabled = 1
网友评论