MySQL主从复制异步原理以及搭建

Wesley13
• 阅读 465

MySQL主从复制的原理:

  1、首先,MySQL主库在事务提交时会把数据变更作为时间events记录在二进制日志文件binlog中;MySQL主库上的sync_binlog参数控制Binlog日志以什么样的方式刷新到磁盘上。

  2、主库推送二进制日志文件Binlog中的事件到从库的中继日志Relay Log中,之后从库根据中继日志Relay Log重做数据变更操作,通过逻辑复制以此来达到主库和从库的数据一致。

MySQL主从复制的关键点:

  MySQL通过3个线程来完成主从库间的数据复制,其中Binlog Dump线程跑在主库上,I/O线程和SQL线程跑在从库上。当在从库上启动复制时,首先创建I/O线程来连接主库,随后主库创建Binlog Dump线程读取数据库事件并发送给I/O线程,I/O线程获取到事件数据后更新到从库的中继日志Relay log中去,之后从库上的SQL线程读取中继日志Relay Log中更新的数据库事件并应用到从库上来。

示意图:

MySQL主从复制异步原理以及搭建

主库端查看线程:

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 55
   User: chaofeng
   Host: ip-172-31-26-133.ec2.internal:35750
     db: NULL
Command: Binlog Dump
   Time: 1433
  State: Master has sent all binlog to slave; waiting for more updates
   Info: NULL
*************************** 2. row ***************************
     Id: 56
   User: root
   Host: localhost
     db: NULL
Command: Query
   Time: 0
  State: starting
   Info: show processlist
2 rows in set (0.00 sec)

从主库端的Binlog Dump线程的状态我们可以看到,MySQL的复制时主库主动推送日志到从库上去的,属于“推”日志的方式来做同步。

 MySQL主从复制中的各类文件

从MySQL的复制流程可以看到复制过程中涉及了两类非常重要的日志文件:二进制日志文件(Binlog)和中继日志文件(Relay Log)

 二进制日志文件会把MySQL中的所有数据修改操作以二进制的形式记录到日志文件中,其中包括:create、drop、insert、update、delete等。但是二进制日志文件并不会记录select操作,因为select并不对数据做修改。

我们可以来查看一下Binlog的格式,Binlog支持三种格式:Row、Statement、Mixed

mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

中继日志Relay log与二进制日志文件Binlog在文件格式、内容上等都是一样的。唯一的区别就是:从库上的SQL线程在执行完Relay log中的事件之后,SQL线程还会自动删除当前中继日志文件。这样做的原因是为了避免中继日志Relay Log过多从而占用磁盘空间。

为了保证从库crash重启之后,从库的I/O线程和SQL线程仍然能够知道从哪里开始继续复制,从库默认还会创建两个日志文件master.info和relay-log.info用来保存复制的进度。master.info用来保存从库的I/O线程当前读取的二进制日志Binlog的进度,relay-log.info用来保存SQL线程应用中继日志Relay log的进度

下面青绿色表示master.info文件的信息。紫色表示relay-log.info文件的信息。

MySQL [(none)]> show slave status\G*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.31.22.29                          #主库IP
                  Master_User: chaofeng                            #主库上用户主从复制的账号
                  Master_Port: 3306                              #主库的mysql的端口
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001                       #从库I/O线程当前读取的主库Binlog文件名
          Read_Master_Log_Pos: 915                               #从库I/O线程读取主库上Binlog的位置
               Relay_Log_File: ip-172-31-26-133-relay-bin.000002             #SQL线程正在应用的Relay Log
                Relay_Log_Pos: 479                               #SQL线程郑州应用的Relay Log的位置
        Relay_Master_Log_File: mysql-bin.000001                       #SQL线程正在应用的Relay Log对于的Binlog
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 915                              #SQL线程正在应用Relay Log的位置对应主库Binlog的位置                             
              Relay_Log_Space: 697
             Master_Server_Id: 1
                  Master_UUID: ce15f67e-0ccc-11e9-9e0a-0af74ce261dc
             Master_Info_File: /data/data_mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 1 row in set (0.00 sec)

搭建步骤:

主从同步的MySQL版本最好一致,我们这里使用两台机器来做演示,每台机器均部署一个MySQL实例

master:172.31.22.29

 

slave:172.31.26.133

 

1、在主库上,设置一个复制使用的账户,并授予" REPLICATION SLAVE "权限。

mysql> GRANT REPLICATION SLAVE ON *.* TO 'chaofeng'@'172.31.26.133' IDENTIFIED BY '123456789';
Query OK, 0 rows affected, 0 warning (0.00 sec)

这个操作表示创建一个复制用户chaofeng,可以从172.31.26.133这个主机上进行连接,密码为123456789

2、在主库这台机器上,修改my.cnf文件,开启binlog日志功能,并设置server_id的值。修改完并重启服务

[mysqld]

#secure_file_priv = /root
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
basedir = /usr/local/mysql
datadir = /data/data_mysql
log-bin=/usr/local/mysql/logs/mysql-bin.log                    #这里我把日志单独放置一个目录下。
server_id=1
log-error=/usr/local/mysql/logs/mysql.log
port = 3306

3、在主库上,设置读锁定有效,这个操作是为了确保没有数据库操作,以便获取一致性的快照。

mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)

4、查看主库上此时的二进制日志名以及偏移位。

mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      756 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.01 sec)

5、接下来我们就开始备份主库的数据了,这里我们直接通过将mysql数据目录进行打包操作发送给从库。因为我们这里的主库服务可以停止,因此我们直接复制文件是最快的操作,但是在生产环境中你需要考虑很多。

