[toc]
## 使用主从的好处
1. 采用主从服务器这种架构,稳定性得以提升。如果主服务器发生故障,我们可以使用从服务器来提供服务。
2. 在主从服务器上分开处理用户的请求,如读写分离,可以提升数据处理效率。
3. 将主服务器上的数据复制到从服务器上,保护数据免受意外的损失(数据热备)。
## 原理(基于Binlog)
主从复制有两种方式
* 基于Binlog
* 基于GTID(全局事务标识符)
![](https://i.vgy.me/nCHhDe.png)
1. 主库记录二进制日志,每次准备提交事务完成数据库更新前,先记录二进制日志,记录二进制日志后,主库会告诉存储引擎可以提交事务了
2. 备库将主库的二进制日志复制到本地的中继日志中,首先,备库会先启动一个工作进程,称为IO工作线程,负责和主库建立一个普通的客户端连接。如果该进程追赶上了主库,它将进入睡眠状态,直到主库有新的事件产生通知它,他才会被唤醒,将接收到的事件记录到中继日志中。
3. 备库的SQL线程执行最后一步,该线程从中继日志中读取事件并且在备库执行,当SQL线程赶上IO线程的时候,中继日志通常记录在系统缓存中,所以中继日志的开销很低。SQL线程也可以根据配置选项来决定是否写入其自己的二进制日志中。
## 复制的方式
**异步复制**
MySQL复制默认是异步复制,Master将事件写入binlog,提交事务,自身并不知道slave是否接收是否处理;
缺点:不能保证所有事务都被所有slave接收。
**同步复制**
Master提交事务,直到事务在所有slave都已提交,才会返回客户端事务执行完毕信息;
缺点:完成一个事务可能造成延迟。
**半同步复制**
当Master上开启半同步复制功能时,至少有一个slave开启其功能。当Master向slave提交事务,且事务已写入relay-log中并刷新到磁盘上,slave才会告知Master已收到;若Master提交事务受到阻塞,出现等待超时,在一定时间内Master 没被告知已收到,此时Master自动转换为异步复制机制;
注:半同步复制功能要在Master和slave上开启才会起作用,只开启一边,依然是异步复制。
## 关于主从复制和读写分离
主从复制只是实现读写分离的基础,要实现读写分离还需要借助数据库中间件或者程序实现。
## 主从复制注意点
1. 主从服务器操作**系统版本和位数一致**
2. Master 和 Slave 数据库的**版本要一致**
3. 同步之前Master 和 Slave 数据库中的**数据要一致**
4. Master 开启二进制日志,Master 和 Slave 的 **server\_id 在局域网内必须唯一**
## 配置
master : 172.30.0.2
slave: 172.30.0.3
### master
**在 \[mysqld\] 中增加以下配置项**
~~~
## 设置 server_id,一般设置为局域网IP最后一段
server_id=2
## 复制过滤:需要备份的数据库,输出 binlog,如果有多项就复制多段
# 如果有过滤,不建议在master端处理。Slave端操作粒度更细,并且master全量复制不需要另外修改。
#binlog-do-db=test
#binlog-do-db=test2
## 复制过滤:不需要备份的数据库,不输出(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
~~~
**关于MySQL 对于二进制日志 (binlog)的复制类型**
1. 基于语句的复制:在 Master 上执行的 SQL 语句,在 Slave 上执行同样的语句。MySQL 默认采用基于语句的复制,效率比较高。一旦发现没法精确复制时,会自动选着基于行的复制。
2. 基于行的复制:把改变的内容复制到 Slave,而不是把命令在 Slave 上执行一遍。从MySQL5.0 开始支持。
3. 混合类型的复制:默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。
**创建数据同步用户,并授予相应的权限**
~~~
mysql -uroot -p
# 5.7以前(会有个warnning)
mysql> grant replication slave, replication client on *.* to 'repl'@'172.30.0.3' identified by 'caiwen.123'
# 5.7以后
create user 'dba'@'172.30.0.%' identified by 'caiwen.123';
grant replication slave on *.* to dba@'172.30.0.%';
# 刷新授权表信息
mysql> flush privileges;
# 查看 position 号,记下 position 号(从机上需要用到这个 position 号和现在的日志文件)
mysql> show master status;
~~~
![](https://i.vgy.me/bJG8LG.png)
<br />
**锁表同步数据**
~~~
# 锁定当前表
mysql> flush tables with read lock;
# 进行备份同步当前数据到主库
# 解锁表
mysql> unlock tables;
~~~
### slave
**在 \[mysqld\] 中增加以上的master配置项,然后添加或修改以下选项**
~~~
## 设置 server_id,一般设置为 IP
server_id=3
## 开启二进制日志,以备 Slave 作为其它 Slave 的 Master 时使用
log-bin=edu-mysql-slave1-bin
## relay_log 配置中继日志
relay_log=edu-mysql-relay-bin
## log_slave_updates 表示 slave 将复制事件写进自己的二进制日志
log_slave_updates=1
## 防止改变数据(除了特殊的线程)
read_only=1
~~~
如果 Slave 为其它 Slave 的 Master 时,必须设置 bin\_log。在这里,我们开启了二进制日志,而且显式的命名(默认名称为 hostname,但是,如果 hostname 改变则会出现问题)。
relay\_log 配置中继日志,log\_slave\_updates 表示 slave 将复制事件写进自己的二进制日志。当设置 log\_slave\_updates 时,你可以让 slave 扮演其它 slave 的 master。此时,slave 把 SQL线程执行的事件写进行自己的二进制日志(binary log),然后,它的 slave 可以获取这些事件并执行它。如下图所示(发送复制事件到其它 Slave):
![](https://i.vgy.me/2VVOWG.png)
~~~
mysql -uroot -p
# 配置主从
# master_log_file ##指定 Slave 从哪个日志文件开始读复制数据
#(可在 Master 上使用 show master status 查看到日志文件名)
# master_log_pos=429 ## 从哪个 POSITION 号开始读
# master_connect_retry=30 ##当重新建立主从连接时,如果连接建立失败,间隔多久后重试。
# 单位为秒,默认设置为 60 秒,同步延迟调优参数。
mysql> change master to master_host='172.30.0.2',master_user='repl', master_password='caiwen.123', master_port=3306, master_log_file='edu-mysql-bin.000001', master_log_pos=618, master_connect_retry=30;
## 查看主从同步状态
mysql> show slave status\G;
# 可看到 Slave_IO_State 为空, Slave_IO_Running 和 Slave_SQL_Running 是 No,表明 Slave 还 没有开始复制过程。
## 开启主从同步
mysql> start slave;
## 再查看主从同步状态
mysql> show slave status\G;
~~~
### 查看状态
可查看 master 和 slave 上线程的状态。在 master 上,可以看到 slave 的 I/O 线程创建的连接:
~~~
Master : mysql> show processlist\G;
~~~
![](https://i.vgy.me/eufSqL.png)
~~~
Slave: mysql> show processlist\G;
~~~
![](https://i.vgy.me/QjWDFu.png)
2\. row : 为 I/O 线程状态
3\. row :为 SQL 线程状态。
<br />
当主从复制正在进行中时,如果想查看从库两个线程运行状态,可以通过执行在从库里执行”show slave statusG”语句,以下的字段可以给你想要的信息:
~~~
Master_Log_File — 上一个从主库拷贝过来的binlog文件
Read_Master_Log_Pos — 主库的binlog文件被拷贝到从库的relay log中的位置
Relay_Master_Log_File — SQL线程当前处理中的relay log文件
Exec_Master_Log_Pos — 当前binlog文件正在被执行的语句的位置
~~~
### 重置主从复制设置
测试过程中,如果遇到同步出错,可在 Slave 上重置主从复制设置(选操作):
```
(1) mysql> reset slave;
(2) mysql> change master to master_host='172.30.0.2',master_user='repl', master_password='caiwen.123', master_port=3306, master_log_file='edu-mysql-bin.000001', master_log_pos=618, master_connect_retry=30;
(此时,master_log_file 和 master_log_pos 要在 Master 中用 show master status 命令查看)
```
>[warning] 注意:如果在 Slave 没做只读控制的情况下,千万不要在 Slave 中手动插入数据,那样数据 就会不一致,主从就会断开,就需要重新配置了。
### 主从相关命令
~~~
show master status; //查看master的状态,尤其是当前的日志及位置
show slave status; //查看slave的状态
reset slave; //重置slave状态
start slave; //启动slave状态(开启监听master的变化)
stop slave; //暂停salve状态
~~~
## 关于主主复制
两台服务器上都开启二进制日志和relay日志,都设置replication账号,都设置对方为自己的master。 即可完成主主复制。
但是,这样配置主主复制会出现很多同步冲突的问题,一般都是通过第三方工具来处理。后续会介绍。
## 关于半同步复制
我们知道,普通的replication,即MySQL的异步复制,依靠MySQL二进制日志也即binary log进行数据复制。比如两台机器,一台主机(master),另外一台是从机(slave)。
1)正常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave的io线程接收到t1并写入到自己的的relay log;slave的sql线程写入到本地数据库。 这时,master和slave都能看到这条新的事务,即使master挂了,slave可以提升为新的master。
2)异常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave因为网络不稳定,一直没有收到t1;master挂掉,slave提升为新的master,t1丢失。
3)很大的问题是:主机和从机事务更新的不同步,就算是没有网络或者其他系统的异常,当业务并发上来时,slave因为要顺序执行master批量事务,导致很大的延迟。
为了弥补以上几种场景的不足,MySQL从5.5开始推出了半同步复制。相比异步复制,半同步复制提高了数据完整性,因为很明确知道,在一个事务提交成功之后,这个事务就至少会存在于两个地方。即在master的dumper线程通知slave后,增加了一个ack(消息确认),即是否成功收到t1的标志码,也就是dumper线程除了发送t1到slave,还承担了接收slave的ack工作。如果出现异常,没有收到ack,那么将自动降级为普通的复制,直到异常修复后又会自动变为半同步复制。
半同步复制具体特性:
* 从库会在连接到主库时告诉主库,它是不是配置了半同步。
* 如果半同步复制在主库端是开启了的,并且至少有一个半同步复制的从库节点,那么此时主库的事务线程在提交时会被阻塞并等待,结果有两种可能,要么至少一个从库节点通知它已经收到了所有这个事务的Binlog事件,要么一直等待直到超过配置的某一个时间点为止,而此时,半同步复制将自动关闭,转换为异步复制。
* 从库节点只有在接收到某一个事务的所有Binlog,将其写入并Flush到Relay Log文件之后,才会通知对应主库上面的等待线程。
* 如果在等待过程中,等待时间已经超过了配置的超时时间,没有任何一个从节点通知当前事务,那么此时主库会自动转换为异步复制,当至少一个半同步从节点赶上来时,主库便会自动转换为半同步方式的复制。
* 半同步复制必须是在主库和从库两端都开启时才行,如果在主库上没打开,或者在主库上开启了而在从库上没有开启,主库都会使用异步方式复制。
![](https://i.vgy.me/ArY36h.png)
## MySQL 主从数据同步延迟问题的调优
基于局域网的 Master/Slave 机制在通常情况下已经可以满足“实时”备份的要求了。如果延
迟比较大,可以从以下几个因素进行排查:
(1) 网络延迟;
(2) Master 负载过高;
(3) Slave 负载过高;
一般的做法是使用多台Slave来分摊读请求,再单独配置一台Slave只作为备份用,不进行 其他任何操作,就能相对最大限度地达到“实时”的要求了。MySQL 5.7之后,可以使用多线程复制,使用MGR复制架构
- 【mysql的编程专题①】流程控制与其他语法
- 【mysql的编程专题②】触发器
- 【mysql的编程专题③】内置函数
- 【mysql的编程专题④】存储过程
- 【mysql的编程专题⑤】自定义函数
- 【mysql的编程专题⑥】视图
- 【mysql的设计与优化专题(1)】ER图,数据建模与数据字典
- 【mysql的设计与优化专题(2)】数据中设计中的范式与反范式
- 【mysql的设计与优化专题(3)】字段类型与合理的选择字段类型
- 【mysql的设计与优化专题(4)】表的垂直拆分和水平拆分
- 【mysql的设计与优化专题(5)】慢查询详解
- 【mysql的设计与优化专题(6)】mysql索引攻略
- 【Mysql问题集锦(1)】mysql不能使用innodb存储引擎
- 【Mysql进阶技巧(2)】利用mysql生成唯一序号
- 【Mysql进阶技巧(1)】MySQL的多表关联与自连接
- 【Mysql高可用架构(1)】基于日志点的主从复制
- 【Mysql高可用架构(2)】主从管理的系统视图
- 【Mysql高可用架构(3)】基于GTID的主从复制
- 【Mysql高可用架构(4)】在线变更复制类型
- 【Mysql高可用架构(5)】多源复制(多主一从)
- 【Mysql高可用架构(6)】多线程复制
- 【Mysql高可用架构(7)】在线设置复制过滤
- 【Mysql高可用架构(8)】解决主从不一致
- 【Mysql高可用架构(9)】初识mycat以及制作mycat镜像
- 【Mysql高可用架构(10)】mycat配置mysql读写分离
- MyCat 集群部署(HAProxy + MyCat)
- 常用复杂sql语句整理