datetime:2019/9/5 10:01
author:nzb

MySQL的主从复制

主从复制

  • 两台数据库服务器(Linux服务器), IP分别为192.168.1.110和192.168.1.111,服务器上装上MySQL(版本为5.7)

  • 打开主数据库配置文件:vim /etc/mysql/my.cnf, 加入以下内容后重启MySQL服务

      [mysqld]
      log-bin=mysql-bin
      server-id=1     (默认为1,总之两台服务器要设置为不同的ID)
    
  • 打开从数据库配置文件:vim /etc/mysql/my.cnf, 加入相同内容后将其中的 server-id 设为2(默认为1)然后重启MySQL服务

  • 设192.168.1.110为主数据库, 在数据库中加入一个从服务器可以登录的用户, 语句如下:

      GRANT ALL PRIVILEGES ON *.* TO 'user'@'192.168.1.111' IDENTIFIED BY 'password' WITH GRANT OPTION;(或 GRANT REPLICATION SLAVE ON *.* TO 'username'@'192.168.1.111' IDENTIFIED BY 'password' WITH GRANT OPTION;)
      FLUSH PRIVILEGES;
    
  • 完成后, 在192.168.1.111服务器上执行:mysql -h 192.168.1.110 -u用户名 -p密码, 查看连接是否成功

  • 最后在主数据库中执行以下语句, 查询master状态:show master status; 可以看到以上结果,这儿只需要看 File 和 Position,其它的两个分别是白名单和黑名单,意思为同步哪几个数据库和不同步哪几个数据库,可自行根据需求进行设置。记录了前两个字段后,在从库上执行以下语句:

      CHANGE MASTER TO
      MASTER_HOST='192.168.1.110',
      MASTER_USER='user',
      MASTER_PASSWORD='password',
      MASTER_PORT='3306',
      MASTER_LOG_FILE='mysql-bin.000020',
      MASTER_LOG_POS=1441;
    
  • 执行完成后, 在从数据库上继续执行以下语句:

      start slava;
      show slave status\G;
    
  • 这样,查看从服务器的状态,如果状态中的用红线标出来两个参数的值都为YES,那证明配置已经成功,否则可以检查一下具体问题出现在什么地方。

  • 这样,就算配置完成了。在主库中新建数据库,新建一张表,插几条数据,到从库上查询一下看是否已经同步过来。

  • PS:如果失败,可以从以下几个方面去排查问题:  

    • 1.首先试一下主从服务器相互之间是否 PING 得通

    • 2.试一下远程连接是否正确,如果连不上,则有可能是网卡不一致、防火墙没有放行 3306 端口

    • 3.server-id 是否配成一致

    • 4.bin-log 的信息是否正确

主主复制

  • 上面说了主从复制的配置方法,现在接着上面的配置继续,然后实现双主复制,让以上的两个服务器互为主从。

  • 在主服务器上配置 /etc/mysql/my.cnf 文件,配置如下,配置之后重启MYSQL服务:

      auto_increment_increment=2         #步进值auto_imcrement。一般有n台主MySQL就填n
      auto_increment_offset=1            #起始值。一般填第n台主MySQL。此时为第一台主MySQL
      binlog-ignore=mysql                #忽略mysql库【一般都不写】
      binlog-ignore=information_schema   #忽略information_schema库【一般都不写】
    
  • 在从服务器上配置 /etc/mysql/my.cnf 文件,配置如下, 配置之后重启MYSQL服务:

      auto_increment_increment=2   #步进值auto_imcrement。一般有n台主MySQL就填n
      auto_increment_offset=2   #起始值。一般填第n台主MySQL。此时为第二台主MySQL
      binlog-ignore=mysql   #忽略mysql库【一般都不写】
      binlog-ignore=information_schema   #忽略information_schema库【一般都不写】
    
  • 在从服务器上添加一个主服务器可以访问的用户,命令如下:

      GRANT REPLICATION SLAVE ON *.* TO 'user'@'192.168.1.110' IDENTIFIED BY 'password' with grant option ;
      FLUSH PRIVILEGES
    
  • 建好之后在192.168.1.110服务器上访问从数据库, 测试是否成功,如果可以连上,则进行下一步,连不上的话,参考上面进行问题排查。

  • 因为要互为主从,所以现在从服务器也是master ,所以也要查看一下状态:show master status;

  • 查到相应的信息后,在原来的主服务器上执行以下命令(因为现在它现在也是另一台的从服务器)

      CHANGE MASTER TO
      MASTER_HOST='192.168.1.111',
      MASTER_USER='user',
      MASTER_PASSWORD='password',
      MASTER_LOG_FILE='mysql-bin.000021',
      MASTER_LOG_POS=1457;
    
  • 执行完毕后,在原主库上继续执行如下语句:

      start slave;
      show slave status\G;
    
  • 同上,如果出现如下画面,则证明配置成功。

  • 在两台服务器的MYSQL中分别进行一些建库、建表、插入、更新等操作,看一下另一台会不会进行同步,如果可以则证明主主配置成功,否则还是上面的排错方法,进行错误排查。

