1. 程式人生 > 實用技巧 >MySQL基礎 - 主從複製

MySQL基礎 - 主從複製

概要

主要探索一下幾個問題:

  • MySQL 主從複製 (MySQL Replication) 是什麼?
  • MySQL主從架構
  • 用docker搭建一個簡單的主從庫
  • 讀寫分離
  • 主從延時分析

主從複製原理

Mysql主從基本原理,主要形式以及主從同步延遲原理 (讀寫分離)導致主庫從庫資料不一致問題的及解決方案

深度探索MySQL主從複製原理

MySQL 主從複製是指資料可以從一個MySQL資料庫伺服器主節點複製到一個或多個從節點。在Master與Slave之間的實現整個複製過程主要由三個執行緒來完成,其中兩個執行緒(SQL執行緒和I/O執行緒)在Slave端,另外一個執行緒(I/O執行緒)在Master端。

從上面架構圖看,可以知道主從複製的大概步驟:

  1. 主庫master開啟binary log,將資料更新操作記錄到binlog中;
  2. 從庫slave啟動一個I/O thread 連線並監聽master的binlog,以的方式讀取;如果沒有它會睡眠等待Master產生新的日誌事件。
  3. 如果從庫監聽到主庫有新的日誌事件(Log Events),則會將其拷貝至Slave伺服器中的中繼日誌(Relay Log)
  4. 從庫Slave重做中繼日誌(Relay Log)中的事件,將Master上的改變反映到它自己的資料庫中。

MySQL 主從形式

From 深度探索MySQL主從複製原理

一主一從

一主多從

一主一從和一主多從是最常見的主從架構,實施起來簡單並且有效,不僅可以實現HA,而且還能讀寫分離,進而提升叢集的併發能力。

多主一從

多主一從(從5.7開始支援)可以將多個mysql資料庫備份到一臺儲存效能比較好的伺服器上。

雙主複製

雙主複製,也就是互做主從複製,每個master既是master,又是另外一臺伺服器的slave。這樣任何一方所做的變更,都會通過複製應用到另外一方的資料庫中。

級聯複製

級聯複製模式下,部分slave的資料同步不連線主節點,而是連線從節點。因為如果主節點有太多的從節點,就會損耗一部分效能用於replication,那麼我們可以讓3~5個從節點連線主節點,其它從節點作為二級或者三級與從節點連線,這樣不僅可以緩解主節點的壓力,並且對資料一致性沒有負面影響。

主要用途

  • 讀寫分離
  • 資料實時備份
  • 高可用HA(High Available),實時災備,用於故障切換。
  • 架構擴充套件

隨著系統中業務訪問量的增大,如果是單機部署資料庫,就會導致I/O訪問頻率過高。有了主從複製,增加多個數據儲存節點,將負載分佈在多個從節點上,降低單機磁碟I/O訪問的頻率,提高單個機器的I/O效能。資料實時備份,當系統中某個節點發生故障時,可以方便的故障切換。

搭建一主一從

docker基本操作

Docker 安裝 MySQL

要想檢視映象的版本號TAG,可以在 docker hub 檢視,進入之後,在頁面左上角搜尋框搜尋。

進入dockerhub 的mysql介紹頁面,裡面有介紹docker啟動mysql的基本操作。

  • 拉取映象:選擇了8.0版本。

    $ docker pull mysql:8.0
    
  • 檢視映象

    $ docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    mysql               8.0                 db2b37ec6181        12 days ago         545MB
    
  • 啟動映象

    $ docker run -itd --name mysql -p 53306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
    

    引數說明:

    • -p 53306:3306 :對映容器服務的 53306 埠到宿主機的 3306 埠,外部主機可以直接通過 宿主機ip:53306 訪問到 MySQL 的服務。
    • MYSQL_ROOT_PASSWORD=123456:設定 MySQL 服務 root 使用者的密碼。
    • mysql:8.0:映象版本
  • 檢視容器程序

    $ docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                NAMES
    961e398feca0        mysql:8.0           "docker-entrypoint.s…"   3 minutes ago       Up 3 minutes        33060/tcp, 0.0.0.0:53306->3306/tcp   mysql
    
  • 連線mysql

    $ mysql -h localhost -P 53306 -u root -p
    
  • 進入容器

    可以進入mysql-master容器中,檢視或修改一些配置

    $ docker exec -it mysql-master bash
    

