复制的简单概述

复制(replication)是Mysql数据库提供的一种高可用性能的解决方案,它解决的问题是让一台服务器的数据与其他服务器保持一致,一台主库的数据可以同步到多台备库上,备库本身也可以被配置成另外一台服务器的主库,主库和备库之间有多种不同的组合方式。

Mysql中的复制分为两种:基于行的复制和基于语句的复制(二者下文介绍),这两种方式都是通过在主库上记录二进制日志(bin log),在备库上使用重放日志(relay log)来实现异步的数据复制。需要注意的是,无论是哪种复制,都是有延迟的,在同一时间点备库上的数据和主库上的数据并不是一致的,一些大的语句可能导致备库产生机几秒、几分钟、甚至几个小时的延迟。

为什么要有复制

因为我们的需要,所以才有了复制的功能,那么复制解决了哪些问题?

1、高可用性和故障切换

复制可以帮助应用程序避免Mysql单点失败,快速的切换系统能够显著缩短宕机的时间。

2、备份

复制是Mysql中备份的一种技术补充,但复制并不是备份,也不能取代备份。

3、Mysql升级测试

Mysql需要升级的时候,使用更高版本的Mysql作为备库。

4、数据分布

Mysql中的复制操作不会对带宽造成很大的压力,所以当网络环境不够稳定的时候,也可以完成复制操作,可以在不同的地理位置来分布数据备份,但是网络延迟高,那复制的延迟也会变高。

5、负载均衡

通过Mysql复制可以将读操作分布到多个服务器上,实现对读密集型应用的优化,并且实现方便。外界客户端可以从各个备份中来读取数据,但是写入从主机上写入,也就是实现了读写分离,分散压力,提高性能。

复制的工作方式

复制工作主要分为3个步骤:

1、在主库上把数据的更改记录到二进制文件(bin log)中。

2、备库将主库上的日志复制到自己的中继日志(relay log)中。

3、备库读取中继日志中的事件,将其重新放到备份数据库中。

工作图如下:

dump mysql 复制库 mysql复制方式_SQL


1、第一步就是将主库中提交后的事务记录到bin log 文件中。

2、第二步,备库会启动一个工作线程,称为I/O线程,该线程会和主库建立一个普通的客户端连接,然后在主库上启动一个特殊的二进制转储(binlog dump)线程,这个二进制转储线程会读取主库上二进制文件中的事件给备库中的I/O线程,该二进制存储线程不会对事件进行轮询,如果该线程读取的进度追上了二进制文件中的事件,它就会进入休眠状态,直到主库发送信号量通知其有新的事件产生时才会被唤醒,然后备库中的I/O线程将接收到的事件记录到中继日志中。

3、第三步是备库中的SQL线程从中继日志中读取事件并在备库执行,读入备库自己的二进制文件中从而实现备库数据的更新;当SQL线程追赶上I/O线程时,中继日志通常已经在系统缓存中,所以中继日志的开销很低。

复制方式的分类

Mysql中支持两种复制方式:基于行的语句的复制(逻辑复制)和基于行的复制。

基于语句的复制

在Mysql5.0之前的版本中只支持基于语句的复制,基于语句的复制下,主库会记录那些造成数据更改的SQL语句,当备库读取并重放这些事件时,实际上只是把主库上执行过的SQL再执行一遍。

优点

1、简单,理论上只需要记录和执行这些SQL语句即可。
2、另一个好处是记录在bin log 文件里的事件比较紧凑,所以相对而言,基于语句的复制不会使用太多的带宽,除此之外,该方式占用存储空间少,一条更新好几兆的语句在二进制日志里可能只占几十个字节。

缺点

1、主库和备库中同一条SQL的执行时间可能会不相同,在主库中就是单纯的SQL,但传输到备库中除了SQL外,还会包含一些元数据信息,例如时间戳,并且备库可能存在无法正确复制SQL的现象。
2、更新必须是串行的,另外并不是所有的存储引擎都支持这种复制方式。

基于行的复制

Mysql 5.1开始支持基于行的复制,这种方式也是将实际数据记录到二进制日志当中,只不过在复制的过程中是一行一行来复制。他的优点就是可以正确的复制每一行,效率更高。几乎没有基于行复制无法处理的场景,对于所有的SQL构造、触发器、存储过程等都能够正确执行。在某种情况下,基于行的复制能够帮助更快地找到并解决数据不一致的情况,举个例子:如果是使用基于语句的复制模式,在备库更新一个不存在的记录时不会失败,但是基于行的复制模式下则会报错并停止复制。

这两种方式没有哪一种是完美的,MySQL可以在这两种复制模式间动态的切换,默认情况下都是使用的基于语句的复制方式,但是如果发现语句无法被正确的复制,就切换到行的复制模式。

复制的实现

复制的原则:
1、每个备库必须有一个唯一的服务器ID。
2、一个主库可以有多个备库(或者对应的,一个备库可以有多个兄弟备库)。
3、如果打开了 log_slave_updates 选项,一个备库可以把其主库上的数据变化传播到其他备库。
4、在MySQL 5.7 之前,一个备库实例只能有一个主库。

一主库多备库

除了最简单的两台服务器做主备结构外,一主多从的结构和基本配置差不多,备库之间是没有交互的(从技术上讲这并非正确,如果有重复的服务器ID,它们将陷入竞争,并反复将对方从主库上踢出),它们仅仅是连接到同一个主库上。
当有少量的写和大量的读时,这种配置是非常有用的,主库只负责写操作,可以把读操作分摊到多个备库上,直到备库给主库造成了太大的负担,或者说主备之间传输的带宽达到瓶颈时,可以取消备库上的读操作。

