MySQL主从复制原理及其实践
前言
我们在设计系统架构的时候,由于单台数据的IO
能力是有限的,于是我们需要通过数据库集群化的方式进一步提升我们数据库的并发量,本篇博客主要介绍Mysql
集群化的基础——主从复制。
Mysql分表分库可参考我的博客:Mysql分表分库
正文
MySQL主从复制原理及其实践
事务的两阶段提交
Mysql
使用两阶段提交解决故障恢复、主从、主备复制时,保持了数据的一致性,即实现binlog
和InnoDB redo log
的数据一致性。
- 阶段一:
redo log
写盘,InnoDB
事务进入prepare
状态 - 阶段二:如果前面
prepare
成功,binlog
写盘,那么再继续将事务日志(redo log
)持久化到binlog
,如果持久化成功,那么InnoDB
事务 则进入commit
状态
每个事务binlog
的末尾,会记录一个 XID event
,标志着事务是否提交成功
MySql主从复制的涉及三个行为:
- 数据存储:
Master
将数据改变记录到二进制日志(binary log
)中,也就是配置文件log-bin
指定的文件,这些记录叫做二进制日志事件(binary log events
); - 数据传输:
Slave
通过I/O
线程读取Master
中的binary log events
并写入到它的中继日志(relay log
); - 数据重放:
Slave
重做中继日志中的事件,把中继日志中的事件信息一条一条的在本地执行一次,完成数据在本地的存储,从而实现将改变反映到它自己的数据。
MySql数据库主从复制的方式
主从复制是基于日志(binlog
)主要有以下四种:
- 异步复制(默认) :
MySQL
默认的复制策略,Master
处理事务过程中,将其写入Binlog
就会通知Dump thread
线程处理,然后完成事务的提交,不会关心是否成功发送到任意一个slave中。 - 半同步复制(提交后才同步) :
Master
处理事务过程中,提交完事务后,必须等至少一个Slave
将收到的binlog
写入relay log
返回ack
才能继续执行处理用户的事务。 - 增强半同步复制(同步后再提交) :半同步的问题是因为等待
ACK
的点是Commit
之后,此时Master
已经完成数据变更,用户已经可以看到最新数据,当Binlog
还未同步到Slave
时,发生主从切换,那么此时从库是没有这个最新数据的,用户又看到老数据。增强半同步将等待ACK
的点放在提交Commit
之前,此时数据还未被提交,外界看不到数据变更,此时如果发送主从切换,新库依然还是老数据,不存在数据不一致的问题。 - 组复制(半数写成功) :
MySQL
在引擎层完成Prepare
操作写Redo
日志之后,会被MySQL
的预设Hook
拦截进入MGR
层,MGR
层将事务信息打包通过Paxos
协议发送到全部节点上,只要集群中过半节点回复ACK
,那么将告诉所有节点数据包同步成功,然后每个节点开始自己认证(certify
)通过就开始写Binlog
,提交事务或者写relay log
,数据同步,如果认证不通过则rollback
。
单向主从环境的搭建
第一步:关闭数据库服务器的的 selinux 都要 disable
vim /etc/selinux/config
将 SELINUX 改为 disabled
SELinux
主要作用就是最大限度地减小系统中服务进程可访问的资源(最小权限原则)。
第二步:配置Master
## 在 [mysqld] 中增加以下配置项
## 设置 server_id,一般设置为 IP
server_id=100
## 复制过滤:需要备份的数据库,输出 binlog
#binlog-do-db=luoforTest
## 复制过滤:不需要备份的数据库,不输出(mysql 库一般不同步)
binlog-ignore-db=mysql
## 开启二进制日志功能,可以随便取,最好有含义
log-bin=edu-mysql-bin
## 为每个 session 分配的内存,在事务过程中用来存储二进制日志的缓存
binlog_cache_size=1M
## 主从复制的格式(mixed,statement,row,默认格式是 statement)
binlog_format=mixed
## 二进制日志自动删除/过期的天数。默认值为 0,表示不自动删除。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免 slave 端复制中断。
## 如:1062 错误是指一些主键重复,1032 错误是因为主从数据库数据不一致
slave_skip_errors=1062
第三步 :配置Cluster的 /etc/my.cnf中的[mysqld]
## 在 [mysqld] 中增加以下配置项
## 设置 server_id,一般设置为 IP
server_id=101
## 复制过滤:需要备份的数据库,输出 binlog
#binlog-do-db=luoforTest
##复制过滤:不需要备份的数据库,不输出(mysql 库一般不同步)
binlog-ignore-db=mysql
## 开启二进制日志,以备 Slave 作为其它 Slave 的 Master 时使用
log-bin=edu-mysql-slave1-bin
## 为每个 session 分配的内存,在事务过程中用来存储二进制日志的缓存
binlog_cache_size = 1M
## 主从复制的格式(mixed,statement,row,默认格式是 statement)
binlog_format=mixed
## 二进制日志自动删除/过期的天数。默认值为 0,表示不自动删除。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免 slave 端复制中断。
## 如:1062 错误是指一些主键重复,1032 错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log 配置中继日志
relay_log=edu-mysql-relay-bin
## log_slave_updates 表示 slave 将复制事件写进自己的二进制日志
log_slave_updates=1
## 防止改变数据(除了特殊的线程)
read_only=1
mysql cluster数据库通过命令的形式开启主从复制(以root身份执行命令)
mysql> change master to master_host='192.168.25.100', master_user='root', master_password='092248', master_port=3306, master_log_file='edu-mysql-bin.000006', master_log_pos=1389, master_connect_retry=30;
可能会用到的命令:
- 开启主从复制:
start slave; stop slave; reset slave;
- 查看主从同步状态:
show slave status\G; show master status\G;
- 线程的状态:
show processlist\G;
解决未同步问题
/etc/my.cnf server-id
设置/var/lib/mysql/auto.cnf
要删除reset slave
可解决中继日志问题- 从机同步命令中
master_log_file
要与master
中的要一致(通过show master status\G;
可查看)
同步的结构优化
- 一主多从的结构,可能会导致
master
宕机,大规模数据库失效 - 链式的结构,可以避免
master
宕机的情况
数据同步延迟优化
造成延迟大的原因有以下几种:
- 网络延迟
Master
负载过高Slave
负载过高
一般的做法是使用多台 Slave
来分摊读请求,再单独配置一台 Slave
只作为备份用,不进行其他任何操作,就能相对最大限度地达到“实时”的要求了。
在使用命令进行数据同步时,可选择一下参数:
- –master-retry-count:定义重试连接的最大次数 默认是86400次
- –master-connect-retry:定义多少间隔时间重试一次 默认是60秒
- –slave-net-timeout :定义超时重试连接
master
的时间 默认是3600s