建立主從庫

https://www.jianshu.com/p/0439206e1f28

建立主庫

  • 建立一個master目錄,然後建立my.cnf 和 dockerfile檔案。

  • 建立 my.cnf 檔案:

    [mysqld]
    #[必須]伺服器唯一ID,預設是1,一般取IP最後一段,這裡看情況分配
    server_id = 1
    #[必須]啟用二進位制日誌
    log-bin = mysql-bin
    # 使用mysql_native_password加密規則
    default_authentication_plugin = mysql_native_password
    
  • 建立 Dockerfile 檔案:

    FROM mysql:8.0
    COPY my.cnf /etc/mysql/
    RUN mkdir /var/lib/mysql-files
    EXPOSE 3306
    CMD ["mysqld"]
    
  • 在當前master目錄下構建映象:

    #構建映象
    $ docker build -t master/mysql .
    
  • 啟動映象

    $ docker run -itd --name mysql-master -p 33306:3306 -e MYSQL_ROOT_PASSWORD=123456 master/mysql
    
  • 主庫授權

    本地連線主庫:

    $ mysql -u root -P 33306 -p
    

    授權:

    # 建立角色
    # create user ‘username’@‘host’ identified by ‘password’;
    mysql> CREATE USER 'slave'@'%' IDENTIFIED BY 'slv123456';
    # 授權
    mysql> GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' WITH GRANT OPTION;
    #重新整理許可權
    mysql> flush privileges;
    
  • 檢視主容器資料庫狀態

    mysql> show master status;
    +---------------+----------+--------------+------------------+-------------------+
    | File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +---------------+----------+--------------+------------------+-------------------+
    | binlog.000002 |     1178 |              |                  |                   |
    +---------------+----------+--------------+------------------+-------------------+
    1 row in set (0.00 sec)
    

    主庫的 File 和 Position 引數在配置從庫時候會用到。 binlog.000002 是binlog檔名。

建立從庫

  • 建立一個slave目錄,然後建立my.cnf 和 dockerfile 檔案。

  • 建立 my.cnf 檔案:

    [mysqld]
    #[必須]伺服器唯一ID,預設是1,一般取IP最後一段,這裡看情況分配
    server_id = 2
    #[必須]啟用二進位制日誌
    log-bin = mysql-bin
    # 使用mysql_native_password加密規則
    default_authentication_plugin = mysql_native_password
    
  • 建立 Dockerfile 檔案:

    FROM mysql:8.0
    COPY my.cnf /etc/mysql/
    RUN mkdir /var/lib/mysql-files
    EXPOSE 3306
    CMD ["mysqld"]
    
  • 在slave目錄下構建映象:

    #構建映象
    $ docker build -t master/mysql .
    
  • 啟動容器

    $ docker run -itd --name mysql-slave -p 43306:3306 -e MYSQL_ROOT_PASSWORD=123456 slave/mysql
    

    如果是本地起的兩個容器,需要使用--link 主庫容器名:容器別名 命令,讓從庫容器訪問到主庫容器。

    $ docker run -itd --name mysql-slave --link mysql-master:mysql-master -p 43306:3306 -e MYSQL_ROOT_PASSWORD=123456 slave/mysql
    
  • 配置maser節點

    連線mysql-slave

    $ mysql -u root -P 43306 -p
    

    配置master節點

    mysql> change master to
        -> master_host='mysql-master',
        -> master_user='slave',
        -> master_log_file='mysql-bin.000002',
        -> master_log_pos=1178,
        -> master_port=3306,
        -> master_password='slv123456';
    Query OK, 0 rows affected, 1 warning (0.04 sec)
    
    mysql> start slave;
    

    master_host=’x.x.x.x’ // 這裡填 master 主機 ip,如果master是本地的容器,使用了--link命令後,可以用別名連線

    master_log_file=’mysql-bin.000002’, // 這裡填寫主庫的 File 的值

    master_log_pos=1178, // 這裡填寫主庫的 Position 的值。

    mysql> start slave; // 啟動從伺服器複製功能

    如果不小心配置錯, 輸入 mysql> stop slave; 然後重新錄入一遍

  • 檢視從庫連線情況

    mysql> show slave status \G
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: mysql-master
                      Master_User: slave
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000003
              Read_Master_Log_Pos: 838
                   Relay_Log_File: 219ff327d373-relay-bin.000002
                    Relay_Log_Pos: 324
            Relay_Master_Log_File: mysql-bin.000003
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
    

