擺攤也要抽時間學的MySQL主從複製

前言

聽說大家現在都在積極響應國家號召,沒事的時候會推着三輪車去街角、天橋擺個地攤。博主剛剛收攤回來,順手寫了篇MySQL。

MySQL相信大家都很熟悉了,單節點MySQL出現性能瓶頸的時候,大家首先想到的是優化SQL。但是單節點畢竟能力有限,所以在優化之後,還是無法滿足性能要求時,就會想到部署MySQL讀寫分離,也就是主從複製。除了性能瓶頸之外,還有單節點故障、單節點容量等問題,都必須依靠集羣才能解決。本篇主要講解MySQL從單機到集羣的原理和實踐。

環境

  • VMware Workstation 15
  • CentOS Linux release 7.7.1908
  • MySQL 5.7.30

注意事項

  • 三個節點ip分別爲192.168.1.101192.168.1.102192.168.1.103
  • 確保三個節點都能訪問互聯網,並且三個節點能夠相互通信
  • 確保Linux的yumwget等基礎命令可用
  • 建議先關閉防火牆,Centos 7操作如下
    firewall-cmd --state ## 查看防火牆狀態 not running表示已經關閉
    systemctl stop firewalld.service ## 關閉防火牆
    systemctl disable firewalld.service ## 禁止開機啓動防火牆
    

需要搭建的主從複製集羣如下
MySQL主從複製

單機安裝

安裝

整個過程請保持網絡暢通

wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql-community-server

到這一步,MySQL服務的安裝已經完成

啓動

啓動MySQL

systemctl start  mysqld.service

查看運行狀態

systemctl status mysqld.service

執行結果

● mysqld.service - MySQL Server
   Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
   Active: active (running) since 二 2020-06-02 21:58:25 CST; 11s ago
     Docs: man:mysqld(8)
           http://dev.mysql.com/doc/refman/en/using-systemd.html
  Process: 2160 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS (code=exited, status=0/SUCCESS)
  Process: 2111 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
 Main PID: 2163 (mysqld)
   CGroup: /system.slice/mysqld.service
           └─2163 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid

6月 02 21:58:18 localhost.localdomain systemd[1]: Starting MySQL Server...
6月 02 21:58:25 localhost.localdomain systemd[1]: Started MySQL Server.

根據日誌可以看到MySQL Server已經成功啓動

找到臨時密碼

在MySQL的啓動日誌中,會打印一個臨時密碼用於登錄,用戶名是root

cat /var/log/mysqld.log |grep password

執行結果

2020-06-02T13:58:21.946584Z 1 [Note] A temporary password is generated for root@localhost: cScTwtyMl4;*

可以看到我此處的臨時密碼是cScTwtyMl4;*
用這個密碼登錄,登錄之後需要修改密碼,才能進行其他的操作,且密碼要滿足一定的複雜度

alter user 'root'@'localhost' identified by 'your password';

因爲安裝了Yum Repository,以後每次yum操作都會自動更新,需要把這個卸載掉

yum -y remove mysql57-community-release-el7-10.noarch

至此,此時MySQL的單機安裝已經完成。其餘節點均可按照此方法安裝。

異步複製

原理

不僅是MySQL,幾乎所有的主從複製集羣,都是master\slave模式,也就是slave節點從master節點上同步數據。MySQL主從複製的整個過程如下圖所示
MySQL主從複製
整個過程可以分爲三個步驟

  • master節點把對數據的修改記錄到bin log中,所以master節點必須開啓bin log
  • slave節點會從指定的位置(logfile和偏移量pos)開始讀取masterbin log,把讀取到的日誌寫入自己的relay log
  • slave節點根據relay log中的日誌進行重放(replay),slave節點可以配置是否需要寫入自己的bin log

瞭解了MySQL主從複製的基本原理,再來進行主從複製的搭建,就會容易理解很多。

異步複製缺點

