首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >MySQL主从同步-原理&实践篇

MySQL主从同步-原理&实践篇

作者头像
chengcheng222e
发布于 2021-11-04 11:46:56
发布于 2021-11-04 11:46:56
1.6K0
举报
文章被收录于专栏:简栈文化简栈文化
什么是mysql的主从复制?

MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。

Mysql复制原理

原理:

(1)Master服务器将数据的改变记录二进制Binlog日志,当Master上的数据发生改变时,则将其改变写入二进制日志中;

(2)Slave服务器会在一定时间间隔内对Master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/OThread请求Master二进制事件

(3)同时主节点为每个I/O线程启动一个Dump线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中,从节点将启动SQL线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致,最后I/OThread和SQLThread将进入睡眠状态,等待下一次被唤醒。

Undo log与Redo log原理分析
Undo log原理

Undo log是把所有没有COMMIT的事务回滚到事务开始前的状态,系统崩溃时,可能有些事务还没有COMMIT,在系统恢复时,这些没有COMMIT的事务就需要借助Undo log来进行回滚。

使用Undo log时要求:

代码语言:javascript
复制
 1、记录修改日志时(Redo log),(T,x,v)中v为x修改前的值,这样才能借助这条日志来回滚;
 2、事务提交后,必须在事务的所有修改(包括记录的修改日志)都持久化后才能写COMMIT T日志;这样才能保证,宕机恢复时,已经COMMIT的事务的所有修改都已经持久化,不需要回滚。

使用Undo log时事务执行顺序

代码语言:javascript
复制
 1、记录START T
 2、记录需要修改的记录的旧值(要求持久化)
 3、根据事务的需要更新数据库(要求持久化)
 4、记录COMMIT T

使用Undo log进行宕机回滚

代码语言:javascript
复制
 1、扫描日志,找出所有已经START,还没有COMMIT的事务。
 2、针对所有未COMMIT的日志,根据Redo log来进行回滚。

如果数据库访问很多,日志量也会很大,宕机恢复时,回滚的工作量也就很大,为了加快回滚,可以通过Checkpoint机制来加速回滚。

代码语言:javascript
复制
 从后往前,扫描Undo log
 1、如果先遇到checkpoint_start, 则将checkpoint_start之后的所有未提交的事务进行回滚;
 2、如果先遇到checkpoint_end, 则将前一个checkpoint_start之后所有未提交的事务进行回滚;(在checkpoint的过程中,可能有很多新的事务START或者COMMIT)。

使用Undo log,在写COMMIT日志时,要求Redo log以及事务的所有修改都必须已经持久化,这种做法通常很影响性能。

Redo log原理

Redo log是指在回放日志的时候把已经COMMIT的事务重做一遍,对于没有COMMIT的事务按照abort处理,不进行任何操作。

使用Redo log时,要求:

代码语言:javascript
复制
 1、记录Redo log时,(T,x,v)中的v必须是x修改后的值,否则不能通过Redo log来恢复已经COMMIT的事务。
 2、写COMMIT T日志之前,事务的修改不能进行持久化,否则恢复时,对于未COMMIT的操作,可能有数据已经修改,但重放Redo log不会对该事务做任何处理,从而不能保证事务的原子性。

使用Redo log时事务执行顺序

代码语言:javascript
复制
 1、记录START T
 2、记录事务需要修改记录的新值(要求持久化)
 3、记录COMMIT T(要求持久化)
 4、将事务相关的修改写入数据库

使用Redo log重做事务

代码语言:javascript
复制
 1、扫描日志,找到所有已经COMMIT的事务;
 2、对于已经COMMIT的事务,根据Redo log重做事务;

根据Checkpoint来加速恢复

代码语言:javascript
复制
 从后往前,扫描Redo log
 1,如果先遇到checkpoint_start, 则把T1~Tn以及checkpoint_start之后的所有已经COMMIT的事务进行重做;
 2. 如果先遇到checkpoint_end, 则T1~Tn以及前一个checkpoint_start之后所有已经COMMIT的事务进行重做;