問題

本地容器網路互訪

因為兩個容器的網路是獨立的,所以在容器中localhost只能訪問到本容器中的。為了從庫容器能訪問到主庫容器,可以在啟動從庫的容器時,使用--link命令。

$ docker run -itd --name mysql-slave --link mysql-master:mysql-master -p 43306:3306 -e MYSQL_ROOT_PASSWORD=123456 slave/mysql

進入容器,可以ping下

$ docker exec -it mysql-slave bash

root@920659abcaec:/# ping mysql-master
PING mysql-master (172.17.0.2) 56(84) bytes of data.
64 bytes from mysql-master (172.17.0.2): icmp_seq=1 ttl=64 time=0.076 ms
64 bytes from mysql-master (172.17.0.2): icmp_seq=2 ttl=64 time=0.238 ms
64 bytes from mysql-master (172.17.0.2): icmp_seq=3 ttl=64 time=0.130 ms

msyql 8 認證方式

報 caching_sha2_password 認證錯誤:

2020-11-04T10:01:00.787811Z 9 [ERROR] [MY-010584] [Repl] Slave I/O for channel '': error connecting to master 'slave@mysql-master:3306' - retry-time: 60 retries: 4 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. Error_code: MY-002061

2020-11-04T10:02:00.788882Z 9 [ERROR] [MY-010584] [Repl] Slave I/O for channel '': error connecting to master 'slave@mysql-master:3306' - retry-time: 60 retries: 5 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. Error_code: MY-002061

2020-11-04T10:03:00.789919Z 9 [ERROR] [MY-010584] [Repl] Slave I/O for channel '': error connecting to master 'slave@mysql-master:3306' - retry-time: 60 retries: 6 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. Error_code: MY-002061

因為我們這裡測試使用的是MySQL 8.0,在mysql8 之前的版本中加密規則是mysql_native_password,而在mysql8之後,加密規則是caching_sha2_password, 解決問題方法有兩種,一種是升級navicat驅動,一種是把mysql使用者登入密碼加密規則還原成mysql_native_password. 我們這裡使用舊的加密規則,在主庫修改:

mysql> alter user 'slave'@'%' identified by 'slv123456' password expire never;
Query OK, 0 rows affected (0.01 sec)