爲了瞭解異步複製的缺點,先看如下圖
MySQL異步複製過程
如圖所示,描述了一主兩從的異步複製集羣,相信大家也很容易看出缺點。假設master節點commit之後就宕機了,而此時slave節點可能還沒有讀到master的全部bin log,就會導致數據丟失

異步複製不僅不能保證masterslave之間的數據一致性,甚至不能保證slaveslave之間的數據一致性。

半同步複製

MySQL5.6對異步複製做了改進,引入半同步複製

半同步複製過程如下
半同步複製
半同步複製在MySQL5.6中被引入,相比於異步複製,主要的改進就是在master寫完bin log之後不會直接commit,而是收到slave節點的ACK之後纔會commit,期間mastercommit操作被阻塞。當然,爲了防止部分slave節點故障導致master遲遲收到不ACKmastercommit操作可以設置超時時間,超時之後,半同步複製降級爲異步複製

有經驗的同學應該可以看出來,這其實就是兩階段提交master節點commit的時候,slave已經讀取了master的完整bin log。即使此時master宕機,slave節點也能通過重放,實現和master節點的數據同步。

基於GTID複製

前文說講的主從複製集羣是依靠logfile + pos的方式實現,除了這種方式外,還有一種就是基於GTID的主從複製。GTID (Global Transaction ID)是全局事務ID

GTID的結構如下:source_id:transaction_id,組成分成兩個部分source_idtransaction_id,分別代表執行事務的主機UUID,和事務ID。事務ID是遞增的,保證不重複。

  • master節點修改數據時,把GTID寫入bin log
  • slave讀取master節點的bin log,寫入到自己的relay log
  • slave節點的sql_threadrelay log獲取GTID,查找自己的bin log中是否有對應的記錄
  • 如果有,說明該GTID的事務已經在slave上執行,slave會忽略該事務
  • 如果沒有,slave就會從relay log中執行該事務,並記錄到bin log

整個過程與logfile + pos方式幾乎一致,只是獲取執行偏移的方式不同。並且slave節點必須開啓bin log

基於GTID的複製和基於日誌點的複製有什麼區別?

  • 基於日誌點的複製是MySQL實現的第一種複製方式,幾乎所有的MySQL分支版本都支持
  • 基於日誌點的複製,slave請求master的增量日誌依賴於日誌偏移量
  • 基於日誌點的複製配置複製鏈路時,需要指定master_log_filemaster_log_pos參數,一旦master宕機,很難從新的master中找到正確的偏移量的值
基於日誌點的複製 基於GTID的複製
兼容性好 老版本MySQLMariaDB不兼容
支持MMMMHA架構 僅支持MHA架構
主從切換後很難找到新的同步點 基於事務ID的複製,可以很方便的找到未完成的同步的事務ID
可以方便的跳過複製錯誤 只能置入空事務的方式跳過複製

優先選擇基於GTID的複製,不能選擇GTID的複製方式時,再使用基於日誌點的複製。

實踐