与Undo log类似,在使用时对持久化以及事务操作顺序的要求都比较高,可以将两者结合起来使用,在恢复时,对于已经COMMIT的事务使用Redo log进行重做,对于没有COMMIT的事务,使用Undo log进行回滚。Redo/Undo log结合起来使用时,要求同时记录操作修改前和修改后的值,如(T,x,v,w),v为x修改前的值,w为x修改后的值,具体操作顺序为:

代码语言:javascript
复制
 1. 记录START T
 2. 记录修改日志(T,x,v,w)(要求持久化,其中v用于undo,w用于redo)
 3. 更新数据库
 4. 记录 COMMIT T
实战操作

上一篇已经对于Binlog设置做了一些初步的实践:http://www.cyblogs.com/mysql-binlogshe-zhi/,还是在本地利用Docker的方式启动了2个容器。

代码语言:javascript
复制
 ➜  ~  docker ps -a
 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                     NAMES
 662e8531eb70        centos:7            "/bin/bash"         2 hours ago         Up 2 hours          0.0.0.0:33062->3306/tcp   docker-mysql-slave
 c738746e9623        centos:7            "/bin/bash"         4 hours ago         Up 4 hours          0.0.0.0:33061->3306/tcp   docker-mysql-master

一个是docker-mysql-master作为主节点,docker-mysql-slave作为从节点,最后实现一个主从同步的功能。

Master节点
设置slave_account账户
代码语言:javascript
复制
 [root@c738746e9623 bin]# ./mysql -u root -p
 Enter password:
 Welcome to the MySQL monitor.  Commands end with ; or \g.
 Your MySQL connection id is 2
 Server version: 5.6.45-log MySQL Community Server (GPL)
 mysql> grant replication slave on *.* to 'slave_account'@'%' identified by '123456';
 Query OK, 0 rows affected (0.01 sec)
 mysql> flush privileges;
 Query OK, 0 rows affected (0.01 sec)
Master节点的my.cnf
代码语言:javascript
复制
 [root@c738746e9623 bin]# cat /etc/my.cnf
 [client]
 default-character-set=utf8
 
 [mysql]
 default-character-set=utf8
 
 [mysqld]
 user=mysql
 default-storage-engine=INNODB
 character-set-server=utf8
 basedir = /usr/local/mysql
 datadir = /usr/local/mysql/data
 port = 3306
 socket = /tmp/mysql.sock
 
 server-id = 1
 log-bin=mysql-bin
 
 binlog-ignore-db = mysql
 binlog-ignore-db = information_schema
 
 sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
查看master节点状态
代码语言:javascript
复制
 mysql> show master status;
 +------------------+----------+--------------+--------------------------+-------------------+
 | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB         | Executed_Gtid_Set |
 +------------------+----------+--------------+--------------------------+-------------------+
 | mysql-bin.000002 |      120 |              | mysql,information_schema |                   |
 +------------------+----------+--------------+--------------------------+-------------------+
 1 row in set (0.00 sec)
Slave节点
Slave节点my.cnf
代码语言:javascript
复制
 [root@662e8531eb70 mysql]#cat /etc/my.cnf
 [client]
 default-character-set=utf8
 
 [mysql]
 default-character-set=utf8
 
 [mysqld]
 user=mysql
 default-storage-engine=INNODB
 character-set-server=utf8
 basedir = /usr/local/mysql
 datadir = /usr/local/mysql/data
 port = 3306
 socket = /tmp/mysql.sock
 
 server-id = 2
 
 sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
配置与主节点同步的配置
代码语言:javascript
复制
 mysql> change master to master_host='172.17.0.2',master_user='slave_account',master_password='123456',master_log_file='mysql-bin.000002',master_log_pos=120;
 Query OK, 0 rows affected, 2 warnings (0.06 sec)
启动同步
代码语言:javascript
复制
 mysql> start slave;
 Query OK, 0 rows affected (0.01 sec)
查看一个主从同步的状态
代码语言:javascript
复制
 mysql> show slave status\G;
 *************************** 1. row ***************************
                Slave_IO_State: Waiting for master to send event
                   Master_Host: 172.17.0.2
                   Master_User: slave_account
                   Master_Port: 3306
                 Connect_Retry: 60
               Master_Log_File: mysql-bin.000002
           Read_Master_Log_Pos: 120
                Relay_Log_File: 662e8531eb70-relay-bin.000002
                 Relay_Log_Pos: 283
         Relay_Master_Log_File: mysql-bin.000002
              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: 120
               Relay_Log_Space: 463
               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: 7323857e-254b-11ea-9b62-0242ac110002
              Master_Info_File: /usr/local/mysql/data/master.info
                     SQL_Delay: 0
           SQL_Remaining_Delay: NULL
       Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
            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
 1 row in set (0.00 sec)
