MySQL基礎 - 主從複製
概要
主要探索一下幾個問題:
- MySQL 主從複製 (MySQL Replication) 是什麼?
- MySQL主從架構
- 用docker搭建一個簡單的主從庫
- 讀寫分離
- 主從延時分析
主從複製原理
MySQL 主從複製是指資料可以從一個MySQL資料庫伺服器主節點複製到一個或多個從節點。在Master與Slave之間的實現整個複製過程主要由三個執行緒來完成,其中兩個執行緒(SQL執行緒和I/O執行緒)在Slave端,另外一個執行緒(I/O執行緒)在Master端。
從上面架構圖看,可以知道主從複製的大概步驟:
- 主庫master開啟binary log,將資料更新操作記錄到binlog中;
- 從庫slave啟動一個I/O thread 連線並監聽master的binlog,以拉的方式讀取;如果沒有它會睡眠等待Master產生新的日誌事件。
- 如果從庫監聽到主庫有新的日誌事件(Log Events),則會將其拷貝至Slave伺服器中的中繼日誌(Relay Log)。
- 從庫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基本操作
要想檢視映象的版本號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
建立主從庫
建立主庫
-
建立一個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
讀寫分離
讀寫分離一般分兩種方式:直連方式和代理方式。
-
直連方式:客戶端(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
總結:
- 如果master的binlog檔案突然變得很大,比如平常的是1G,最近的binlog檔案飆升到10G,那麼要排查業務是否有大量的操作,如批量插入等業務。
- 如果當前slave消費的binlog檔案落後於master的最新binlog檔案,那說明從庫消費慢。
分析 slave 執行情況
一般因為機器效能、從庫引數配置從而導致從庫延遲情況,可以檢視從庫的執行,從Read_Master_Log_Pos
、Exec_Master_Log_Pos
、Seconds_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_Pos
、Exec_Master_Log_Pos
、Seconds_Behind_Master
引數,可以分析從庫的處理情況。如果處理慢,有可能是:
- binlog檔案過大,導致消費不過來
- 從庫有慢查詢sql、鎖、事務等耗時操作導致從庫壓力大
- 機器效能差,檢視I/O情況
- 從庫配置不合適
優化方案
- 優化表結構:
- 建立/優化索引
- 拆分大表
- 業務優化:
- 優化慢查詢、減少事務
- 避免在業務繁忙期執行批量操作
- 根據業務的實時性要求,改查主從庫;實時性要求高的直接查主庫。
- 機器效能優化:選擇高效能機器,堆疊CPU、固態硬碟、記憶體
- 調整mysql叢集架構和引數