尽管这是非常简单的拓扑结构,但是它非常灵活,能够满足多种需求,下面是它的一些用途:
1、把一台备库服务器作为待用的主库,除了复制没有其他数据传输。
2、将一台备库放到远程数据中心,作为灾难恢复。
3、使用其中一个备库,作为备份、培训、开发或者测试使用服务器。
4、为不同的角色使用不同的备库(例如备库和主库的存储引擎不同,但不建议这么做)。

这种结构流行的原因是它比较简单,例如:可以方便的比较不同备库重放事件在主库二进制日志的位置,换句话说,如果在同一个逻辑点停止所有备库的复制,它们正在读取的是主库上同一个日志文件的相同的物理位置,这种特性只存在于兄弟备库之间,可以减轻管理员的许多工作,例如把备库升级为主库。

dump mysql 复制库 mysql复制方式_数据库_02

实现

1、进入主服务器之后,进入my.cnf文件

#进入my.cnf文件
vi /etc/my.cnf

2、修改配置文件里面的内容,加入如下内容:

# 主服务器的配置
#日志文件的名称
log-bin=master-a-bin
#二进制日志格式,有row、statement和mixed三种类型
binlog-format=ROW
#要求各个服务器的该id必须不一样
server-id=1
#同步数据库的名称(自己创建的)
binlog-do-db=copy

#允许外界可以连接该虚拟机
bind-address = 0.0.0.0

注意:如果mysql是使用docker安装的,需要修改docker容器里面的 my.cnf文件。
进入mysql容器后依次执行命令如下:

#如果没有安装vim 需要依次执行下面两个命令,安装则不用执行
apt-get update
apt-get install vim

#执行下面命令之后,将上面的配置文件内容放进去。
vim /etc/mysql/my.cnf

3、登录mysql

#用户名是root,密码是123456;可以自定义(u是user的意思,p是password的意思)
mysql -uroot -p123456

4、创建自己想要复制的数据库

#创建自己想要进行复制的数据库(copy 是数据库的名字)
create database copy;

#查看该服务器里面有多少个数据库
show databases;

5、使主服务器可以被外界连接,依次执行以下命令。

#mysql 8.0版本之后的 用户信息在 mysql 表里面,所以我们需要进入msyql 表来修改一些系统信息
use mysql;

#用户默认信息都存放在user表里面,所以需要更改user表中root用户的host,如果host 为 localhost表示只允许该服务器连接mysql,
#我们需要将它改为 ‘%’通配符,表示任何服务器都可以连接该服务器的msyql
update user set host='%' where user='root';

#然后刷新权限
flush privileges;

6、修改从服务器的my.cnf配置文件,在配置文件中加入如下配置:

#master -slave copy---slave
#日志文件名称
log-bin=master-a-bin
#二进制日志格式和主服务器一样
binlog-format=ROW
#各个服务器id不能一样
server-id=2
#中继日志执行之后,这些变化是否需要计入自己的bin log。当从服务器需要作为另外
#一个主服务器的时候需要打开
log-slave-updates=true

7、登录从服务器中的mysql之后,创建和主服务器一样的数据库

create database copy;

8、进入主服务器的mysql,查看mysql状态

show master status;

dump mysql 复制库 mysql复制方式_SQL_03


9、重启主、从服务器的mysql,需要使配置文件生效。

10、从服务器进入mysql 然后执行以下命令给从服务器授权(和上图中的主服务内容对应上)

CHANGE MASTER TO MASTER_HOST='192.168.162.128',MASTER_USER='root',MASTER_PASSWORD='123456',MASTER_LOG_FILE='master-a-bin.000001',MASTER_LOG_POS=156;

#解释
MASTER_HOST:主服务器ip地址
MASTER_USER:主服务器root用户
MASTER_PASSWORD:主服务器密码
MASTER_LOG_FILE:主服务器bin文件名
MASTER_LOG_POS:主服务器偏移量

11、刷新权限

flush privileges;

12、启动复制

start slave;

13、查看状态

show slave status \G;

下面是整理常见的错误:

1、如果为connecting表示正在连接主服务器,没有连接上,正确的应该是yes

dump mysql 复制库 mysql复制方式_mysql_04


这时需要主服务器授权,回到第5步。

2、如果 slave IO Running 为 NO,往下找找,查看下面报的错误:

dump mysql 复制库 mysql复制方式_服务器_05


提示server id重复了,需要在my.cnf文件里面进行修改,临时修改可以使用如下方法:

#查看从服务器server id
show variables like 'server_id';

#如果和主服务器一样,则修改server id
set global server_id = 2;

#之后重新启动查看状态
start slave;

show slave status \G;

3、如果slave _SQL_Running 为No:

dump mysql 复制库 mysql复制方式_mysql_06


出现上面错误的原因一般是主库的操作无法复制到从库,比如从库自己先建了一个表,主库也建了一个和从库一样名字的表,从库就会无法同步,产生上面的错误。

也是查看错误之后,依次执行以下语句

stop slave;

SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; 

start slave;

show slave status \G;

问题思考

1、如果先搭建了一台从服务器,这时主库建立了一张新表,然后又搭建了第二台从服务器,那新建的这张表会同步到第二台服务器吗?
答:不会。
2、如果从服务器宕机了一段时间,这期间主服务中的操作还会同步到从服务器吗?
答:会。
3、如果从服务器宕机了一段时间,然后再次启动,需要重新配置连接到主服务器吗?
答:不用,重新启动复制命令(start slave)即可。
4、从服务器可以自己创建表吗?
答:可以,但是不会影响主服务器和其他从服务器。如果内容和主服务器重复,则会报错,SQL 线程就会停止运行。
以上问题是我验证过的,如果错误,欢迎指出。