mysql> alter user 'slave'@'%' identified with mysql_native_password by 'slv123456';
Query OK, 0 rows affected (0.01 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

或者,在主庫的my.cnf中新增下面命令後重啟:

default_authentication_plugin=mysql_native_password

再檢視從庫的狀態:

mysql> show slave status \G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: mysql-master
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 838
               Relay_Log_File: 219ff327d373-relay-bin.000002
                Relay_Log_Pos: 324
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

讀寫分離

28 | 讀寫分離有哪些坑?

MySQL 技術內幕:主從同步和主從延時

https://www.cnblogs.com/a-phper/p/10313967.html

讀寫分離一般分兩種方式:直連方式代理方式

  • 直連方式:客戶端(client)主動做負載均衡,這種模式下一般會把資料庫的連線資訊放在客戶端的連線層。

  • 代理方式:在MySQL和客戶端之間有一箇中間代理層proxy,客戶端只連線proxy, 由proxy根據請求型別和上下文決定請求的分發路由。常見的MySQL中介軟體有:mysql-proxy(官方)、MyCat、atlas、Sharding-JDBC......

兩種方案的有確定:

  • 直連方式:效能較好,因為直連資料庫。但是會增加客戶端資料庫讀寫程式碼複雜性和程式碼量,以及出現主備切換、庫遷移等操作的時候,可能需要客戶端調整資料庫連線資訊、重啟。
  • 代理方式:對客戶端比較友好。客戶端不需要關注後端細節,連線維護、後端資訊維護等工作,都是由proxy完成的。但引入了proxy架構,效能較直連方式差,同時需要單獨對proxy服務/叢集進行維護。

但是,不論使用哪種架構,都存在主從延遲問題。

延遲分析

延遲的原因一般有:

  • 主從伺服器處於不同的網路之中,由於網路延遲導致;
  • 主從伺服器的硬體配置不同,從伺服器的硬體配置(包括記憶體,CPU,網絡卡等)遠低於主伺服器;
  • 主庫上有大量的寫入操作,導致從庫無法實時重放主庫上的binlog;
  • 主庫上存在著大事務操作或者慢SQL,導致從庫在應用主庫binlog的過程過慢,形成延遲;
  • 資料庫例項的引數配置問題導致,如:從庫開啟了binlog,或者配置了每次事務都去做刷盤操作;

分析過程

分析binlog檔案大小、時間戳

  • 連線master,檢視master binlog位置
mysql> show variables like '%log_bin%';
+---------------------------------+--------------------------------+
| Variable_name                   | Value                          |
+---------------------------------+--------------------------------+
| log_bin                         | ON                             |
| log_bin_basename                | /var/lib/mysql/mysql-bin       |
| log_bin_index                   | /var/lib/mysql/mysql-bin.index |
| log_bin_trust_function_creators | OFF                            |
| log_bin_use_v1_row_events       | OFF                            |
| sql_log_bin                     | ON                             |
+---------------------------------+--------------------------------+
  • 檢視master binlog 檔案列表

    root@641a6f1ade6a:/var/lib/mysql# cd /var/lib/mysql/
    root@641a6f1ade6a:/var/lib/mysql# 
    root@641a6f1ade6a:/var/lib/mysql# ls -l | grep mysql-bin
    -rw-r----- 1 mysql mysql      179 Nov  4 10:45 mysql-bin.000001
    -rw-r----- 1 mysql mysql  3104223 Nov  4 10:46 mysql-bin.000002
    -rw-r----- 1 mysql mysql     1029 Nov  5 03:45 mysql-bin.000003
    -rw-r----- 1 mysql mysql      397 Nov  5 03:47 mysql-bin.000004
    -rw-r----- 1 mysql mysql       76 Nov  5 03:45 mysql-bin.index
    

    如果master的binlog檔案突然變得很大,比如平常的是1G,最近的binlog檔案飆升到10G,那麼要排查業務是否有大量的操作,如批量插入等業務。

  • 連線slave,檢視slave 的狀態,檢視當前處理master的哪個binlog檔案,檢視引數:Relay_Master_Log_File如果當前slave消費的binlog檔案和master的最新binlog檔案不是同一個,那說明從庫消費慢導致延誤。

    mysql> show slave status \G
    

總結:

  1. 如果master的binlog檔案突然變得很大,比如平常的是1G,最近的binlog檔案飆升到10G,那麼要排查業務是否有大量的操作,如批量插入等業務。
  2. 如果當前slave消費的binlog檔案落後於master的最新binlog檔案,那說明從庫消費慢。

分析 slave 執行情況

一般因為機器效能、從庫引數配置從而導致從庫延遲情況,可以檢視從庫的執行,從Read_Master_Log_PosExec_Master_Log_PosSeconds_Behind_Master引數中看到延遲情況

mysql> show slave status \G

Seconds_Behind_Master引數,這個引數表示的是從庫上的IO執行緒和SQL執行緒相差的時間,然後根據該引數值判斷,這個值只是初步判斷,不能由這個值來下結論,有如下幾種情況:

  • 0:表示無延遲,理想狀態;
  • NULL:表示從庫上的IO執行緒和SQL執行緒中,有某一個執行緒出現問題,可以再次檢視Slave_IO_Running和Slave_SQL_Running的值是否都為Yes;
  • 大於0:表示主從已經出現延遲,這個值越大,表示從庫和主庫之間的延遲越嚴重;

總結:

從從庫的slave status 中的Read_Master_Log_PosExec_Master_Log_PosSeconds_Behind_Master引數,可以分析從庫的處理情況。如果處理慢,有可能是:

  1. binlog檔案過大,導致消費不過來
  2. 從庫有慢查詢sql、鎖、事務等耗時操作導致從庫壓力大
  3. 機器效能差,檢視I/O情況
  4. 從庫配置不合適

優化方案

  1. 優化表結構:
    • 建立/優化索引
    • 拆分大表
  2. 業務優化:
    • 優化慢查詢、減少事務
    • 避免在業務繁忙期執行批量操作
    • 根據業務的實時性要求,改查主從庫;實時性要求高的直接查主庫。
  3. 機器效能優化:選擇高效能機器,堆疊CPU、固態硬碟、記憶體
  4. 調整mysql叢集架構和引數

資料