1. 主从复制
MySQL 的主从复制依赖于 binlog ,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。
MySQL主从复制涉及到三个线程。一个运行在主节点的 log dump 线程,两个是运行在从节点的I/O 线程, SQL 线程。
1.1 复制流程
主写binlog,从请求binlog,然后再回放。
1. 主库写入 Binlog
MySQL 主库会先写入 binlog,再提交事务。
2. 从库同步 Binlog
从库会创建一个专门的 I/O 线程,连接主库的 log dump 线程。请求主库的 binlog,并写到本地的 relay-log(中继日志)文件中。
3. 从库回放 Binlog
从库会创建一个SQL 线程,去读 relay log 中继日志,然后回放 binlog 更新存储引擎中的数据,最终实现主从的数据一致性。
1.2 复制模型
半同步复制,是等待部分从库成功。
1. 全同步复制
MySQL 主库提交事务的线程要等待所有从库的复制成功响应,才返回客户端结果。这种方式在实际项目中,基本上没法用。
原因有两个:一是性能很差,因为要复制到所有节点才返回响应;二是可用性也很差,主库和所有从库任何一个数据库出问题,都会影响业务。
2. 异步复制(默认模型)
MySQL 主库提交事务的线程并不会等待 binlog 同步到各从库,就返回客户端结果。这种模式一旦主库宕机,数据就会发生丢失。
3. 半同步复制
MySQL 5.7 版本之后增加的一种复制方式,介于两者之间,事务线程不用等待所有的从库复制成功响应,只要一部分复制成功响应回来就行。
比如一主二从的集群,只要数据成功复制到任意一个从库上,主库的事务线程就可以返回给客户端。
这种半同步复制的方式,兼顾了异步复制和同步复制的优点,即使出现主库宕机,至少还有一个从库有最新的数据,不存在数据丢失的风险。
2. 操作配置
第一个当做主服务器, 第二个当做从服务器。
1 | docker pull mysql:5.7 # 拉取镜像 |
2.1 主服务器配置
1 | # 1. 进入容器修改配置文件 |
2.2 从服务器配置
1 | # 1. 进入容器修改配置文件 |
测试
1 | # 主数据库创建 |
3. 复制问题
3.1 binlog 文件组织方式
- 基于SQL语句的复制(statement-based replication)
Mysql 5.1.4 及之前的版本默认。只会修改数据的sql语句到binlog中,但是now()函数不一致行为。
- 基于行的复制(row-based replication)
记录哪条数据被修改了,修改成什么样。但是日志会非常多。
- 混合模式复制(mixed-based replication)
是以上两种模式的混合,对于一般的复制使用STATEMENT模式保存到binlog,对于STATEMENT模式无法复制的操作则使用ROW模式来保存,MySQL会根据执行的SQL语句选择日志保存方式。
3.2 从库是不是越多越好?
不是的。
因为从库数量增加,从库连接上来的 I/O 线程也比较多,主库也要创建同样多的 log dump 线程来处理复制的请求,对主库资源消耗比较高,同时还受限于主库的网络带宽。
所以在实际使用中,一个主库一般跟 2~3 个从库,这就是一主多从的 MySQL 集群结构。
3.3 延迟问题
可以在备库上执行 show slave status
命令,它的返回结果里面会显示 seconds_behind_master
,用于表示当前备库延迟了多少秒。
产生的原因
- 备库的查询压力过大的话,备库的查询消耗了大量的CPU资源,那么必不可少的就会影响同步的速度。
- 大事务执行。如果主库的一个事务执行了10分钟,而binlog的写入必须要等待事务完成之后,才会传入备库,那么此时在开始执行的时候就已经延迟了10分钟了。
- 从库在同步数据的同时,可能跟其他查询的线程发生锁抢占的情况,也会发生延时。
- 当主库的TPS并发非常高的时候,也可能带来延迟。
- 在进行binlog日志传输的时候,网络延迟造成数据同步延迟。
解决方案
半同步复制
是介于全同步复制与全异步复制之间的一种,主库只需要等待至少一个从库接收到并写到 relay Log 文件即可,主库不需要等待所有从库给主库返回 ACK。主库收到这个 ACK 以后,才能给客户端返回 “事务完成” 的确认。
修改sync_binlog的参数的值
当sync_binlog=0的时候,表示每次提交事务都只write,不fsync
当sync_binlog=1的时候,表示每次提交事务都执行fsync
当sync_binlog=N的时候,表示每次提交事务都write,但积累N个事务后才fsync。
设置innodb_flush_log_at_trx_commit 属性
0(每次写到服务缓存,一秒钟刷写一次)
1(每次事务提交都刷写一次磁盘)
2(每次写到os缓存,一秒钟刷写一次)
一般情况下我们推荐设置成2,这样就算mysql的服务宕机了,写在 os缓存中的数据也会进行持久化。