12
上篇文章详细介绍了MySQL数据库的单机多实例搭建,本篇文章将在上篇文章的基础上介绍MySQL主从复制过程,其中常见的复制架构有:异步复制,半同步复制及同步复制。常用的复制架构有异步复制及半同步复制!微信搜索公众号:”菜鸟封神记“,定期分享一线大厂常用技术干货。

一、常见的复制架构

1、主主复制
(1)结构图:

图片描述

(2)说明:主主复制即复制的两个实例互为主从,两个库中都可以同时读和写;
(3)优点:

a、对于读写请求都较多的需求,可以在多个实例之间分摊读写请求,减轻单实例的读写压力
b、互为主从,当一个示例出故障时,可以迅速切换到另外一个实例上,提供读写服务

2、一主一从
(1)结构图:

图片描述

(2)说明:指的是在两个数据库实例中,一个实例扮演着主库的角色,另一个实例扮演着从库的角色。这种方案中,从库通常用来作为备份使用,提供服务的多为主库;
(3)优点:

a、多数情况下,可以有效降低因某台数据库服务器故障而导致数据丢失的概率
b、作为备份服务器,可以在从库上完成在线数据的全备份,而不影响主库的写服务

3、一主多从
(1)结构图:

图片描述

(2)说明:指的是在多个数据库实例中,只包含了一个主库,其他实例都作为该主库的从库,这种架构是业务规模较大场景中的一种复制架构;
(3)优点:

a、该方已经比较成功,而且使用范围极为广泛,出问题之后可以迅速找到解决方案
a、作为主库的备份,可以迅速扩展多个从库
b、可以使用mysql-proxy等中间件提供读写分离服务,通过多个从库来应对大量的读请求,提高网站的吞吐量
c、当主库出故障时,从库可以快速接管主库,成为新的主库,提供写服务

二、主从复制的原理和过程

1、主从异步复制的原理

主库上的二进制bin-log中记录主库的所有DML操作,同时在主库上运行有一个IO线程,用于响应从库上的bin-log日志读取请求;在从库上运行有一个IO线程和一个SQL线程,IO线程会实时通过网络请求去从库上读取bin-log日志,然后写入到自身的relay-log日志文件中,同时运行在从库上的SQL线程会去解析并读取relay-log,然后在自身库上执行读取到的SQL,完成主从数据的同步,示意图如下:
图片描述
2、主从同步的工作过程
(1)详细过程

a、主库上会开启了二进制bin-log日志记录,同时运行有一个IO线程;
b、主库上对于需要同步的数据库或者表所发生的所有DML操作都会被记录到bin-log二进制日志文件中;
c、从库上开启relay-log日志,同时运行有一个IO线程和一个SQL线程;
d、IO线程负责从主库中读取bin-log二进制日志,并写入到本地的relay-log日志中,同时记录从库所读取到的主库的日志文件位置信息,以便下次从这个位置点再次读取;
e、SQL线程负责从本地的relay-log日志中读取同步到的二进制日志,并解析为数据库可以识别的SQL语句,然后应用到本地数据库,完成同步;
f、执行完relay-log中的操作之后,进入睡眠状态,等待主库产生新的更新;

(2)以上详细过程可总结为三步

第一步:主库在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中;
第二步:从库开启一个I/O线程,该线程对主库打开一个普通连接,主要工作是读取二进制日志。如果读取的进度已经跟上了主库,就进入睡眠状态并等待主库产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中;
第三步:SQL线程会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致;

三、MySQL异步复制搭建过程(单机多实例介绍,沿用上篇文章中搭建的多实例环境)
1、环境准备

操作系统:CentOS6.9
服务器IP:192.168.0.10
数据库版本:MySQL-5.6.39
数据库实例:实例1--3306端口(主),实例2--3307端口(从)

2、编辑3306实例的配置文件,打开该实例的二进制日志,并修改server-id,如下

[root@WB-BLOG ~]# cd /mysql_data/3306/
[root@WB-BLOG 3306]# vim my.cnf
    [mysqld]
    server_id=3
    log_bin=/mysql_data/3306/data/mysql-bin
    log_bin_index=/mysql_data/3306/data/mysql-bin-index
    binlog_format=mixed
    ...

参数解释:

(1)server-id:用来标识一个唯一的实例,如果是在同一个局域网内,可以使用ip地址的最后一段,要保证唯一
(2)log_bin:二进制日志文件的路径,mysql用户对该路径必须具有读写权限
(3)log_bin_index:二进制文件的索引路径,mysql用户对该路径必须具有读写权限
(4)binlog_format:表示二进制日志内容的记录方式,有三种方式:
    a、row:基于行记录的方式,MySQL会将真实发生变化的行记录进日志,所以如果有update更新全表的操作,二进制日志文件会变得非常大。通常用于SQL语句复杂但是影响的行比较少的场景
    b、statement:基于语句的方式,MySQL会将导致数据发生变化的SQL语句记录到日志文件中,适用于一条语句影响很多行的场景,但是注意当在主库上使用到了UUID,SYSDATE,FOUND_ROWS函数时,使用statement方式的复制会出现主从不一致的情况;
    c、mixed:混合记录模式,MySQL会自动进行判断具体是使用row格式还是statement格式,通常情况下都使用mixed,由MySQL来进行判断

3、重启主库

[root@WB-BLOG ~]# cd /mysql_data/3306/
[root@WB-BLOG 3306]# ./mysqld restart

4、备份主库的数据

[root@WB-BLOG 3306]# cd /usr/local/mysql-5.6.39/bin/
[root@WB-BLOG tmp]# ./mysqldump -uroot -proot -h127.0.0.1 -P3306 -S /mysql_data/3306/data/mysql.sock -A --master-data=2 -F --single-transaction | gzip > /tmp/mysql_all.sql.gz

参数说明:

-S:指定socket文件,单机多实例必须要指定
-A:--all-databases,表示备份所有的数据库
--master-data:表示change master命令是否包括在备份之后的sql文件中,常用的值有1和2
    1:表示change master指令在sql文件中处于打开状态,可用于快速创建主从同步,不用再次手动修改日志文件名称和位置点
    2:表示change master指令在sql文件中会被注释,从库上使用change master时需要手动指定日志文件的文件名和位置点
-F:表示备份日志的时候刷新二进制日志,重新创建一个新的二进制日志文件
--single-transaction:用于InnoDB存储引擎格式的表备份,导出开始时设置事务隔离状态并使用一致性快照开始事务,而后马上执行unlock tables,然后执行导出
gzip:表示将备份的sql文件压缩
#其他常见参数在后面的MySQL数据备份于恢复会详细介绍

5、登陆主库,然后创建复制账户

[root@WB-BLOG 3306]# cd ..
[root@WB-BLOG mysql_data]# ./mysql_login.sh

mysql-server-3306> USE mysql
#授权从库的
mysql-server-3306> GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* to 'repl'@'127.0.0.1' IDENTIFIED BY 'repl';
mysql-server-3306> FLUSH PRIVILEGES;

6、查看主库的二进制日志文件及位置点

mysql-server-3306> show master status \G
*************************** 1. row ***************************
             File: mysql-bin.000014
         Position: 367
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 
1 row in set (0.00 sec)

7、将主库导出的数据导入从库中

[root@WB-BLOG mysql_data]# cd /usr/local/mysql-5.6.39/bin/
[root@WB-BLOG bin]# gzip -d /tmp/mysql_all.sql.gz | ./mysql -uroot -proot -S /mysql_data/3307/data/mysql.sock

8、修改从库的配置文件,开启relay-log日志,并设置server-id,如下

[mysqld]
server-id=4
relay_log=/mysql_data/3307/data/relay-log
relay_log_index = /mysql_data/3307/data/relay-log-index
...

9、修改从库上的master指向,使其指向主库,并且从主库上最新的二进制日志和位置点开始同步,然后启动主从同步

[root@WB-BLOG mysql_data]# ./mysql_login.sh
mysql-server-3307> CHANGE MASTER TO master_host = '127.0.0.1',master_port = 3306,master_user='repl',master_password='repl',master_log_file='mysql-bin.000014',master_log_pos = 367; 
mysql-server-3307> START SLAVE;
mysql-server-3307> SHOW SLAVE STATUS \G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 127.0.0.1
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000015
          Read_Master_Log_Pos: 425
               Relay_Log_File: relay-log.000004
                Relay_Log_Pos: 588
        Relay_Master_Log_File: mysql-bin.000015
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:

注意:上述结果中Slave_IO_Running和Slave_SQL_Running都为Yes表示主从同步成功,如果为Connecting...,可以等待一会再次查看,如果为No,表示同步失败;
参数说明:

master_host:主库的主机名或者IP地址
master_port:主库的端口号,必须为整数,不能加引号,否则会提示错误
master_user:在主库上添加的复制用户名称
master_password:在主库上添加的复制用户密码
master_log_file:主库当前的二进制日志文件名称
master_log_pos:主库当前的二进制文件位置点,整数,不可加引号,否则会提示错误

开启主从的另外一种方法是分别开启SQL线程和IO线程,如下:

mysql> START SLAVE IO_THREAD;
mysql> START SLAVE SQL_THREAD;

10、验证,登陆主库,然后创建数据库,查看从库是否可以正常同步