双主多从

  • 现在已经是双主配置了,但是如果要进行读写分离,那么我们要再增加N台从库,如何做呢?非常简单,按如下操作即可:

  • 新增加一台数据库服务器,192.168.1.112,数据库配置均与前两台相同

  • 确定一下要将哪一台当作自己的主服务器,我们姑且设 192.168.1.110 为主服务器

  • 在第三台服务器中编辑 /etc/mysql/my.cnf ,将其 server-id 设为 3(保证与前两个不一样即可),然后重启MYSQL服务

  • 在主服务器中,增加一条用户记录,用于当前服务器对主库对的连接,代码如下:

      GRANT REPLICATION SLAVE ON *.* TO 'user'@'192.168.1.112' IDENTIFIED BY 'password';
      FLUSH PRIVILEGES;
    
  • 在 192.168.216.130 服务器上测试是否可以连接到主库: mysql -h 192.168.1.110 -u用户名 -p密码, 如果可以连上,则可以进行下一步,否则根据上面的提示排查问题。

  • 在 192.168.216.130 服务器上查询 master 当前状态: show master status;

  • 看到相关信息后,我们执行如下操作:

      CHANGE MASTER TO
      MASTER_HOST='192.168.1.110',
      MASTER_USER='user',
      MASTER_PASSWORD='password',
      MASTER_LOG_FILE='mysql-bin.000020',
      MASTER_LOG_POS=1441;
    
  • 执行完毕后,在原主库上继续执行如下语句:

      start slave;
      show slave status\G;
    
  • 同上,如果出现如下画面,则证明配置成功。如果此处有问题,参考上面所提排查并解决问题。

  • 此时我们在 192.168.1.110 上建库、建表、插入、更新、删除数据,在 另外两台上分别进行查看,发现均已经同步。 但是如果我们在 192.168.1.111 上做相应的操作,则发现只有 192.168.1.110 上进行了相应的同步,而 192.168.1.112 上的数据并未同步。 这是为什么呢?因为我们设置的主库是 192.168.1.110,所以在 192.168.1.111 进行数据操作的时候并未同步,这显然不符合我们的需求, 那么,我们要怎么修改呢?非常简单,在互为主从的两台服务器的配置文件中均加入以下语句:log-slave-updates=on

  • 加上后将两台服务器的MYSQL重启,然后再进行测试,发现数据已经可以同步了。如果要再多加一些从服务器,和以上类似,现在我们做的是双主一从,我们可以再加N台从服务器,配置也是一样的。

  • 至此,MYSQL主从复制、主主复制、双主多从配置我们均已经搞定!

Docker配置MySQL主从复制

下面还是基于Docker来演示如何配置MySQL主从复制。我们事先准备好MySQL的配置文件以及保存MySQL数据和运行日志的目录,然后通过Docker的数据卷映射来指定容器的配置、数据和日志文件的位置。

