0%

mysql的主从复制原理和操作

1. 主从复制

MySQL 的主从复制依赖于 binlog ,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。

MySQL主从复制涉及到三个线程。一个运行在主节点的 log dump 线程,两个是运行在从节点的I/O 线程, SQL 线程。

MySQL 主从复制过程

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
2
3
4
docker pull mysql:5.7 # 拉取镜像

docker run --name mysql1 -p 33061:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7 # 运行第一个 mysql
docker run --name mysql2 -p 33062:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7 # 运行第二个 mysql

2.1 主服务器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 进入容器修改配置文件
docker exec -it mysql1 /bin/bash
vi /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
server-id=1
log-bin=mysql-bin

docker restart mysql1

# 2. 配置权限, 可以被连接
mysql -u root -p
grant replication slave on *.* to 'root'@'172.17.0.1' identified by '123456';
flush privileges;
show master status\G
*************************** 1. row ***************************
File: mysql-bin.000003
Position: 319
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:

2.2 从服务器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 1. 进入容器修改配置文件
docker exec -it mysql2 /bin/bash

vi /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
server-id=2
log-bin=mysql-bin

docker restart mysql2


# 2. change master 配置, 172.17.0.1 是docker mac 本机ip, 33061是主服务器的端口
mysql -u root -p

change master to master_host='172.17.0.1',
master_port=33061,
master_user='root',
master_password='123456',
master_log_file='mysql-bin.000003',
master_log_pos=319;

# 3. 开启slave
start slave;
show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.17.0.1
Master_User: root
Master_Port: 33061
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 649
Relay_Log_File: 9339fdceb667-relay-bin.000002
Relay_Log_Pos: 650
Relay_Master_Log_File: mysql-bin.000003
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

测试

1
2
3
4
# 主数据库创建
create database test_1;
# 从数据查看, 即可以看到
show databases;

3. 复制问题

3.1 binlog 文件组织方式

  1. 基于SQL语句的复制(statement-based replication)

Mysql 5.1.4 及之前的版本默认。只会修改数据的sql语句到binlog中,但是now()函数不一致行为。

  1. 基于行的复制(row-based replication)

记录哪条数据被修改了,修改成什么样。但是日志会非常多。

  1. 混合模式复制(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,用于表示当前备库延迟了多少秒。

产生的原因

  1. 备库的查询压力过大的话,备库的查询消耗了大量的CPU资源,那么必不可少的就会影响同步的速度。
  2. 大事务执行。如果主库的一个事务执行了10分钟,而binlog的写入必须要等待事务完成之后,才会传入备库,那么此时在开始执行的时候就已经延迟了10分钟了。
  3. 从库在同步数据的同时,可能跟其他查询的线程发生锁抢占的情况,也会发生延时。
  4. 当主库的TPS并发非常高的时候,也可能带来延迟。
  5. 在进行binlog日志传输的时候,网络延迟造成数据同步延迟。

解决方案

  1. 半同步复制

    是介于全同步复制与全异步复制之间的一种,主库只需要等待至少一个从库接收到并写到 relay Log 文件即可,主库不需要等待所有从库给主库返回 ACK。主库收到这个 ACK 以后,才能给客户端返回 “事务完成” 的确认。

  2. 修改sync_binlog的参数的值

    当sync_binlog=0的时候,表示每次提交事务都只write,不fsync

    当sync_binlog=1的时候,表示每次提交事务都执行fsync

    当sync_binlog=N的时候,表示每次提交事务都write,但积累N个事务后才fsync。

  3. 设置innodb_flush_log_at_trx_commit 属性

    0(每次写到服务缓存,一秒钟刷写一次)

    1(每次事务提交都刷写一次磁盘)

    2(每次写到os缓存,一秒钟刷写一次)

    一般情况下我们推荐设置成2,这样就算mysql的服务宕机了,写在 os缓存中的数据也会进行持久化。

4. 参考资料

可以加首页作者微信,咨询相关问题!