mysql-server-3306> CREATE DATABASE test_db;
mysql-server-3306> QUIT
mysql-server-3307> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
| test_db            |
+--------------------+
5 rows in set (0.00 sec)
#从上面的结果可以看到,test_db已经同步到3307实例上了

11、至此,MySQL的主从复制搭建完毕。

12、主从同步中常见的问题
(1)从库的IO线程无法连接,通过"show slave status G"可以查看到具体的错误信息
原因1:在主库上创建的用户授权错误,导致从库无法远程连接主库
解决办法1:在主库上通过"show grants for 'user'@'ip';"查看授权是否正确,如果错误,重新授权即可

原因2:如果是独立主机上的两个主从数据库实例,授权正确的情况下,可能是由于主库的防火墙拦截导致从库无法连接主库
解决办法2:关闭主库的防火墙,或者在主库所在服务器添加防火墙规则,允许从库的tcp连接

(2)从库启动的时候提示server-id冲突,导致无法同步主库上的数据
原因:主从库配置文件中的server-id相同了
解决办法:将主库可从库配置文件中的server-id改为不同,重新开启从库上的同步即可

(3)在从库上执行了创建库或者表的操作,然后在主库上又执行了一遍,导致同步错误,如下:

           Last_SQL_Error: Error 'Can't create database 'test1'; database exists' on query. Default database: 'test1'. Query: 'create database test1'

原因:从库上创建了库,主库上再次创建,从库会将主库上的创建过程再次应用到从库,导致从库上创建同名的库,发生错误
解决办法:停止从库,然后设置sql_slave_skip_count,使其跳过同步主库创建库的操作,从下一个操作开始同步,如下:

#停止从库
mysql-server-3307> STOP SLAVE;
Query OK, 0 rows affected (0.00 sec)
#向前跳跃一步,从下一个点开始同步
mysql-server-3307> SET GLOBAL sql_slave_skip_counter =1;
Query OK, 0 rows affected (0.00 sec)
#重新开启从库上的同步
mysql-server-3307> START SLAVE ;
Query OK, 0 rows affected (0.03 sec) 
#再次查看,发现已经正常

针对直接写从库的操作,可以再从库上创建一个普通用户,授予其部分操作权限,然后设置从库的只读,通过在从库的配置文件中增加"read-only"参数来设置。但是注意,这个参数对而且只对非super用户生效,对root用户没有任何效果。

13、再生产场景下如何保证主库上的用户可以有写权限,从库上的用户只有读权限
方法1:在设置从库同步的时候,排除对mysql系统库的同步,通过在配置文件中指定binlog_ignore_db=mysql来排除不需要同步的库,或者在配置文件中指定binlog_do_db=db_name只来同步需要同步的库,然后分别在主库上创建可以写的用户,在从库上创建只能读的用户;

[mysqld]
binlog_ignore_db=mysql
binlog_do_db=user_db

方法2:在未排除任何库的情况下,先在主库上创建可以读写的用户,然后在从库中从新回收用户的写权限;
方法3:在主库和从库上创建不同的用户,然后分别授予不同的权限,使得主库只能写,从库只能读;

四、MySQL半同步搭建过程(介绍过程仍然使用单机多实例的环境)

1、定义
是介于异步复制和全同步复制之间的一种复制方式,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。

2、优缺点
(1)优点:有效的提高了数据的安全性,需要等到数据写到从库之后才返回给客户端;
(2)缺点:因为需要等待至少一个从库接收到并写入relaylog中,索引会造成一定的网络延迟,需要在网络延迟较低的环境中使用

3、搭建过程
(1)前提条件:

a、MySQL数据库版本为5.5及以上
b、属性变量have_dynamic_loading的值为YES
c、异步复制已经搭建完成

(2)查看主库和从库上的have_dynamic_loading变量

[root@WB-BLOG mysql_data]# ./mysql_login.sh 
mysql-server-3306> SHOW VARIABLES LIKE 'have_dynamic_loading';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| have_dynamic_loading | YES   |
+----------------------+-------+
1 row in set (0.00 sec)

(3)登陆主库,在主库上安装半同步插件

mysql-server-3306> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.01 sec)
mysql-server-3306> SHOW PLUGINS \G
*************************** 43. row ***************************
   Name: rpl_semi_sync_master
 Status: ACTIVE
   Type: REPLICATION
Library: semisync_master.so
License: GPL
43 rows in set (0.00 sec)
#查看输出结果中包括上面的一行,表示半同步插件安装成功

注:如果想卸载半同步插件,可以使用如下命令:

mysql-server-3306> UNINSTALL PLUGIN rpl_semi_sync_master;

(4)登陆从库,安装从库上的半同步插件