root
└── mysql
    ├── master
    │   ├── conf
    |    └── data
    └── slave-1
    |    ├── conf
    |    └── data
    └── slave-2
    |    ├── conf
    |    └── data
    └── slave-3
        ├── conf
        └── data
  1. MySQL的配置文件(master和slave的配置文件需要不同的server-id)。

    [mysqld]
    pid-file=/var/run/mysqld/mysqld.pid
    socket=/var/run/mysqld/mysqld.sock
    datadir=/var/lib/mysql
    log-error=/var/log/mysql/error.log
    server-id=1
    log-bin=/var/log/mysql/mysql-bin.log
    expire_logs_days=30
    max_binlog_size=256M
    symbolic-links=0
    # slow_query_log=ON
    # slow_query_log_file=/var/log/mysql/slow.log
    # long_query_time=1
    
  2. 创建和配置master。

    docker run -d -p 3306:3306 --name mysql-master \
    -v /root/mysql/master/conf:/etc/mysql/mysql.conf.d \
    -v /root/mysql/master/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
    
    docker exec -it mysql-master /bin/bash
    
    mysql -u root -p
    Enter password:
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 1
    Server version: 5.7.23-log MySQL Community Server (GPL)
    Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    
    mysql> grant replication slave on *.* to 'slave'@'%' identified by 'iamslave';
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    
    mysql> flush privileges;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> show master status;
    +------------------+----------+--------------+------------------+-------------------+
    | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +------------------+----------+--------------+------------------+-------------------+
    | mysql-bin.000003 |      590 |              |                  |                   |
    +------------------+----------+--------------+------------------+-------------------+
    1 row in set (0.00 sec)
    
    mysql> quit
    Bye
    exit
    

    上面创建Docker容器时使用的-v参数(--volume)表示映射数据卷,冒号前是宿主机的目录,冒号后是容器中的目录,这样相当于将宿主机中的目录挂载到了容器中。

  3. 创建和配置slave。

    docker run -d -p 3308:3306 --name mysql-slave-1 \
    -v /root/mysql/slave-1/conf:/etc/mysql/mysql.conf.d \
    -v /root/mysql/slave-1/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    --link mysql-master:mysql-master mysql:5.7
    
    docker run -d -p 3309:3306 --name mysql-slave-2 \
    -v /root/mysql/slave-2/conf:/etc/mysql/mysql.conf.d \
    -v /root/mysql/slave-2/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    --link mysql-master:mysql-master mysql:5.7
    
    docker run -d -p 3310:3306 --name mysql-slave-3 \
    -v /root/mysql/slave-3/conf:/etc/mysql/mysql.conf.d \
    -v /root/mysql/slave-3/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    --link mysql-master:mysql-master mysql:5.7
    
    docker exec -it mysql-slave-1 /bin/bash
    
    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.7.23-log MySQL Community Server (GPL)
    Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    
    mysql> reset slave;
    Query OK, 0 rows affected (0.02 sec)
    
    mysql> change master to master_host='mysql-master', master_user='slave', master_password='iamslave', master_log_file='mysql-bin.000003', master_log_pos=590;
    Query OK, 0 rows affected, 2 warnings (0.03 sec)
    
    mysql> start slave;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> show slave status\G
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: mysql57
                      Master_User: slave
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000001
              Read_Master_Log_Pos: 590
                   Relay_Log_File: f352f05eb9d0-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: 590
                  Relay_Log_Space: 534
                  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: 30c38043-ada1-11e8-8fa1-0242ac110002
                 Master_Info_File: /var/lib/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)
    
    mysql> quit
    Bye
    exit
    

    接下来可以如法炮制配置出slave2和slave3,这样就可以搭建起一个“一主带三从”的主从复制环境。上面创建创建容器时使用的--link参数用来配置容器在网络上的主机名(网络地址别名)。

配置好主从复制后,写数据的操作应该master上执行,而读数据的操作应该在slave上完成。为此,在Django项目中需要配置DATABASE_ROUTERS并通过自定义的主从复制路由类来实现读写分离操作,如下所示:

DATABASE_ROUTERS = [
    # 此处省略其他配置
    'common.routers.MasterSlaveRouter',
]
class MasterSlaveRouter(object):
    """主从复制路由"""

    @staticmethod
    def db_for_read(model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        return random.choice(('slave1', 'slave2', 'slave3'))

    @staticmethod
    def db_for_write(model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        return 'default'

    @staticmethod
    def allow_relation(obj1, obj2, **hints):
        """
        Allow relations if a model in the auth app is involved.
        """
        return None

    @staticmethod
    def allow_migrate(db, app_label, model_name=None, **hints):
        """
        Make sure the auth app only appears in the 'auth_db'
        database.
        """
        return True

上面的内容参考了Django官方文档的DATABASE_ROUTERS配置,对代码进行了适当的调整。

results matching ""

    No results matching ""