異步複製

  • 開啓bin log,使用如下命令查看bin log是否開啓

    show variables like '%log_bin%';
    +---------------------------------+-------+
    | Variable_name                   | Value |
    +---------------------------------+-------+
    | log_bin                         | OFF   |
    | log_bin_basename                |       |
    | log_bin_index                   |       |
    | log_bin_trust_function_creators | OFF   |
    | log_bin_use_v1_row_events       | OFF   |
    | sql_log_bin                     | ON    |
    +---------------------------------+-------+
    6 rows in set (0.00 sec)
    

    log_binOFF表示未開啓,可以使用如下方式開啓bin log

    vim /etc/my.cnf
    

    配置如下內容

    ## 開啓bin log
    log-bin=/var/lib/mysql/mysql-bin
    
    ## 服務節點ID,每個節點不一樣
    server-id=101
    
    ## 開啓GTID複製模式(如果只想用基於日誌點複製,不需要配置這裏)
    gtid-mode=on
    enforce-gtid-consistency=1
    

    重啓MySQL之後,再次查看bin log開啓狀態,可以看到bin log已經開啓

    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                             |
    +---------------------------------+--------------------------------+
    6 rows in set (0.00 sec)
    

    slave節點均按照此方式配置

  • master創建複製賬號並授權
    需要自己指定密碼

    create user repl@'192.168.1.%' identified by 'your password';
    grant replication slave on *.* to repl@'192.168.1.%';
    
  • 備份master數據

    mysqldump --single-transaction -uroot -p --routines --triggers --events --master-data=2 --all-databases > sicimike.sql
    

    可以查看備份的文件中,有GTIDlogfile:pos相關的信息
    mysql主從複製
    將備份的文件導入到slave節點的數據庫。

  • slave節點上配置複製鏈路

    ## MASTER_LOG_FILE和MASTER_LOG_POS的值在前一步中可以找到
    change master to master_host='192.168.1.101', MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=2615;
    

    執行成功後,可以查看slave節點的狀態

    show slave status\G;
    *************************** 1. row ***************************
                   Slave_IO_State:
                      Master_Host: 192.168.1.101
                      Master_User:
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000002
              Read_Master_Log_Pos: 2615
                   Relay_Log_File: localhost-relay-bin.000001
                    Relay_Log_Pos: 4
            Relay_Master_Log_File: mysql-bin.000002
                 Slave_IO_Running: No
                Slave_SQL_Running: No
    			......
    
  • 啓動複製鏈路

    ## 用戶名和密碼就是在master上配置的用戶名和密碼
    start slave user='repl' password= 'your password';
    

    再次查看slave節點的狀態

    show slave status\G;
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 192.168.1.101
                      Master_User: repl
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000002
              Read_Master_Log_Pos: 2615
                   Relay_Log_File: localhost-relay-bin.000002
                    Relay_Log_Pos: 320
            Relay_Master_Log_File: mysql-bin.000002
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
    			......
    

    可以看到Slave_IO_RunningSlave_SQL_Running都變成了Yes狀態,這兩個線程就是前文主從複製原理圖io_threadsql_thread

    其餘的節點也是以同樣的方式配置成slave節點。

至此,基於logfile:pos模式的異步複製已經配置完成。在master節點上進行數據的修改,馬上就會同步到slave庫

可以在master節點上執行show slave hosts查看追隨自己的所有slave節點

show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID                           |
+-----------+------+------+-----------+--------------------------------------+
|       103 |      | 3306 |       101 | d6532e2a-a592-11ea-99c3-000c297f5b55 |
|       102 |      | 3306 |       101 | 1dbd5375-a4d9-11ea-9eef-000c29cf4cca |
+-----------+------+------+-----------+--------------------------------------+
2 rows in set (0.00 sec)

接下來開始配置半同步複製

