主从复制实现原理
MySQL的复制(replication)是一个异步的复制,从一个MySQLinstace(称之为Master)复制到另一个MySQLinstance(称之Slave)。整个复制操作主要由三个进程完成的,其中两个进程在Slave(Sql进程和IO进程),另外一个进程在Master(IO进程)上。
要实施复制,首先必须打开Master端的binarylog(bin-log)功能,否则无法实现。因为整个复制过程实际上就是Slave从Master端获取该日志然后再在自己身上完全顺序的执行日志中所记录的各种操作。复制的基本过程如下:
(1)Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
(2)Master接收到来自Slave的IO进程的请求后,通过负责复制的IO进程根据请求信息读取指定日志指定位置之后的日志信息,返回给Slave的IO进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置;
(3)Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master“我需要从某个bin-log的某个位置开始往后的日志内容,请发给我”;
(4)Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行。
MySQL的复制有三种模式:Statement Level、Row Level、Mixed Level。复制级别的不同,会导致Master端二进制日志文件的生成形式的不同。
1 Statement Level复制
该模式是最早的复制模式,主要的流程是Master端将每一条会修改数据的Query记录下来,Slave端在复制的时候会根据二进制文件重新执行相同的Query。这种模式的优点是Master端不需要记录每一行数据的变化,二进制日志文件量小,IO成本低,速度快。
相应的,该模式存在的缺点如下:由于记录的是执行语句,就需要额外的知道每条语句执行的上下文信息,以保证该相同的操作在Slave端执行时能够得到和Master同样的结果。但由于MySQL功能的不断增多,这种复制模式需要考虑的情况也就越来越多,出现bug的几率也就也大。从MySQL 5.0开始,MySQL复制解决了大量的之前版本中出现的无法复制或复制错误的问题,但随着MySQL的发展,这种挑战将会日趋严峻。
2 Row Level复制
MySQL开发人员意识到Statement Level存在的问题,于5.1.5开始提供Row Level模式。该模式的主要流程是,MySQL二级制日志文件会将每一行数据修改都记录下来,然后在Slave端进行同样的修改。这种模式的优点是:日志文件不会将SQL语句执行的上下文记录下来,只是记录哪一条数据修改了,修改成什么样子了;这样做可以避免如某些特定情况下存储过程、trigger的调用和触发没有被正确执行等复制问题。
同样,该模式也存在缺点:日质量的成倍增加。例如:执行alter table之类的语句的时候,由于表结构修改,每条记录都发生改变,那么该表每一条记录都会记录到日志中。这样就大增加了复制过程的IO成本,导致速度下降、性能下降。
3 Mixed Level复制
MySQL从5.1.8开始,提供Mixed Level。该模式结合了之前两种模式的优点,规避了二者的缺点。在该模式下,MySQL会根据执行的每一条语句来区分记录日志文件的格式。举例说明,当涉及到复杂的存储过程时,采用Row Level,规避Statement Level存在的某些场景无法复制的问题;当涉及到Alter table等操作时,采用Statement Level来规避Row Level带来的日志量巨大的问题。
一.新建mysql主服务器容器实例3307
docker run -p 3307:3306 --name=mysql-master
-v /wuqirui/mysql-master/conf:/etc/mysql
-v /wuqirui/mysql-master/log:/var/log/mysql
-v /wuqirui/mysql-master/data:/var/lib/mysql
--privileged=true
-e MYSQL_ROOT_PASSWORD=root #发现命令 -e 在 -d 之后不生效
-d 192.168.32.130:8083/mysql:5.7
二. 在/wuqirui/mysql-master/conf目录下,新建my.cnf
(注意:复制到my.cnf中,注释和语句之前不能有空行,语句结尾不能有空格)
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=101
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
三.重启mysql-master
docker restart mysql-master
四.进入mysql-master容器
docker exec -it mysql-master /bin/bash
mysql -uroot -p
五.mysql-master容器内创建数据同步用户
#创建用户slave,密码123456。%表示任意ip,可指定ip。
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
#为slave用户授权 *.*表示复制所有库.所有表,&表示任意ip
GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'slave'@'%';
六.新建mysql从服务器容器实例3308
firewall-cmd --zone=public --add-port=3308/tcp --permanent
firewall-cmd --reload
docker run -p 3308:3306 --name=mysql-slave
-v /wuqirui/mysql-slave/log:/var/log/mysql
-v /wuqirui/mysql-slave/data:/var/lib/mysql
-v /wuqirui/mysql-slave/conf:/etc/mysql
-e MYSQL_ROOT_PASSWORD=root
-d 192.168.32.130:8083/mysql:5.7
七. 在/wuqirui/mysql-slave/conf目录下新建my.cnf
(注意:复制到my.cnf中,注释和语句之前不能有空行,语句结尾不能有空格)
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1
八.重启mysql-slave容器实例
docker restart mysql-slave
九.进入mysql-master容器实例,查看主从同步状态
docker exec -it mysql-master /bin/bash
show master status
十.进入mysql-slave容器实例
docker exec -it mysql-slave /bin/bash
十一. 在从数据库中配置主从复制
#主数据库的IP地址
change master to master_host='192.168.32.130',
#在主数据库创建的用于同步数据的用户账号
master_user='slave',
#在主数据库创建的用于同步数据的用户密码
master_password='123456',
#主数据库的运行端口
master_port=3307,
#指定从数据库要复制数据的日志文件,(通过查看主数据的状态,获取File参数)
master_log_file='mall-mysql-bin.000001',
#指定从数据库从哪个位置开始复制数据,(通过查看主数据的状态,获取Position参数)
master_log_pos=617,
#连接失败重试的时间间隔,单位为秒
master_connect_retry=30;
十二.从数据库查看主从同步状态
show slave status \G;
十三.在从数据库开启主从同步
start slave;
十四.再次查看从数据库主从同步状态
十五. 主从复制测试
1.主机新建库,新建表,插入数据
2.从机使用库查看数据