1. 程式人生 > >MySQL 主從複製原理及建立過程

MySQL 主從複製原理及建立過程

前言

mysql 是我工作中常用的資料庫,不過僅限於 SQL 操作,通過阿里雲的 RDS 可以快速生成一個例項,對於其原理並不甚瞭解,所以閒暇之餘瞭解了一下,並記錄下來,與大家共享、交流。

目錄

一、MySQL複製技術

1. 複製的用途

  • 實時災備,用於故障切換
  • 可建立讀寫分離,提供更好的查詢服務
  • 把備份等操作都放在從伺服器上進行,減少對業務的影響

2. 複製存在的問題

  • 主庫宕機後,資料可能丟失
  • 從庫只有一個sql Thread,主庫寫壓力大時,複製很可能延時
  • 一主多從,從機不宜過多,主伺服器需要同時向多臺伺服器中寫入資料,壓力會很大,這個時候推薦使用叢集技,這個我之後會試做,在此不做描述

至於宕機後怎麼做,複製延遲怎麼解決,就不提了,因為我也沒遇到過,不敢信口胡說。網上有好多大神給出了這些問題的解決方案,需要時可借鑑一下。

3. 複製的原理

  • MySQL 主從複製(replication)是一個非同步的複製過程。從一個例項(Master)複製到另一個例項(Slave),整個過程需要由 Master 上的 IO 程序Slave 上的 Sql 程序IO 程序 共同完成。
  • 首先 Master 端必須開啟 binary log(bin-log),因為整個複製過程實際上就是 Slave 端從 Master 端獲取相應的二進位制日誌,然後在本地完全順序的執行日誌中所記錄的各種操作。

原理圖如下:


MySQL主從複製原理圖

主從複製過程:

1. Slave 端的 IO 程序連線上 Master,向 Master 請求指定日誌檔案的指定位置(或者從最開始的日誌)之後的日誌內容

2. Master 接收到來自 Slave 的 IO 程序的請求後,負責複製的 IO 程序根據 Slave 的請求資訊,讀取相應日誌內容,返回給 Slave 的IO程序,並將本次請求讀取的 bin-log 檔名及位置一起返回給 Slave 端
3. Slave 端的 IO 程序接收到資訊後,將接收到的日誌內容依次新增到 Slave 端的 relay-log(中繼日誌) 檔案的最末端,並將讀取到的 Master 端的 bin-log 的檔名和位置記錄到 master-info 檔案中

,以便在下一次讀取的時候能夠清楚的告訴 Master :”我需要從某個 bin-log 的哪個位置開始往後的日誌內容,請發給我”;
4. Slave 端的 Sql 程序檢測到 relay-log (中繼日誌)中新增加了內容後,會馬上解析 relay-log 的內容成為在 Master 端真實執行時候的那些可執行的內容,並在本地執行。
>

過程產生三個執行緒(thread):

兩個 IO執行緒:主庫會建立一個執行緒,用來發送 binlog 內容到從庫;從庫I/O執行緒讀取主庫的 binlog 輸出執行緒傳送的更新並拷貝這些更新到本地檔案,其中包括 relay-log(中繼日誌) 檔案
一個 SQL執行緒:SQL負責將中繼日誌應用到 slave 資料庫中,完成 AB (主從)複製資料同步。

主從複製的方式:

1) 同步複製:
Master 伺服器操作完成,當操作作為事件寫入二進位制日誌,傳遞給 slave,存放到中繼日誌中,然後在本地執行完操作,即反饋同步成功
2) 半同步複製:
主庫在執行完客戶端提交的事務後不是立刻返回給客戶端,而是等待至少一個從庫接收到並寫到relay log中才返回給客戶端。
該功能不是 mysql 官方提供的,是5.5版本時由 google 研發半同步補丁後支援,需要 semi 外掛
3) 非同步複製:
主庫在執行完客戶端提交的事務後會立即將結果返給給客戶端,並不關心從庫是否已經接收並處理

