MySQL 的主从复制又叫 replication、AB 复制。至少需要两个 MySQL 服务(可以是同一台机器,也可以是不同机器之间进行)。
比如A服务器做主服务器,B服务器做从服务器,在A服务器上进行数据的更新,通过 binlog 日志记录同步到B服务器上,并重新执行同步过来的 binlog 数据,从而达到两台服务器数据一致。
- 作用
- 可以实时灾备,用于故障切换;
- 读写分离,提供查询服务,实现负载均衡;
MySQL 主备的基本原理
image.png备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。一个事务日志同步的完整过程是这样的:
- 在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。
- 在备库 B 上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接。
- 主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog,发给 B。
- 备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)。
- sql_thread 读取中转日志,解析出日志里的命令,并执行。
binlog 的 三种格式对比
复制拓扑
数据复制策略
-
异步复制
主库将事务写入到 Binlog 文件中,并通知 dump 线程发送这些新的 Binlog,然后主库就会继续处理提交操作,所以此时无法保证这些 Binlog 已经成功传到任何一个从库节点上。假设如果此时主库上已经提交的事务因为某些原因未传送到从库,同时主库发生宕机,且在此时从库提升为主库,就会导致新主库数据缺失,从而造成主从数据不一致的情况发生。该复制模式下必然存在此问题。
-
半同步复制
image.png
主库需要等待至少一个从库节点收到日志事件,并刷新 Binlog 到 Relay Log 文件中的 ACK 确认消息后,才能提交并返回,主库一般不需要等待所有从库的 ACK。注意 :该 ACK 是确保日志传送到从库并写入到中继日志,而不是从库已经完成重放、将数据写入完成。详见图 1。相对于异步复制,半同步复制提高了数据的安全性,同时也带来了一定程度的延迟,这个延迟最少是一个 TCP/IP 往返的时间。所以,半同步复制最好在低延时的网络中使用。
一般在实际生产中,我们把同城或同机房,网络延迟小的(2ms 内)设置为半同步,对于异地延迟在 10ms 以上的,采用异步复制的方式。并且一般将
rpl_semi_sync_master_wait_for_slave_count
设置为1,在数据一致性的情况下,可以最大程度保证主库事务处理的性能 -
同步复制
主库执行完一个事务,且所有从库都执行了该事务才返回给客户端。因为需要等待所有从库才能返回,所以事务的时间会被拉长,从而性能必然会受到较大影响,一般不建议采用此方案。
主从异步复制实践
从上面复制的机制上可以知道,在复制进行前,slave上 必须具有 master 上部分完整内容作为复制基准数据。例如, master 上有数据库A,二进制日志已经写到了 pos1 位置,那么在复制进行前,slave 上必须要有数据库A,且如果要从 pos1 位置开始复制的话,就必须要有和 master上 pos1 之前完全一致的数据。
如果不满足这样的一致性条件,那么在 relay log 的时候将不知道如何进行应用而导致数据混乱。也就是说,复制是基于 binlog 的 position 进行的,复制之前必须保证position一致。
下面是用 mysql docker来实践主从异步复制。
-
创建主服务器:
# mkdir -p master/conf master/data master/logs # docker run --name mysql-master \ -p 4306:3306 \ -v `pwd`/master/conf:/etc/mysql/conf.d \ -v `pwd`/master/data:/var/lib/mysql \ -v `pwd`/master/logs:/var/log/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ -d mysql
-
创建备服务器:
# mkdir -p backup/conf backup/data backup/logs # docker run --name mysql-slave -p 5306:3306 \ -v `pwd`/slave/conf:/etc/mysql/conf.d \ -v `pwd`/slave/data:/var/lib/mysql \ -v `pwd`/slave/logs:/var/log/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ -d mysql
-
模拟数据
给主数据库创建数据库表并插入几条模拟的数据: -
配置文件修改:
主服务器:[mysqld] ... server-id = 1 binlog-do-db = TestDB # 要同步的数据库 log-bin = /var/log/mysql/binlogs/mysql-bin # 要生成二进制日志文件 主服务器一定要开启,这里注意要给mysql权限 expire_logs_days=7 # 7天过期
备服务器:
[mysqld] ... server-id = 2 binlog-do-db = TestDB # 要同步的数据库
可以使用如下命令查看本数据库的 server_id
mysql> show variables like 'server_id';
-
创建主服务器复制用户相关权限用于从服务器连接
mysql> create user 'slave'@'%' identified by '123456'; // 创建用户 mysql> grant replication slave,replication client on *.* to 'slave'@'%'; // 设置用户权限 mysql> flush privileges; // 刷新权限 mysql> show grants for 'slave'@'%'; // 查看用户权限
这一步可以在从服务器上连接登录主服务器验证。
-
数据同步
前面提到同步需要从服务器的起点和主服务器一致,所以这里把主服务器数据导出来给从服务器mysql> FLUSH TABLES WITH READ LOCK;
FLUSH TABLES WITH READ LOCK 执行后所有库所有表都被锁定只读,这时候,写操作将会被阻塞,读操作可顺利进行。
将数据导出再导入到从数据库后,执行如下命令,解除数据库的锁定。mysql> UNLOCK TABLES;
-
从服务器开启复制
主服务器查看 binlog 状态:mysql> show master status;
连接主服务器及设置复制的起始节点:
mysql> change master to master_host='172.17.0.3', \ master_port=3306, \ master_user='slave', \ master_password='123456', \ master_log_file='mysql-bin.000001', \ master_log_pos=769;
这里的 master_log_file master_log_pos 和上面在主服务器上查询的结果要一一对应。
从服务器启动复制:
mysql> start slave;
查看复制状态:
mysql> show slave status\G
重点查看 Slave_IO_RUNNING: Yes 和 Slave_SQL_RUNNING: Yes
如果这一步出错,那么需要使用,重置状态。mysql> reset slave
网友评论