mysql 主从_一文详解MySQL主从复制

bbc8b87afc2ae9ff36b9bbe278cda26e.png

    之前文章详细介绍了MySQL数据库的单机多实例搭建,可参考:《MySQL单机多实例搭建》。本篇文章将在上篇文章的基础上介绍MySQL主从复制过程,其中常见的复制架构有:异步复制,半同步复制及同步复制。常用的复制架构有异步复制及半同步复制!

一、常见的复制架构
1.1、主主复制
  1. 结构图:

d3c0492de64b63b82779ad73e2cdb728.png2. 说明

    主主复制即复制的两个实例互为主从,两个库中都可以同时读和写;

  1. 优点

a、对于读写请求都较多的需求,可以在多个实例之间分摊读写请求,减轻单实例的读写压力

b、互为主从,当一个示例出故障时,可以迅速切换到另外一个实例上,提供读写服务

1.2、一主一从
  1. 结构图
f4397a926a13db5d02f64c1780338277.png
  1. 说明

    一主一从复制指的是在两个数据库实例中,一个实例扮演着主库的角色,另一个实例扮演着从库的角色。这种方案中,从库通常用来作为备份使用,提供服务的多为主库;

  1. 优点

a、多数情况下,可以有效降低因某台数据库服务器故障而导致数据丢失的概率

b、作为备份服务器,可以在从库上完成在线数据的全备份,而不影响主库的写服务

1.3、一主多从
  1. 结构图
35eeefe99e5d186b0b6efec7d67c204a.png
  1. 说明

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

  1. 优点

a、该方已经比较成功,而且使用范围极为广泛,出问题之后可以迅速找到解决方案

b、作为主库的备份,可以迅速扩展多个从库

c、可以使用mysql-proxy等中间件提供读写分离服务,通过多个从库来应对大量的读请求,提高网站的吞吐量

d、当主库出故障时,从库可以快速接管主库,成为新的主库,提供写服务

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

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

994f851feacabdcb7b9509ec1538b04b.png
2.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中的操作之后,进入睡眠状态,等待主库产生新的更新;

  1. 复制过程总结

a、主库在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中;

b、从库开启一个I/O线程,该线程对主库打开一个普通连接,主要工作是读取二进制日志。如果读取的进度已经跟上了主库,就进入睡眠状态并等待主库产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中;

c、SQL线程会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致;

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

操作系统:CentOS6.9

服务器IP:192.168.0.10

数据库版本:MySQL-5.6.39

数据库实例:实例1--3306端口(主),实例2--3307端口(从)

3.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.3、重启主库
[root@WB-BLOG ~]# cd /mysql_data/3306/
[root@WB-BLOG 3306]# ./mysqld restart
3.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文件压缩

3.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;
3.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)
3.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
3.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
...
3.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;
3.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实例上了
3.11、至此,MySQL的主从复制搭建完毕。
3.12、主从同步中常见的问题
  1. 从库的IO线程无法连接,通过"show slave status G"可以查看到具体的错误信息

原因1:在主库上创建的用户授权错误,导致从库无法远程连接主库

解决办法1:在主库上通过"show grants for 'user'@'ip';"查看授权是否正确,如果错误,重新授权即可

原因2:如果是独立主机上的两个主从数据库实例,授权正确的情况下,可能是由于主库的防火墙拦截导致从库无法连接主库

解决办法2:关闭主库的防火墙,或者在主库所在服务器添加防火墙规则,允许从库的tcp连接

  1. 从库启动的时候提示server-id冲突,导致无法同步主库上的数据

原因:主从库配置文件中的server-id相同了

解决办法:将主库可从库配置文件中的server-id改为不同,重新开启从库上的同步即可

  1. 在从库上执行了创建库或者表的操作,然后在主库上又执行了一遍,导致同步错误,如下:
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用户没有任何效果。

3.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半同步搭建过程(介绍过程仍然使用单机多实例的环境)
4.1、定义

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

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

a、MySQL数据库版本为5.5及以上

b、属性变量have_dynamic_loading的值为YES

c、异步复制已经搭建完成

  1. 查看主库和从库上的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)
  1. 登陆主库,在主库上安装半同步插件
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;
  1. 登陆从库,安装从库上的半同步插件
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;
  1. 查看插件是否加载成功
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)
  1. 配置并开启主库的半同步复制,然后重启主库
[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
  1. 配置并开启从库的半同步复制,然后重启从库
[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
  1. 重启从库上的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)
  1. 查看主库和从库上的半同步复制是否在运行

登录主库查看:

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.4、半同步搭建中常见问题
  1. 主从不能正常同步:和主从同步无法正常复制的排查方法相同
  2. 不能正常安装半同步插件

原因1:可能是版本问题

解决办法1:查看MySQL实例的版本,如果版本问题,更换新版本重新安装即可

mysql> SELECT version();

原因2:MySQL的安装目录中未包含用于半同步复制的共享库 

解决办法2:找到该版本对应的半同步共享库,然后重新安装

五、全同步复制

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

近期精彩回顾:

《FactoryBean和BeanFactory是个啥?》

《聊聊MySQL的索引》

《聊聊Spring的自定义标签》

《Spring标签解析过程源码分析》

《Spring容器初始化可真不容易》

《SPI机制及使用场景》

常驻内容:

源码搭建:《Spring5.1.x源码环境搭建》

注释版源码传送门:进入公众号点击底部"获取源码"资料即可获取

关注菜鸟封神记,定期分享技术干货!

4f8a795bd93af72c1bf14cfb0d847fa2.png

点赞和在看是最大的支持,感谢↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值