4. 複製技術

  • 傳統複製技術
    主從複製,預設是通過pos複製(postion),就是說在日誌文件裡,將使用者進行的每一項操作都進行編號(pos),每一個event都有一個起始編號,一個終止編號。我們在配置主從複製時從節點時,要輸入master的log_pos值就是這個原因,要求它從哪個pos開始同步資料庫裡的資料,這是傳統複製技術。

  • 基於GDIT的複製技術(MySQL5.6 版本之後才出現)
    gtid: global transaction id (全域性事務編號)
    啟用 GTID 功能之後,MySQL伺服器在二進位制日誌檔案中記錄每一個操作時,每一個操作都有一個唯一的ID,就叫作GTID,它由 server_uuid + gtid 組成。
    這裡參考Groot的部落格講解的很明白。

  • GTID 的優、缺點
    這裡參考了張衝andy的部落格,總結的比較好。

二、MySQL 主從複製的實現

我在搭建了一個 LAMP 架構的模擬QQ農場後,想起主從複製的問題,所以在此基礎上,建立了從主機,過程記錄在此。
這次就是一個測試學習的過程,所以一切從簡,基於 centos7.4 的系統環境,全部用網路源安裝所需資源包(資料庫是我自定義的 repo,是 MySQL5.7.18 版本)。

1. 安裝並啟動

  • 安裝
    master 端,檢查是否安裝了 mariadb 資料庫,有的話先用命令 rpm -e --nodeps mariadb...解除安裝
    master端mysql
    slave 端,方法同 master 端。
    slave端

  • 啟動
    systemctl start mysqld && systemctl enable mysqld
    檢視啟動狀態
    啟動mysql服務

2. 編輯配置檔案

mkdir -p /mydata/binlog/master
chown -R mysql.mysql /mydata # 這一步很重要,否則mysql重啟失敗
vim /etc/my.cnf

配置檔案

master 端和 slave 端的 server_id 不可一樣,一般用 ipv4 的 IP 末位來標誌。
  • 重啟 mysqld 服務。
    systemctl restart mysqld

3. 配置主從複製

# 將 slave 端的庫內容與 master 端同步,執行如下步驟
master 端:
mysqldump -uroot -p --all-databases >> /root/all.sql
slave  端:
mysql -uroot -p < all.sql

同步資料庫
同步資料庫2

# 在master端為slave授權
mysql> grant replication slave on *.* to 'slave'@'$IP' identified by 'password';
# 從slave端登陸測試
mysql -uslave -p -h $IP

# master端查詢一些引數
mysql> select @@server_uuid;    # 這裡的值和slave端不可一致,有的人用克隆機做的,這樣的話修改其中一臺機器的這個值就可以了
mysql> show master status;
mysql> show binlog events in "File列的值";
# slave 端部署
mysql> select @@server_uuid;    # 確認與master端不一樣
mysql> show global variables like "%gtid%"; # 確認gtid已經開啟
mysql> change master to
    -> MASTER_HOST='192.168.40.135',
    -> MASTER_USER='slave',
    -> MASTER_PASSWORD='...',
    -> MASTER_AUTO_POSITION=1;
mysql> start slave;
mysql> show slave status;

以下是slave端的操作:
status
status

到此就可以做測試了,在 master 端執行 DML(data manipulation language) 語句,在slave端看到資料的同步即是成功了。
查詢1
刪除1
查詢2

4. 測試宕機

停掉slave的 mysql 服務,在master端執行DML(data manipulation language) 語句,重啟slave端的服務,發現數據是同步的。
查詢3
master 停掉 mysql 服務,應用切換到從資料庫,可以用 keepalived 來配置並實現,在此不做描述。

三、雙主複製

本次測試不需要雙主,不過這個功能的實現也很容易,就當做幾條命令的筆記在這了。
實現了上面的操作,雙主複製就很好操作了,如下:

# 在master端給自己授權
mysql> grant replication slave on *.* to 'master'@'192.168.40.%' identified by 'password';
# 在slave端
mysql> show master status;
查得 log_file 和 log_pos 的值