mysql-server-3307> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.01 sec)
mysql-server-3307> SHOW PLUGINS;
*************************** 43. row ***************************
   Name: rpl_semi_sync_slave
 Status: ACTIVE
   Type: REPLICATION
Library: semisync_slave.so
License: GPL
43 rows in set (0.01 sec)

注:从库上的半同步插件,也可以使用如下命令完成卸载:

mysql-server-3307> UNINSTALL PLUGIN rpl_semi_sync_slave;

(5)查看插件是否加载成功
主库:

mysql-server-3306> SELECT plugin_name,plugin_status FROM information_schema.plugins WHERE plugin_name LIKE '%semi%';
+----------------------+---------------+
| plugin_name          | plugin_status |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE        |
+----------------------+---------------+
1 row in set (0.00 sec)

从库:

mysql-server-3307> SELECT plugin_name,plugin_status FROM information_schema.plugins WHERE plugin_name LIKE '%semi%';
+---------------------+---------------+
| plugin_name         | plugin_status |
+---------------------+---------------+
| rpl_semi_sync_slave | ACTIVE        |
+---------------------+---------------+
1 row in set (0.00 sec)

(6)配置并开启主库的半同步复制,然后重启主库

[root@WB-BLOG 3306]# vim my.cnf
#在mysqld段下面添加如下内容:
    [mysqld]
    plugin-load = rpl_semi_sync_master=semisync_master.so
    rpl_semi_sync_master_enabled=1
[root@WB-BLOG 3306]# ./mysqld restart

(7)配置并开启从库的半同步复制,然后重启从库

[root@WB-BLOG 3307]# vim my.cnf
#添加如下内容:
    [mysqld]
    plugin-load = rpl_semi_sync_slave=semisync_slave.so
    rpl_semi_sync_slave_enabled=1
[root@WB-BLOG 3307]# ./mysqld restart

(8)重启从库上的IO线程

mysql-server-3307> STOP SLAVE IO_THREAD;          
Query OK, 0 rows affected (0.00 sec)
ysql-server-3307> START SLAVE IO_THREAD;
Query OK, 0 rows affected (0.00 sec)

(9)查看主库和从库上的半同步复制是否在运行
登录主库查看:

mysql-server-3306> SHOW STATUS LIKE 'rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON    |
+-----------------------------+-------+

登录从库查看:

mysql-server-3307> SHOW STATUS LIKE 'rpl_semi_sync_slave_status';      
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.00 sec)

上述结果表示主库和从库上的半同步复制运行正常。

(10)验证半同步复制是否正常
验证方法:正常在主库上创建一张表,会立刻返回,耗时0.1s。关闭从库的io线程,然后在主库上执行建表操作,会发现,主库上回阻塞10秒之后才会返回,而这个时间正好和主库上的rpl_semi_sync_master_timeout相同,表示半同步起作用了,主库的DDL操作需要等到从库应用完relaylog之后才返回;

#主库执行:
mysql-server-3307> STOP SLAVE IO_THREAD;

#从库执行:
mysql-server-3306> CREATE TABLE test(id int);
Query OK, 0 rows affected (10.03 sec)

#查看主库上的rpl_semi_sync_maser_timeout
mysql-server-3306> SHOW VARIABLES LIKE 'rpl_semi_sync_master_timeout';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| rpl_semi_sync_master_timeout | 10000 |
+------------------------------+-------+

至此,MySQL的半同步复制搭建完成。

4、半同步搭建中常见问题
(1)主从不能正常同步:和主从同步无法正常复制的排查方法相同
(2)不能正常安装半同步插件
原因1:可能是版本问题
解决办法1:查看MySQL实例的版本,如果版本问题,更换新版本重新安装即可

mysql> SELECT version();

原因2:MySQL的安装目录中未包含用于半同步复制的共享库
解决办法2:找到该版本对应的半同步共享库,然后重新安装

五、全同步复制
同步复制在所有复制方案中最安全,但是性能最差,而且需要使用DRBD(分布式复制块设备)来完成数据的同步,DRBD是一种类似于"rsync+inotify"的架构,通常使用较少,几乎不用,此处不做详细介绍。

到此,MySQL的主从复制介绍完毕,主从复制是一块很大的内容,包括延迟排查,数据一致问题、快速主从搭建及主从复制的高可用,后面会继续写文章介绍,欢迎转发评论!

后续更多文章将更新在个人小站上,欢迎查看。

另外提供一些优秀的IT视频资料,可免费下载!如需要请查看https://www.592xuexi.com


夏日寒冰
318 声望85 粉丝

忠实的技术爱好者,追求极致,喜欢总结一些自己用过的技术点,与他人交流分享。