[root@:vg_adn_tidbCkhsTest:172.31.22.29 /data/data_mysql]#tar -zcf mysql.tar.gz ./*
[root@:vg_adn_tidbCkhsTest:23.22.172.65:172.31.22.29 /data/data_mysql]#ls
auto.cnf  ib_buffer_pool  ibdata1  ib_logfile0  ib_logfile1  ib_logfile2  ibtmp1  mysql  mysql.pid  mysql.tar.gz  performance_schema  sys

6、主库完成之后我们就可以恢复主库的写操作了

mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)

7、将mysql.tar.gz发送给从库。并解压到从库的数据目录下

[root@:vg_adn_tidbCkhsTest:172.31.26.133 /data]#tar -zxf mysql.tar.gz -C /data/data_mysql
[root@:vg_adn_tidbCkhsTest:172.31.26.133 /data]#ls data_mysql
auto.cnf  ib_buffer_pool  ibdata1  ib_logfile0  ib_logfile1  ib_logfile2  ibtmp1  mysql  mysql.pid  performance_schema  sys

9、接下来我们开始配置从库,修改从库的配置文件/etc/my.cnf,其中有个server_id参数,注意这个server_id的值必须与主库的server_id不一样,如果是多个从库,每个服务器的server_id值都不能相同。

[mysqld]
server_id=2

从库不必开启binlog功能。

10、在mysql5.7以上的版本中,除了保证server_id不一样外,还有保证server_uuid不一样才行。server_uuid的值在MySQL数据目录下的auto.cnf文件下

查看主库的server_uuid

[root@:vg_adn_tidbCkhsTest:172.31.22.29 /data/data_mysql]#cat auto.cnf
[auto]
server-uuid=ce15f67e-0ccc-11e9-9e0a-0af74ce261dc

查看从库的server_uuid

[root@:vg_adn_tidbCkhsTest:172.31.26.133 /data/data_mysql]#cat auto.cnf
[auto]
server-uuid=ce15f67e-0ccc-11e9-9e0a-0af74ce261dc

 因为我们是直接将主库的数据目录复制过来的,因此两个文件下的auto.cnf的值是一模一样的,我们修改一下从库的这个值,只修改一位即可,只要能保证这两个值不一样就行。

修改从库的server_uuid值为

server-uuid=ce15f67e-0ccc-11e9-9e0a-0af74ce261dd

11、接下来重启从服务器,对从数据库服务器进行设置,这里主要设置一下几项:

MySQL [(none)]> CHANGE MASTER TO
          -> MASTER_HOST='172.31.22.29',
          -> MASTER_PORT=3306,        -> MASTER_USER='chaofeng',
          -> MASTER_PASSWORD='123456789',
          -> MASTER_LOG_FILE='mysql-bin.000001',
          -> MASTER_LOG_POS=756;
Query OK, 0 rows affected, 2 warnings (0.02 sec)

12、从库上启动线程

MySQL [(none)]> start slave;
Query OK, 0 rows affected (0.00 sec)

13、这时在slave上执行show processlist命令将显示类似如下的信息:

MySQL [(none)]> show processlist\G
*************************** 1. row ***************************
     Id: 4
   User: root
   Host: localhost
     db: NULL
Command: Query
   Time: 0
  State: starting
   Info: show processlist
*************************** 2. row ***************************
     Id: 6
   User: system user
   Host: 
     db: NULL
Command: Connect
   Time: 5
  State: Waiting for master to send event
   Info: NULL
*************************** 3. row ***************************
     Id: 7
   User: system user
   Host: 
     db: NULL
Command: Connect
   Time: 5
  State: Slave has read all relay log; waiting for more updates
   Info: NULL
3 rows in set (0.00 sec)

或者你也可以这样子查看:

MySQL [(none)]> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.31.22.29
                  Master_User: chaofeng
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 756
               Relay_Log_File: ip-172-31-26-133-relay-bin.000002
                Relay_Log_Pos: 320
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 756
              Relay_Log_Space: 538
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 1
                  Master_UUID: ce15f67e-0ccc-11e9-9e0a-0af74ce261dc
             Master_Info_File: /data/data_mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

以上都是查看主从复制是否正常的方法,只要能看到上面标记青绿色的信息,就说明主从复制搭建成功了

主从复制异步存在的问题:

在MySQL5.5之前的版本中,MySQL的复制是异步复制,主库和从库的数据之间存在一定的延迟,比如网络故障等各种原因,这样子容易存在隐患就是:当在主库写入一个事务成功后并提交了,但是由于从库延迟没有及时得到主库推送的Binlog日志时,主库突然宕机了,那么此时从库就可能损失这个事务,从而造成主从不一致的状况。

因此我们MySQL5.5版本之后引入了半同步复制的概念,看下节文章

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
2年前
mysql 的 log 和 MVCC
1、binlogMySQL的二进制日志binlog可以说是MySQL最重要的日志,它记录了所有的DDL和DML语句(除了数据查询语句select),以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。使用binlog日志大约会有1%的性能损耗。应用:1)MySQL主从复制:MySQLReplication在Ma
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
MYSQL数据库之主从复制及读写分离
MYSQL数据库之主从复制及读写分离一、MySQL的主从复制1、概述  MySQLReplication俗称MySQLAB复制或主从复制,是MySQL官方推荐的数据同步技术。数据同步基本过程:从库会实时去读取主库的二进制日志文件,按照日志中记录对从座进行同样的操作,以达到数据同步效果。2、MySQLReplication优点
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这