# 指定master主機
mysql> change master to
    -> MASTER_HOST='192.168.40.138', # slave 端的地址
    -> MASTER_USER='master',
    -> MASTER_PASSWORD='...',
    -> MASTER_LOG_FILE="slave.000001",
    -> MASTER_LOG_POS=154;
mysql> start slave;
mysql> show slave status;

測試雙主複製,略…

四、讀寫分離

實現讀寫分離的中介軟體有好多,例如:
mysql-proxy
Atlas —— 360
amoeba ——— 阿里的
mycat

本次測試操作,讀寫分離應用中介軟體 mycat 實現。

1. 安裝包準備和環境配置

  • GitHub 上下載 mycat
  • java 官網下載 jdk
  • 配置服務
# 解除安裝系統自帶jdk
rpm -qa | grep -E 'gcj|jdk'
rpm -e --nodeps java-1.8.0-openjdk-headless java-1.8.0-openjdk java-1.7.0-openjdk java-1.7.0-openjdk-headless

# 解壓到 /usr/local 目錄下
tar -xf filename -C /usr/local/java
tar -xf filename -C /usr/local/mycat

# 建立使用者
useradd -s /sbin/nologin -M mycat
chown -R mycat.mycat /usr/local/mycat
chown -R root.root /usr/local/java

# 配置全域性變數
vim /etc/profile
[root@lamp /root] LAMP
# tail -5 /etc/profile
export JAVA_HOME=/usr/local/java
export MYCAT_HOME=/usr/local/mycat
export PATH=$PATH:$JAVA_HOME/bin

# 建立軟連線
ln -s /usr/local/mycat/bin/mycat /usr/bin/mycat
# 啟動服務
mycat start
netstat -antp | grep -E "8066|9066"

安裝包
安放位置
全域性變數
啟動mycat
檢視埠

2. 配置 mycat 虛擬資料庫

# 備份原配置檔案,以防修改出錯,事實上我是出錯了,查了好久
cd /usr/local/mycat
cp conf/server.xml{,.bak}
cp conf/schema.xml{,.bak}

vim conf/server.xml
 80     <user name="nimo">
 81         <property name="password">123</property>
 82         <property name="schemas">discuz</property>
         ...
 93     </user>
 這裡只保留一組<user>標籤,這就是我出問題的地方,當時我保留了原配置檔案裡的,後來備註掉之後,就可以啟動了。

vim conf/schema.xml
  1 <?xml version="1.0"?>
  2 <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3 <mycat:schema xmlns:mycat="http://io.mycat/">
  4
  5     <schema name="discuz" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
  6     </schema>
  7     <dataNode name="dn1" dataHost="localhost1" database="discuz" />
  8     <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
  9               writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
 10         <heartbeat>select user()</heartbeat>
 11         <!-- can have multi write hosts -->
 12         <writeHost host="hostM1" url="localhost:3306" user="discuz"
 13                    password="Www.1.com">
 14             <!-- can have multi read hosts -->
 15             <readHost host="hostS2" url="192.168.40.138:3306" user="discuz" password="Www.1.com" />
 16         </writeHost>
 17         <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
 18     </dataHost>
 19 </mycat:schema>
原配置檔案註釋掉的內容全被我刪掉了,每個標籤裡的值都是關鍵,但是我沒時間詳細備註了,後期如有時間再來補上。

# 重啟服務
mycat restart
netstat -antp | grep -E "8066|9066"

配置server.xml
配置檔案
重啟服務
檢視埠

3. 總結

整個學習、操作、測試流程做完,有點兒溫故知新的感覺,對 mysql 工作原理和效能實現有了較深的理解,也學習了網友大神們的心得,姑且算是促進了技能的成長和發展吧。
做到這我想到了接下來該進行的內容,Python 與 mysql 的結合,在工作中的應用該是怎樣的?

後記

本文斷斷續續寫完,由於我還有自己的工作,部分內容未能全面表述,甚至可能存在嚴重的錯誤,以後有時間再來彌補。也歡迎各路大神來交流指正。