半同步複製

  • 首先查看master是否安裝了半同步複製插件rpl_semi_sync_master,可以使用如下命令查看
    show plugins;
    
    如果沒有安裝的話,使用如下命令安裝
    install plugin rpl_semi_sync_master soname 'semisync_master.so';
    
  • 配置master相關變量
    首先查看半同步複製相關的變量
    show variables like 'rpl%';
    +-------------------------------------------+------------+
    | Variable_name                             | Value      |
    +-------------------------------------------+------------+
    | rpl_semi_sync_master_enabled              | OFF        |
    | rpl_semi_sync_master_timeout              | 10000      |
    | rpl_semi_sync_master_trace_level          | 32         |
    | rpl_semi_sync_master_wait_for_slave_count | 1          |
    | rpl_semi_sync_master_wait_no_slave        | ON         |
    | rpl_semi_sync_master_wait_point           | AFTER_SYNC |
    | rpl_stop_slave_timeout                    | 31536000   |
    +-------------------------------------------+------------+
    7 rows in set (0.01 sec)
    
    rpl_semi_sync_master_enabled變量需要改成ON
    rpl_semi_sync_master_timeout表示半同步複製的超時時間,可以適當修改
    依然是修改/etc/my.cof文件,配置如下內容
    rpl_semi_sync_master_enabled=on
    rpl_semi_sync_master_timeout=500
    
    配置完成後重啓MySQL即可
  • 查看slave是否安裝了半同步複製插件rpl_semi_sync_slave可以使用如下命令
    show plugins;
    
    如果沒有安裝的話,使用如下命令安裝
    install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
    
  • 配置slave相關變量
    首先查看半同步複製相關的變量
    show variables like 'rpl%';
    +---------------------------------+----------+
    | Variable_name                   | Value    |
    +---------------------------------+----------+
    | rpl_semi_sync_slave_enabled     | OFF      |
    | rpl_semi_sync_slave_trace_level | 32       |
    | rpl_stop_slave_timeout          | 31536000 |
    +---------------------------------+----------+
    3 rows in set (0.01 sec)
    
    rpl_semi_sync_slave_enabled變量需要改成ON,需要修改/etc/my.cof文件,配置如下內容
    rpl_semi_sync_slave_enabled=on
    
    slave完成配置,重啓MySQL後,再次查看slave的狀態
    show slave status\G;
    *************************** 1. row ***************************
                   Slave_IO_State:
                      Master_Host: 192.168.1.101
                      Master_User:
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000003
              Read_Master_Log_Pos: 194
                   Relay_Log_File: localhost-relay-bin.000005
                    Relay_Log_Pos: 4
            Relay_Master_Log_File: mysql-bin.000003
                 Slave_IO_Running: No
                Slave_SQL_Running: Yes
    			.....
    
    可以看到,Slave_SQL_Running依然是Yes,只有Slave_IO_RunningNO,所以只需要重啓slave的IO線程即可
    start slave io_thread user='repl' password = 'your password';
    
    啓動完成後,基於logfile:pos模式的半同步複製也就配置完成。

GTID複製

由於上面的實驗,我們已經啓動了logfile + pos方式的複製鏈路,所以要改成GTID方式,先要停止slave,再重新配置複製鏈路

  • 停止slave
    slave節點上執行如下命令
    stop slave;
    
  • 配置複製鏈路
    change master to master_host='192.168.1.101', master_user='repl', master_password='your password', master_auto_position=1;
    
  • 啓動複製鏈路
    start slave;
    

查看鏈路狀態

show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.1.101
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 811
               Relay_Log_File: localhost-relay-bin.000002
                Relay_Log_Pos: 414
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              ......
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set: 1dbd5375-a4d9-11ea-9eef-000c29cf4cca:1,
81502f9e-a592-11ea-b912-000c2928707c:1-11
                Auto_Position: 1
              ......

可以看到Auto_Position變成了1,說明啓動了基於GTID方式的複製。

主從複製延遲

主從複製是一種異步複製模型,延遲自然是不可避免。只要延遲在業務能夠接受的範圍之內,都是可以容忍的。但是有時候主從複製的延遲會很大,可以總結爲以下原因

  • master上執行了大事務,大事務是指耗時很長的事務。改進辦法是把大事務轉換成多個小事務
  • masterslave之間網絡波動
  • 單個master節點下掛載的slave節點過多,可能會把master網卡帶寬打滿。改進辦法就是減少單個master掛載slave的數量
  • master節點會有多個線程同時寫入,而slave節點進行replaysql_thread只有一個。改進辦法就是使用MySQL 5.7的多線程複製的方式、或者使用MGR複製架構。

總結

本篇主要從理論到實踐,完整的講解了MySQL的幾種主從模型。根據複製點的選取方式,可以分成基於日誌點的方式和基於GTID的方式。根據master節點事務的提交方式可以分成異步複製半同步複製

至於文中提到的MMMMHA架構,留待下一篇再詳細講解。

參考

  • https://www.cnblogs.com/bigbrotherer/p/7241845.html
  • https://blog.51cto.com/13434336/2178937
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章