Master节点写数据
代码语言:javascript
复制
 mysql> CREATE TABLE `person_01` (
     ->   `id` int(11) DEFAULT NULL,
     ->   `first_name` varchar(20) DEFAULT NULL,
     ->   `age` int(11) DEFAULT NULL,
     ->   `gender` char(1) DEFAULT NULL
     -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8
     -> ;
 Query OK, 0 rows affected (0.03 sec)
 mysql> show tables;
 +----------------+
 | Tables_in_test |
 +----------------+
 | person         |
 | person_01      |
 +----------------+
 2 rows in set (0.01 sec)
 
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (1, 'Bob', 25, 'M');
 Query OK, 1 row affected (0.01 sec)
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (2, 'Jane', 20, 'F');
 Query OK, 1 row affected (0.01 sec)
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (3, 'Jack', 30, 'M');
 Query OK, 1 row affected (0.00 sec)
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (4, 'Bill', 32, 'M');
 Query OK, 1 row affected (0.00 sec)
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (5, 'Nick', 22, 'M');
 Query OK, 1 row affected (0.00 sec)
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (6, 'Kathy', 18, 'F');
 Query OK, 1 row affected (0.01 sec)
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (7, 'Steve', 36, 'M');
 Query OK, 1 row affected (0.00 sec)
 mysql> INSERT INTO test.person_01 (id, first_name, age, gender) VALUES (8, 'Anne', 25, 'F');
 Query OK, 1 row affected (0.00 sec)
 
 mysql> select * from person_01;
 +------+------------+------+--------+
 | id   | first_name | age  | gender |
 +------+------------+------+--------+
 |    1 | Bob        |   25 | M      |
 |    2 | Jane       |   20 | F      |
 |    3 | Jack       |   30 | M      |
 |    4 | Bill       |   32 | M      |
 |    5 | Nick       |   22 | M      |
 |    6 | Kathy      |   18 | F      |
 |    7 | Steve      |   36 | M      |
 |    8 | Anne       |   25 | F      |
 +------+------------+------+--------+
 8 rows in set (0.01 sec)
 mysql> exit
 Bye
 [root@c738746e9623 bin]# 主节点
Slave节点查数据
代码语言:javascript
复制
 mysql> show tables;
 +----------------+
 | Tables_in_test |
 +----------------+
 | person_01      |
 +----------------+
 1 row in set (0.00 sec)
 
 mysql> select * from person_01;
 +------+------------+------+--------+
 | id   | first_name | age  | gender |
 +------+------------+------+--------+
 |    1 | Bob        |   25 | M      |
 |    2 | Jane       |   20 | F      |
 |    3 | Jack       |   30 | M      |
 |    4 | Bill       |   32 | M      |
 |    5 | Nick       |   22 | M      |
 |    6 | Kathy      |   18 | F      |
 |    7 | Steve      |   36 | M      |
 |    8 | Anne       |   25 | F      |
 +------+------------+------+--------+
 8 rows in set (0.00 sec)
 mysql> exit
 Bye
 [root@662e8531eb70 mysql]# 从节点

这样子就做好了最简单的主从同步。主从同步只是最基础的高可用架构。

参考地址
  • https://blog.csdn.net/xuanxuan_good/article/details/54427154
  • https://zhuanlan.zhihu.com/p/96212530
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-31,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 什么是mysql的主从复制?
  • Mysql复制原理
  • Undo log与Redo log原理分析
    • Undo log原理
    • Redo log原理
  • 实战操作
  • Master节点
    • 设置slave_account账户
    • Master节点的my.cnf
    • 查看master节点状态
  • Slave节点
    • Slave节点my.cnf
    • 配置与主节点同步的配置
    • 启动同步
    • 查看一个主从同步的状态
  • Master节点写数据
  • Slave节点查数据
  • 参考地址
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档