MySQL 8.0 主從復制原理分析與實戰

MySQL 8.0 主從復制原理分析與實戰

      • 半同步復制
      • 設計理念:復制狀態機——幾乎所有的分布式存儲都是這么復制數據的
      • 基于全局事務標識符(GTID)復制
      • GTID工作原理
      • 多主模式
      • 多主模式部署示例

課程目標:
MySQL 復制(Replication) 是官方提供的主從復制(源到副本的復制)方案,用于將一個 MySQL 的實例同步到另一個實例中。 這是使用最廣泛的容災方案(重點掌握)。

復制(Replication)
什么是復制
官網:https://dev.mysql.com/doc/refman/8.0/en/replication.html
MySQL Replication是官方提供的主從同步方案,也是用的最廣的同步方案。Replication(復制)使來自一個 MySQL數據庫服務器(稱為源(Source))的數據能夠復制到一個或多個 MySQL 服務器(稱為副本(Replica))。默認情況下,復制是異步的;副本不需要永久連接即可從源接收更新。根據配置,您可以復制所有數據庫、指定數據庫,甚至某個數據庫中的指定表。
說明: 舊版本的 MySQL 復制將源(Source)稱為主(Master),將副本(Replica)稱為從(Slave)
在這里插入圖片描述

復制的優勢:

  • 高可用:通過配置一定的復制機制,MySQL 實現了跨主機的數據復制,從而獲得一定的高可用能力,如果需要獲得更高的可用性,只需要配置多個副本,或者進行級聯復制就可以達到目的。
  • 性能擴展:由于復制機制提供了多個數據備份,可以通過配置一個或多個副本,將讀請求分發至副本節點,從而獲得整體上讀寫性能的提升。
  • 異地災備:只需要將副本節點部署到異地機房,就可以輕松獲得一定的異地災備能力。實際當中,需要考慮網絡延遲等可能影響整體表現的因素。
  • 交易分離:通過配置復制機制,并將低頻、大運算量的交易發送至副本節點執行,就可以避免這些交易與高頻交易競爭運算資源,從而避免整體的性能問題。
    缺點:
  • 沒有故障自動轉移,容易造成單點故障
  • 主庫從庫之間有主從復制延遲問題,容易造成最終數據的不一致
  • 從庫過多對主庫的負載以及網絡帶寬都會帶來很大的負擔

應用場景

  • 電子商務平臺: 在電商平臺中,主從復制可以用于實現讀寫分離,提高并發處理能力,同時確保數據的一致性。
  • 社交網絡: 在社交網絡應用中,可以利用主從復制來提供快速的讀取服務,同時將數據變更復制到從數據庫以備份數據。
  • 實時監控和報警系統: 在監控系統中,主從復制可以用于實現數據的分布式存儲和快速數據查詢。
  • 新聞和媒體網站: 在高訪問量的新聞網站中,可以使用主從復制來提供高可用性和快速的內容訪問。
  • 金融服務: 在金融行業,數據的安全性和可用性至關重要,主從復制可以用于數據備份和高可用性的實現。

復制的方式
MySQL 8.0支持多種復制方式:

  • 傳統的方法是基于從源的二進制日志(binlog)復制事件,并要求日志文件及其中的位置在源和副本之間進行同步。作為源(數據庫更改發生的地方)的 MySQL 實例將更新和更改作為“事件”寫入二進制日志。根據所記錄的數據庫更改,二進制日志中的信息以不同的日志格式存儲。副本配置為從源中讀取二進制日志,并在副本的本地數據庫上執行二進制日志中的事件。
#獲取binlog文件列表
mysql> show binary logs;
#查看指定binlog文件的內容 
mysql> show binlog events in 'binlog.000003';

官網:https://dev.mysql.com/doc/refman/8.0/en/binlog-replication-configuration-overview.html

  • 基于全局事務標識符(GTID)的方式。基于 GTID 的復制是完全基于事務的,所以很容易確定源和副本是否一致; 只要在源上提交的所有事務也在副本上提交,就可以保證兩者之間的一致性。
    官網:https://dev.mysql.com/doc/refman/8.0/en/replication-gtids.html

復制的數據同步類型
MySQL 中的復制支持不同類型的同步。同步的原始類型是單向異步復制,其中一個服務器充當源,而一個或多個其他服務器充當副本。在 MySQL 8.0中,除了內置的異步復制之外,還支持半同步復制。使用半同步復制,在返回執行事務的會話之前,對源執行提交,直到至少有一個副本確認它已經接收并記錄了事務的事件。MySQL 8.0還支持延遲復制,以使副本故意落后于源至少指定的時間。
異步復制
默認情況下,MySQL 采用異步復制的方式,執行事務操作的線程不會等復制 Binlog 的線程。具體的時序你可以看下面這個圖:
在這里插入圖片描述

MySQL 主庫在收到客戶端提交事務的請求之后,會先寫入 Binlog,然后再提交事務,更新存儲引擎中的數據,事務提交完成后,給客戶端返回操作成功的響應。同時,從庫會有一個專門的復制線程,從主庫接收 Binlog,然后把 Binlog 寫到一個中繼日志里面,再給主庫返回復制成功的響應。從庫還有另外一個回放 Binlog 的線程,去讀中繼日志,然后回放 Binlog 更新存儲引擎中的數據。
提交事務和復制這兩個流程在不同的線程中執行,互相不會等待,這是異步復制。異步復制的劣勢是,可能存在主從延遲,如果主節點宕機,可能會丟數據。

半同步復制

MySQL 從 5.7 版本開始,增加一種半同步復制(Semisynchronous Replication)的方式。這種機制與異步復制相比主要有如下區別:

  • 主節點在收到客戶端的請求后,必須在完成本節點日志寫入的同時,還需要等待至少一個從節點完成數據同步的響應之后(或超時),才會響應請求。
  • 從節點只有在寫入 relay-log (中繼日志)并完成刷盤之后,才會向主節點響應。
  • 當從節點響應超時時,主節點會將同步機制退化為異步復制。在至少一個從節點恢復,并完成數據追趕后,主節點會將同步機制恢復為半同步復制。
    在這里插入圖片描述

可以看出,相比于異步復制,半同步復制在一定程度上提高了數據的可用性,在未退化至異步復制時,如果主節點宕機,此時數據已復制至至少一臺從節點。同時,由于向客戶端響應時需要從節點完成響應,相比于異步復制,此時多出了主從節點上網絡交互的耗時以及從節點寫文件并刷盤的耗時,因此整體上集群對于客戶端的響應性能表現必然有所降低。
半同步復制有兩個重要的參數:

  • rpl_semi_sync_master_wait_slave_count(8.0.26之后改為rpl_semi_sync_source_wait_for_replica_count):至少等待數據復制到幾個從節點再返回。這個數量配置的越大,丟數據的風險越小,但是集群的性能和可用性就越差。
  • rpl_semi_sync_master_wait_point(8.0.26之后改為rpl_semi_sync_source_wait_point):這個參數控制主庫執行事務的線程,是在提交事務之前(AFTER_SYNC)等待復制,還是在提交事務之后(AFTER_COMMIT)等待復制。默認是 AFTER_SYNC,也就是先等待復制,再提交事務,這樣就不會丟數據。

設計理念:復制狀態機——幾乎所有的分布式存儲都是這么復制數據的

在 MySQL 中,無論是復制還是備份恢復,依賴的都是全量備份和 Binlog,全量備份相當于備份那一時刻的一個數據快照,Binlog 則記錄了每次數據更新的變化,也就是操作日志。這種基于“快照 + 操作日志”的方法,不是 MySQL 特有的。比如說,Redis Cluster 中,它的全量備份稱為 Snapshot,操作日志叫 backlog,它的主從復制方式幾乎和 MySQL 是一模一樣的。Elasticsearch用的是 translog,它備份和恢復數據的原理和實現方式也是完全一樣的。
任何一個存儲系統,無論它存儲的是什么數據,用什么樣的數據結構,都可以抽象成一個狀態機。存儲系統中的數據稱為狀態(也就是 MySQL 中的數據),狀態的全量備份稱為快照(Snapshot),就像給數據拍個照片一樣。我們按照順序記錄更新存儲系統的每條操作命令,就是操作日志(Commit Log,也就是 MySQL 中的 Binlog)。
在這里插入圖片描述

復制數據的時候,只要基于一個快照,按照順序執行快照之后的所有操作日志,就可以得到一個完全一樣的狀態。在從節點持續地從主節點上復制操作日志并執行,就可以讓從節點上的狀態數據和主節點保持同步。
主從同步做數據復制時,一般可以采用幾種復制策略。

  • 性能最好的方法是異步復制,主節點上先記錄操作日志,再更新狀態數據,然后異步把操作日志復制到所有從節點上,并在從節點執行操作日志,得到和主節點相同的狀態數據。異步復制的劣勢是,可能存在主從延遲,如果主節點宕機,可能會丟數據。
  • 另外一種常用的策略是半同步復制,主節點等待操作日志最少成功復制到 N 個從節點上之后,再更新狀態,這種方式在性能、高可用和數據可靠性幾個方面都比較平衡,很多分布式存儲系統默認采用的都是這種方式。
    在這里插入圖片描述

基于binlog位點同步的主從復制原理

1、主庫會生成多個 binlog 日志文件。
2、從庫的 I/O 線程請求指定文件和指定位置的 binlog 日志文件(位點)。
3、主庫 dump 線程(Dump 線程是指主服務器上的一個線程,它在主從復制過程中起到關鍵作用)獲取指定位點的 binlog 日志。
4、主庫按照從庫發送給來的位點信息讀取 binlog,然后推送 binlog 給從庫。
5、從庫將得到的 binlog 寫到本地的 relay log (中繼日志) 文件中。
6、從庫的 SQL 線程讀取和解析 relay log 文件。
7、從庫的 SQL 線程重放 relay log 中的命令。
異步復制示例
1)快速創建mysql實例
參考:Docker 安裝 MySQL8.0
利用Docker快速搭建Mysql8一主兩從復制架構
角色server_idip:portmysql-source (主)10192.168.65.185:3307mysql-replica1(從1)11192.168.65.185:3308mysql-replica2(從2)12192.168.65.185:3309

2 ) 配置mysql主從復制

  • 主節點
    2.1)創建掛載目錄
    mkdir -p /mysql/replication/source/data /mysql/replication/source/conf /mysql/replication/source/log
    2.2) 準備配置文件
vim /mysql/replication/source/conf/custom.cnf
[mysql]
# 設置mysql客戶端默認編碼
default-character-set=utf8[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysqlsecure-file-priv= NULL# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0# 服務器唯一ID,默認是1
server-id=10# 啟用二進制日志
log-bin=mysql-bin# 最大連接數 
max_connections=1000# 設置默認時區
default-time_zone='+8:00'# 0:區分大小寫
# 1:不區分大小寫
lower_case_table_names=1!includedir /etc/mysql/conf.d/pid-file: 這是MySQL服務器的進程ID文件的位置。通過這個文件,您可以在系統上找到正在運行的MySQL服務器的進程。
socket: 這是MySQL服務器用于本地通信的Unix套接字文件的位置。
datadir: 這是MySQL服務器存儲其數據文件的位置。
secure-file-priv: 這是一個用于限制LOAD_FILE()和SELECT ... INTO OUTFILE命令的文件路徑。如果此選項被設置,那么這兩個命令只能用于讀取在這個路徑下的文件。設置為NULL表示禁用這個功能。
symbolic-links: 如果設置為0MySQL服務器將不允許在數據目錄中使用符號鏈接。這有助于防止安全風險。
server-id: 每個MySQL服務器實例在復制時需要有一個唯一的ID。這有助于區分不同的服務器,特別是在復制環境中。
log-bin: 啟用二進制日志記錄所有對數據庫的更改,這對于復制和恢復操作是必要的。
max_connections: 這是MySQL服務器可以接受的最大并發連接數。
default-time_zone: 這設置了MySQL服務器的默認時區。
lower_case_table_names: 這決定了MySQL如何存儲和比較表名。設置為1意味著表名不區分大小寫(但在文件系統中它們仍然會區分大小寫)。
!includedir /etc/mysql/conf.d/: 這告訴MySQL服務器從/etc/mysql/conf.d/目錄中包含其他配置文件。這意味著該目錄下的任何.cnf或.ini文件都會被合并到這個主配置文件中。
replicate_do_db :  待同步的數據庫日志
replicate_ignore_db:不同步的數據庫日志

2.3)運行mysql容器

# 創建主從復制的網絡
docker network create --driver bridge mysql-source-replica
#運行mysql容器
docker run  -d  \
--name mysql-source \
--privileged=true \
--restart=always \
--network  mysql-source-replica \
-p 3307:3306 \
-v /mysql/replication/source/data:/var/lib/mysql \
-v /mysql/replication/source/conf:/etc/mysql/conf.d  \
-v /mysql/replication/source/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1  

2.4)配置遠程訪問

docker exec -it mysql-source /bin/bash
mysql -u root -pALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
flush privileges;

在這里插入圖片描述

  • 從節點1
# 創建掛載目錄
mkdir -p /mysql/replication/replica1/data /mysql/replication/replica1/conf /mysql/replication/replica1/log#準備配置文件
vim /mysql/replication/replica1/conf/custom.cnf
[mysql]
# 設置mysql客戶端默認編碼
default-character-set=utf8[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysqlsecure-file-priv= NULL# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0# 服務器唯一ID,默認是1
server-id=11# 啟用二進制日志
log-bin=mysql-bin# 最大連接數 
max_connections=1000# 設置默認時區
default-time_zone='+8:00'# 0:區分大小寫
# 1:不區分大小寫
lower_case_table_names=1!includedir /etc/mysql/conf.d/#運行mysql容器
docker run  -d  \
--name mysql-replica1 \
--privileged=true \
--restart=always \
--network  mysql-source-replica \
-p 3308:3306 \
-v /mysql/replication/replica1/data:/var/lib/mysql \
-v /mysql/replication/replica1/conf:/etc/mysql/conf.d  \
-v /mysql/replication/replica1/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1  #配置遠程訪問
docker exec -it mysql-replica1 /bin/bash
mysql -u root -pALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
flush privileges;
  • 從節點2
mkdir -p /mysql/replication/replica2/data /mysql/replication/replica2/conf /mysql/replication/replica2/log#準備配置文件
vim /mysql/replication/replica2/conf/custom.cnf
[mysql]
# 設置mysql客戶端默認編碼
default-character-set=utf8[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysqlsecure-file-priv= NULL# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0# 服務器唯一ID,默認是1
server-id=12# 啟用二進制日志
log-bin=mysql-bin# 最大連接數 
max_connections=1000# 設置默認時區
default-time_zone='+8:00'# 0:區分大小寫
# 1:不區分大小寫
lower_case_table_names=1!includedir /etc/mysql/conf.d/#運行mysql容器
docker run  -d  \
--name mysql-replica2 \
--privileged=true \
--restart=always \
--network  mysql-source-replica \
-p 3309:3306 \
-v /mysql/replication/replica2/data:/var/lib/mysql \
-v /mysql/replication/replica2/conf:/etc/mysql/conf.d  \
-v /mysql/replication/replica2/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1 #配置遠程訪問
docker exec -it mysql-replica2 /bin/bash
mysql -u root -pALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
flush privileges;

在這里插入圖片描述

3)主庫配置復制用戶
每個副本使用一個 MySQL 用戶名和密碼連接到源,因此在源上必須有一個用戶帳戶,副本可以使用該帳戶進行連接。

# 連接主庫mysql-source
CREATE USER 'fox'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'fox'@'%';
flush privileges;

4)查看 master 機器的狀態
使用 SHOW MASTER STATUS 語句確定當前二進制日志文件的名稱和位置

# 主庫上執行
SHOW MASTER STATUS;

在這里插入圖片描述

“文件”列顯示日志文件的名稱,“位置”列顯示文件內的位置。
5) 從節點設置主庫信息
文檔:https://dev.mysql.com/doc/refman/8.0/en/replication-howto-slaveinit.html
在從庫上執行 CHANGE REPLICATION SOURCE TO 語句(來自 MySQL 8.0.23)或 CHANGE MASTER TO語句(在 MySQL 8.0.23之前)

mysql> CHANGE MASTER TO->     MASTER_HOST='source_host_name',->     MASTER_USER='replication_user_name',->     MASTER_PASSWORD='replication_password',->     MASTER_LOG_FILE='recorded_log_file_name',->     MASTER_LOG_POS=recorded_log_position;Or from MySQL 8.0.23:
mysql> CHANGE REPLICATION SOURCE TO->     SOURCE_HOST='source_host_name',->     SOURCE_USER='replication_user_name',->     SOURCE_PASSWORD='replication_password',->     SOURCE_LOG_FILE='recorded_log_file_name',->     SOURCE_LOG_POS=recorded_log_position;

從庫1和從庫2上執行

# from MySQL 8.0.23 執行下面的命令。   
change replication source to source_host='192.168.65.185', source_user='fox', source_password='123456', source_port=3307, source_log_file='mysql-bin.000003', source_log_pos=1273, source_connect_retry=30;
source_host:主數據庫的IP地址;
source_port:主數據庫的運行端口;
source_user:在主數據庫創建的用于同步數據的用戶賬號;
source_password:在主數據庫創建的用于同步數據的用戶密碼;
source_log_file:指定從數據庫要復制數據的日志文件,通過查看主數據的狀態,獲取File參數;
source_log_pos:指定從數據庫從哪個位置開始復制數據,通過查看主數據的狀態,獲取Position參數;
source_connect_retry:連接失敗重試的時間間隔,單位為秒。

在這里插入圖片描述

6)開啟從庫
#開啟從庫
start slave; 或者 start replica;
#查看從庫狀態
show slave status \G; 或者 show replica status \G;
在這里插入圖片描述

看到Replica_SQL_Running_State: Replica has read all relay log; waiting for more updates基本說明配置成功了,已經開始了主從復制。
7)測試主從復制功能
測試腳本


```java
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;CREATE DATABASE IF NOT EXISTS test;
USE test; 
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` int(0) NOT NULL AUTO_INCREMENT,`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`last_updated` bigint(0) NULL DEFAULT NULL,`is_deleted` int(0) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (2, '張三', '廣州白云山', 1691563465, 0);SET FOREIGN_KEY_CHECKS = 1;

半同步復制示例
文檔:https://dev.mysql.com/doc/refman/8.0/en/replication-semisync-installation.html
1)安裝半同步插件
- 主節點
#from MySQL 8.0.26:
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';#驗證是否成功安裝
SELECT PLUGIN_NAME, PLUGIN_STATUSFROM INFORMATION_SCHEMA.PLUGINSWHERE PLUGIN_NAME LIKE '%semi%';
![在這里插入圖片描述](https://i-blog.csdnimg.cn/direct/51ad557d4fed46e7ab63b1794ab47504.png)- 從節點```java
#from MySQL 8.0.26:
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';#驗證是否成功安裝
SELECT PLUGIN_NAME, PLUGIN_STATUSFROM INFORMATION_SCHEMA.PLUGINSWHERE PLUGIN_NAME LIKE '%semi%';

在這里插入圖片描述

2)開啟半同步功能

- 主節點
SET GLOBAL rpl_semi_sync_source_enabled=1;
#查看是否開啟
show variables like "%semi_sync%";

在這里插入圖片描述

  • 從節點
set global rpl_semi_sync_replica_enabled=1;

在這里插入圖片描述

  1. 重啟從節點上的I/O線程
    如果在運行時啟用副本上的半同步復制,您還必須啟動復制I/O(接收端)線程(如果已經運行,則先停止它),以使副本連接到源端并注冊為半同步副本。
    STOP REPLICA IO_THREAD;
    START REPLICA IO_THREAD;
    4)半同步復制測試
    測試:當從節點響應超時時,主節點會將同步機制退化為異步復制。 從節點恢復后,同步機制是否會恢復為半同步復制
# 修改主節點半同步屬性
set global rpl_semi_sync_source_wait_for_replica_count=2;
set global rpl_semi_sync_source_timeout=100000;#停掉從節點2
docker stop mysql-replica2#恢復從節點2
docker start mysql-replica2
#從節點2中執行
set global rpl_semi_sync_replica_enabled=1;
STOP REPLICA IO_THREAD;
START REPLICA IO_THREAD;

基于binlog位點主從復制痛點分析
痛點 1:首次開啟主從復制的步驟復雜

  • 第一次開啟主從同步時,要求從庫和主庫是一致的。
  • 找到主庫的 binlog 位點。
  • 設置從庫的 binlog 位點。
  • 開啟從庫的復制線程。
    痛點 2:恢復主從復制的步驟復雜
  • 找到從庫復制線程停止時的位點。
  • 解決復制異常的事務。無法解決時就需要手動跳過指定類型的錯誤,比如通過設置 slave_skip_errors=1032,1062。當然這個前提條件是跳過這類錯誤是無損的。(1062 錯誤是插入數據時唯一鍵沖突;1032 錯誤是刪除數據時找不到行)
    不論是首次開啟同步時需要找位點和設置位點,還是恢復主從復制時,設置位點和忽略錯誤,這些步驟都顯得過于復雜,而且容易出錯。所以 MySQL 5.6 版本引入了 GTID,徹底解決了這個困難。

基于全局事務標識符(GTID)復制

官網:https://dev.mysql.com/doc/refman/8.0/en/replication-gtids.html
GTID是一個基于原始mysql服務器生成的一個已經被成功執行的全局事務ID,它由服務器ID以及事務ID組合而成。這個全局事務ID不僅僅在原始服務器器上唯一,在所有存在主從關系 的mysql服務器上也是唯一的。正是因為這樣一個特性使得mysql的主從復制變得更加簡單,以及數據庫一致性更可靠。

  • 一個GTID在一個服務器上只執行一次,避免重復執行導致數據混亂或者主從不一致。
  • GTID用來代替傳統復制方法,不再使用MASTER_LOG_FILE+MASTER_LOG_POS開啟復制。而是使用MASTER_AUTO_POSTION=1的方式開始復制。
  • 在傳統的replica端,binlog是不用開啟的,但是在GTID中replica端的binlog是必須開啟的,目的是記錄執行過的GTID(強制)。
    GTID 的優勢
  • 更簡單的實現 failover,不用以前那樣在需要找位點(log_file 和 log_pos)。
  • 更簡單的搭建主從復制。
  • 比傳統的復制更加安全。
  • GTID 是連續的沒有空洞的,保證數據的一致性,零丟失。
    GTID結構
    GTID表示為一對坐標,由冒號(:)分隔,如下所示:

```java
GTID = source_id:transaction_id

- source_id標識source服務器,即源服務器唯一的server_uuid,由于GTID會傳遞到replica,所以也可以理解為源ID。
- transaction_id是一個序列號,由事務在源上提交的順序決定。序列號的上限是有符號64位整數(2^63-1)
例如,最初要在UUID為3E11FA47-71CA-11E1-9E33-C80AA9429562的服務器上提交的第23個事務具有此GTID```java
3E11FA47-71CA-11E1-9E33-C80AA9429562:23

GTID集合是由一個或多個GTID或GTID范圍組成的集合。來自同一服務器的一系列gtid可以折疊成單個表達式,如下所示:

3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5

源自同一服務器的多個單一gtid或gtid范圍也可以包含在單個表達式中,gtid或范圍以冒號分隔,如下例所示:

3E11FA47-71CA-11E1-9E33-C80AA9429562:1-3:11:47-49

GTID集合可以包括單個GTID和GTID范圍的任意組合,也可以包括來自不同服務器的GTID。

2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19

GTID存儲在mysql數據庫中名為gtid_executed的表中。該表中的一行包含它所代表的每個GTID或GTID集合的起始服務器的UUID,以及該集合的開始和結束事務id。
在這里插入圖片描述

GTID工作原理

主庫計算主庫 GTID 集合和從庫 GTID 的集合的差集,主庫推送差集 binlog 給從庫。
當從庫設置完同步參數后,主庫 A 的 GTID 集合記為集合 x,從庫 B 的 GTID 集合記為 y。從庫同步的邏輯如下:
在這里插入圖片描述

  • 從庫 B 指定主庫 A,基于主備協議建立連接。
  • 從庫 B 把集合 y 發給主庫 A。
  • 主庫 A 計算出集合 x 和集合 y 的差集,也就是集合 x 中存在,集合 y 中不存在的 GTID 集合。比如集合 x 是 1~100,集合 y 是 1~90,那么這個差集就是 91~100。這里會判斷集合 x 是不是包含有集合 y 的所有 GTID,如果不是則說明主庫 A 刪除了從庫 B 需要的 binlog,主庫 A 直接返回錯誤。
  • 主庫 A 從自己的 binlog 文件里面,找到第一個不在集合 y 中的事務 GTID,也就是找到了 91。
  • 主庫 A 從 GTID = 91 的事務開始,往后讀 binlog 文件,按順序取 binlog,然后發給 B。
  • 從庫 B 的 I/O 線程讀取 binlog 文件生成 relay log,SQL 線程解析 relay log,然后執行 SQL 語句。
    GTID 同步方案和位點同步的方案區別是:
  • 位點同步方案是通過人工在從庫上指定哪個位點,主庫就發哪個位點,不做日志的完整性判斷。
  • 而 GTID 方案是通過主庫來自動計算位點的,不需要人工去設置位點,對運維人員友好。
    GTID的配置
    1) 修改主庫配置
    修改主庫的配置文件
#GTID:
#啟用全局事務標識符(GTID)模式    
gtid_mode=on    
# 強制GTID的一致性。這意味著在執行事務時,MySQL將確保所有涉及的服務器都使用相同的GTID集。
enforce_gtid_consistency=on  

2)修改從庫配置
修改從庫配置文件

#GTID:
gtid_mode=on  
enforce_gtid_consistency=on

從節點設置主庫信息

# 從庫配置同步參數
mysql> CHANGE MASTER TO>     MASTER_HOST = host,>     MASTER_PORT = port,>     MASTER_USER = user,>     MASTER_PASSWORD = password,>     MASTER_AUTO_POSITION = 1;  Or from MySQL 8.0.23:
mysql> CHANGE REPLICATION SOURCE TO>     SOURCE_HOST = host,>     SOURCE_PORT = port,>     SOURCE_USER = user,>     SOURCE_PASSWORD = password,>     SOURCE_AUTO_POSITION = 1;

SOURCE_AUTO_POSITION = 1: 這告訴從服務器使用自動位置跟蹤功能,以便它可以自動從主服務器獲取最新的二進制日志事件,而無需手動指定位置。
基于GTID主從復制示例
文檔:https://dev.mysql.com/doc/refman/8.0/en/replication-gtids-howto.html
在前面基于binlog日志主從復制的mysql服務上配置GTID復制。

  1. 同步所有的mysql服務器
    在主從服務器上都執行下面的命令:
# 設置MySQL服務器的全局只讀模式
mysql> SET @@GLOBAL.read_only = ON;

注意:只有在使用已經在進行復制而不使用gtid的服務器時才需要此步驟。對于新服務器,請繼續執行步驟3。
在這里插入圖片描述

  1. 停止所有的服務器
docker stop mysql-source mysql-replica1 mysql-replica2
  1. 主從節點都啟用GTID
    修改custom.cnf
# 啟用GTID
gtid_mode=ON
enforce-gtid-consistency=ON
啟動主從節點
docker start mysql-source mysql-replica1 mysql-replica2

4)從節點配置基于GTID的自動定位
進入從節點,執行下面命令:

mysql> stop replica;
mysql> change replication source to source_host='192.168.65.185', source_user='fox', source_password='123456', source_port=3307,source_auto_position=1;

在這里插入圖片描述

  1. 開啟從庫復制,并禁用只讀模式
# 開啟從庫復制
mysql> start replica;
# 只有在步驟1中將服務器配置為只讀時,才需要執行以下步驟。要允許服務器再次開始接受更新
mysql> SET @@GLOBAL.read_only = OFF;

查看從庫狀態是否正常

mysql> show replica status \G

在這里插入圖片描述

主從切換演練
場景1: 模擬主庫down機、從庫1數據同步完成、從庫2數據未同步完成
1)從庫2停止復制

mysql> stop replica;

2)主庫創建測試數據

INSERT INTO `test`.`user` VALUES (12, 'fox', NULL, NULL, NULL);
  1. 查詢數據
    從庫1
    在這里插入圖片描述

從庫2
在這里插入圖片描述

很顯然,從庫1同步了最新數據,比從庫2數據新
場景2:將主庫宕機,從庫1升級為主庫、從庫2切換主庫為從庫1(新的主庫),觀察從庫2是否同步未完成的事務
1)停止主庫
docker stop mysql-source
2)設置新主庫
設置replica1 為replica2的主庫,因為replica1的數據是完整的。
按照普通復制方法,需要計算主庫的log_pos和從庫設置成主庫的log_pos,可能出現錯誤
因為同一事務的GTID在所有節點上的值一致,那么根據replica2當前停止點的GTID就能定位到要主庫的GTID,所以直接在replica2上執行change即可

# replica1上創建復制用戶
CREATE USER 'fox'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'fox'@'%';
flush privileges;# replica2上執行 從replica1查詢
mysql> stop replica;
mysql> change replication source to source_host='192.168.65.185',source_port=3308,source_user='fox',source_password='123456',source_auto_position=1;mysql> start replica; 

在這里插入圖片描述

查詢同步結果
在這里插入圖片描述

場景3:模擬從庫刪除測試表,主庫對表進行插入操作。觀察從庫復制是否報錯
1)replica1刪除test.user表,主庫插入新記錄

# 從庫1 刪除user表
drop table test.user;
# 主庫插入新記錄
INSERT INTO `test`.`user` VALUES (14, 'AAA', NULL, NULL, NULL);
  1. 查看從庫同步情況
# 從庫1執行
mysql> show replica status\G

在這里插入圖片描述

報錯信息:事務aac92b21-b6a4-11ee-bab5-0242ac120002:6執行失敗
Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction ‘aac92b21-b6a4-11ee-bab5-0242ac120002:6’ at master log mysql-bin.000008, end_log_pos 1335. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
3)在主庫繼續進行其他事務,觀察gitd是否復制成功

# 主庫插入新記錄
INSERT INTO `test`.`user` VALUES (15, 'BBB', NULL, NULL, NULL);

從庫狀態
在這里插入圖片描述

可以看出從庫復制中斷 (注意:刪除了表,無法插入)
4)復制中斷修復:采用從庫跳過錯誤事務修復
因為從庫user表已經刪了(user表中部分數據不是利用gtid復制過去的),先從主庫將表數據拷貝到從庫
獲取從庫最新狀態

從庫執行

# 1.停止從庫1復制進程
mysql> stop replica;
# 2.設置事務號,事務號從 Retrieved_Gtid_Set 獲取,在session里設置gtid_next,即跳過這個GTID
# 注意,選擇跳過出現錯誤的事務
mysql> SET @@SESSION.GTID_NEXT= 'aac92b21-b6a4-11ee-bab5-0242ac120002:7';
# 3.設置空事物
mysql> BEGIN; COMMIT;
# 4.恢復自增事物號
mysql> SET SESSION GTID_NEXT = AUTOMATIC;
# 5.啟動從庫1復制進程
mysql> start replica;
# 再次查詢會發現主庫數據已經同步過來了
mysql> select * from test.user;

在這里插入圖片描述

如果需要一次跳過多條,找出需要跳過的gtid,批量執行:

stop replica; 
SET @@SESSION.GTID_NEXT= 'd58e6bad-ef3e-11ee-8285-0242ac120003:4';begin;commit;
SET @@SESSION.GTID_NEXT= 'd58e6bad-ef3e-11ee-8285-0242ac120003:5';begin;commit;
SET @@SESSION.GTID_NEXT= 'd58e6bad-ef3e-11ee-8285-0242ac120003:6';begin;commit;
SET SESSION GTID_NEXT = AUTOMATIC;
start replica;

完整的演示步驟:
1.從庫刪除user表

mysql> drop table user;

2.主庫連續刪除兩條數據

mysql> select * from user;
+----+--------+-----------------+--------------+------------+
| id | name   | address         | last_updated | is_deleted |
+----+--------+-----------------+--------------+------------+
|  2 | 張三   | 廣州白云山      |   1691563465 |          0 |
|  3 | fox    | NULL            |         NULL |       NULL |
|  4 | 李四   | NULL            |         NULL |       NULL |
|  5 | 111    | NULL            |         NULL |       NULL |
| 12 | fox    | NULL            |         NULL |       NULL |
| 14 | AAA    | NULL            |         NULL |       NULL |
| 15 | BBB    | NULL            |         NULL |       NULL |
| 16 | CCC    | NULL            |         NULL |       NULL |
| 20 | DDD    | NULL            |         NULL |       NULL |
| 22 | EEE    | NULL            |         NULL |       NULL |
+----+--------+-----------------+--------------+------------+
10 rows in set (0.00 sec)mysql> delete from user where id=20;
Query OK, 1 row affected (0.19 sec)mysql> select * from user;
+----+--------+-----------------+--------------+------------+
| id | name   | address         | last_updated | is_deleted |
+----+--------+-----------------+--------------+------------+
|  2 | 張三   | 廣州白云山      |   1691563465 |          0 |
|  3 | fox    | NULL            |         NULL |       NULL |
|  4 | 李四   | NULL            |         NULL |       NULL |
|  5 | 111    | NULL            |         NULL |       NULL |
| 12 | fox    | NULL            |         NULL |       NULL |
| 14 | AAA    | NULL            |         NULL |       NULL |
| 15 | BBB    | NULL            |         NULL |       NULL |
| 16 | CCC    | NULL            |         NULL |       NULL |
| 22 | EEE    | NULL            |         NULL |       NULL |
| 23 | 333    | NULL            |         NULL |       NULL |
+----+--------+-----------------+--------------+------------+
10 rows in set (0.00 sec)mysql> delete from user where id=22;
Query OK, 1 row affected (0.29 sec)mysql> select * from user;
+----+--------+-----------------+--------------+------------+
| id | name   | address         | last_updated | is_deleted |
+----+--------+-----------------+--------------+------------+
|  2 | 張三   | 廣州白云山      |   1691563465 |          0 |
|  3 | fox    | NULL            |         NULL |       NULL |
|  4 | 李四   | NULL            |         NULL |       NULL |
|  5 | 111    | NULL            |         NULL |       NULL |
| 12 | fox    | NULL            |         NULL |       NULL |
| 14 | AAA    | NULL            |         NULL |       NULL |
| 15 | BBB    | NULL            |         NULL |       NULL |
| 16 | CCC    | NULL            |         NULL |       NULL |
| 23 | 333    | NULL            |         NULL |       NULL |
+----+--------+-----------------+--------------+------------+
9 rows in set (0.00 sec)
  1. 借助Navicat將主庫user表復制到從庫,包括數據。 這樣一來從庫就有了當前的全量數據,只需要跳過錯誤的gtid,從最新的gtid開始執行就可以了。
# 查詢從庫,確定數據是否和主庫一致
mysql> select * from user;
+----+--------+-----------------+--------------+------------+
| id | name   | address         | last_updated | is_deleted |
+----+--------+-----------------+--------------+------------+
|  2 | 張三   | 廣州白云山      |   1691563465 |          0 |
|  3 | fox    | NULL            |         NULL |       NULL |
|  4 | 李四   | NULL            |         NULL |       NULL |
|  5 | 111    | NULL            |         NULL |       NULL |
| 12 | fox    | NULL            |         NULL |       NULL |
| 14 | AAA    | NULL            |         NULL |       NULL |
| 15 | BBB    | NULL            |         NULL |       NULL |
| 16 | CCC    | NULL            |         NULL |       NULL |
| 23 | 333    | NULL            |         NULL |       NULL |
+----+--------+-----------------+--------------+------------+
9 rows in set (0.00 sec)
  1. 獲取從庫需要配置跳過錯誤的事務
# 獲取到錯誤的gtid為d58e6bad-ef3e-11ee-8285-0242ac120003:12,
# 因為連續刪除過兩條數據,所以d58e6bad-ef3e-11ee-8285-0242ac120003:13也是錯誤的事務
mysql> show replica status\G;
*************************** 1. row ***************************Replica_IO_State: Waiting for source to send eventSource_Host: 192.168.65.223Source_User: foxSource_Port: 3308Connect_Retry: 30Source_Log_File: mysql-bin.000004Read_Source_Log_Pos: 4229Relay_Log_File: 3c946c781110-relay-bin.000012Relay_Log_Pos: 790Relay_Source_Log_File: mysql-bin.000004Replica_IO_Running: YesReplica_SQL_Running: NoReplicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1146Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'd58e6bad-ef3e-11ee-8285-0242ac120003:12' at master log mysql-bin.000004, end_log_pos 3906. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.Skip_Counter: 0Exec_Source_Log_Pos: 3645Relay_Log_Space: 1932Until_Condition: NoneUntil_Log_File: Until_Log_Pos: 0Source_SSL_Allowed: NoSource_SSL_CA_File: Source_SSL_CA_Path: Source_SSL_Cert: Source_SSL_Cipher: Source_SSL_Key: Seconds_Behind_Source: NULL
Source_SSL_Verify_Server_Cert: NoLast_IO_Errno: 0Last_IO_Error: Last_SQL_Errno: 1146Last_SQL_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'd58e6bad-ef3e-11ee-8285-0242ac120003:12' at master log mysql-bin.000004, end_log_pos 3906. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.Replicate_Ignore_Server_Ids: Source_Server_Id: 11Source_UUID: d58e6bad-ef3e-11ee-8285-0242ac120003Source_Info_File: mysql.slave_master_infoSQL_Delay: 0SQL_Remaining_Delay: NULLReplica_SQL_Running_State: Source_Retry_Count: 86400Source_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: 240401 14:17:40Source_SSL_Crl: Source_SSL_Crlpath: Retrieved_Gtid_Set: d58e6bad-ef3e-11ee-8285-0242ac120003:1-13,
dfb1f706-ef3e-11ee-82cc-0242ac120004:2Executed_Gtid_Set: ce6baf4a-ef3e-11ee-99fb-0242ac120002:1-13,
d58e6bad-ef3e-11ee-8285-0242ac120003:1-11,
dfb1f706-ef3e-11ee-82cc-0242ac120004:1-2Auto_Position: 1Replicate_Rewrite_DB: Channel_Name: Source_TLS_Version: Source_public_key_path: Get_Source_public_key: 0Network_Namespace: 
1 row in set (0.00 sec)
  1. 在主庫再次執行刪除操作
mysql> delete from user where id=12;
Query OK, 1 row affected (0.11 sec)mysql> select * from user;
+----+--------+-----------------+--------------+------------+
| id | name   | address         | last_updated | is_deleted |
+----+--------+-----------------+--------------+------------+
|  2 | 張三   | 廣州白云山      |   1691563465 |          0 |
|  3 | fox    | NULL            |         NULL |       NULL |
|  4 | 李四   | NULL            |         NULL |       NULL |
|  5 | 111    | NULL            |         NULL |       NULL |
| 14 | AAA    | NULL            |         NULL |       NULL |
| 15 | BBB    | NULL            |         NULL |       NULL |
| 16 | CCC    | NULL            |         NULL |       NULL |
| 23 | 333    | NULL            |         NULL |       NULL |
+----+--------+-----------------+--------------+------------+

6.在從庫

mysql> select * from user;
+----+--------+-----------------+--------------+------------+
| id | name   | address         | last_updated | is_deleted |
+----+--------+-----------------+--------------+------------+
|  2 | 張三   | 廣州白云山      |   1691563465 |          0 |
|  3 | fox    | NULL            |         NULL |       NULL |
|  4 | 李四   | NULL            |         NULL |       NULL |
|  5 | 111    | NULL            |         NULL |       NULL |
| 12 | fox    | NULL            |         NULL |       NULL |
| 14 | AAA    | NULL            |         NULL |       NULL |
| 15 | BBB    | NULL            |         NULL |       NULL |
| 16 | CCC    | NULL            |         NULL |       NULL |
| 23 | 333    | NULL            |         NULL |       NULL |
+----+--------+-----------------+--------------+------------+
9 rows in set (0.00 sec)mysql> stop replica;
Query OK, 0 rows affected (0.07 sec)
# 如果需要一次跳過多條,找出需要跳過的gtid,批量執行
mysql> SET @@SESSION.GTID_NEXT= 'd58e6bad-ef3e-11ee-8285-0242ac120003:12';begin;commit;
Query OK, 0 rows affected (0.00 sec)Query OK, 0 rows affected (0.00 sec)Query OK, 0 rows affected (0.12 sec)mysql> SET @@SESSION.GTID_NEXT= 'd58e6bad-ef3e-11ee-8285-0242ac120003:13';begin;commit;
Query OK, 0 rows affected (0.00 sec)Query OK, 0 rows affected (0.00 sec)Query OK, 0 rows affected (0.00 sec)mysql> SET SESSION GTID_NEXT = AUTOMATIC;
Query OK, 0 rows affected (0.00 sec)mysql> start replica;
Query OK, 0 rows affected (1.10 sec)mysql> select * from user;
+----+--------+-----------------+--------------+------------+
| id | name   | address         | last_updated | is_deleted |
+----+--------+-----------------+--------------+------------+
|  2 | 張三   | 廣州白云山      |   1691563465 |          0 |
|  3 | fox    | NULL            |         NULL |       NULL |
|  4 | 李四   | NULL            |         NULL |       NULL |
|  5 | 111    | NULL            |         NULL |       NULL |
| 14 | AAA    | NULL            |         NULL |       NULL |
| 15 | BBB    | NULL            |         NULL |       NULL |
| 16 | CCC    | NULL            |         NULL |       NULL |
| 23 | 333    | NULL            |         NULL |       NULL |
+----+--------+-----------------+--------------+------------+
8 rows in set (0.01 sec)

組復制(Group Replication)
文檔:https://dev.mysql.com/doc/refman/8.0/en/group-replication.html
什么是組復制
由于傳統異步復制的缺陷,可能會導致主從數據不一致的問題,在主節點異常宕機時從節點可能造成數據丟失。基于這個缺陷,Mysql5.7.17推出了一個高可用與高擴展的解決方案Mysql Group Replication(簡稱MGR),將原有的gtid復制功能進行了增強,支持單主模式和多主模式。組復制在數據庫層面上做到了只要集群中大多數主機可用,則服務可用,也就是說3臺服務器的集群,允許其中1臺宕機。
在這里插入圖片描述

Group Replication提供了分布式狀態機復制,服務器之間具有很強的協調性。當服務器屬于同一組時,它們會自動進行協調。該組可以在具有自動選主的單主模式下運行,在這種模式下,一次只有一臺服務器接受更新。或者,對于更高級的用戶,可以在多主模式下部署該組,其中所有服務器都可以接受更新,即使它們是并發執行的。
與傳統復制相比,Group Replication有以下大幅改進:

  • 傳統復制的主從復制方式有一個主和不等數量的從。主節點執行的事務會異步發送給從節點,在從節點重新執行。而Group Replication采用整組寫入的方式,避免了單點爭用。
  • Group Replication在傳輸數據時使用了Paxos協議。Paxos協議保證了數據傳輸的一致性和原子性。基于Paxos協議,Group Replication構建了一個分布式的狀態復制機制,這是實現多主復制的核心技術。
  • Group Replication提供了多寫方案,為多活方案帶來了實現的可能。
    MGR 能保證數據庫服務的連續可用,卻無法處理如下問題:當一個組成員變為不可用時,連接到它的客戶端必須被重定向或故障轉移到其他組成員。此時需要使用連接器、負載均衡器、路由器或某種形式的中間件,例如 MySQL Router 8.0 。MGR 本身不提供這些工具。此時便引入了 InnoDB Cluster ,后面詳述。
    單主模式
    組中的每個MySQL服務器實例都可以在獨立的物理主機上運行,這是部署組復制的推薦方式。
    在單主模式下(group_replication_single_primary_mode=ON),組中只有一個主服務器,該主服務器被設置為讀寫模式。組中的所有其他成員都被設置為只讀模式(super_read_only=ON)。
    在這里插入圖片描述

查找primary的兩種方式

# 方式1:查詢performance_schema.replication_group_members的MEMBER_ROLE列
mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;
+-------------------------+-------------+
| MEMBER_HOST             | MEMBER_ROLE |
+-------------------------+-------------+
| remote1.example.com     | PRIMARY     |
| remote2.example.com     | SECONDARY   |
| remote3.example.com     | SECONDARY   |
+-------------------------+-------------+
# 方式2:查看group_replication_primary_member變量狀態
mysql> SHOW STATUS LIKE 'group_replication_primary_member';

單主模式部署示例
文檔:https://dev.mysql.com/doc/refman/8.0/en/group-replication-configuring-instances.html

  1. 部署三個MySQL Server實例
    可以利用docker快速部署3個MySQL實例
    角色server_idDB Port內部通信mgr-node1(primary)13321>3306mgr-node1:33061mgr-node2(Secondary)23322>3306mgr-node2:33061mgr-node3(Secondary)33323>3306mgr-node3:33061
# 創建組復制的網絡  保證三個mysql容器之間可以通過容器名訪問
docker network create --driver bridge mgr-network
mkdir -p /mysql/mgr/node1/data /mysql/mgr/node1/conf /mysql/mgr/node1/log
mkdir -p /mysql/mgr/node2/data /mysql/mgr/node2/conf /mysql/mgr/node2/log
mkdir -p /mysql/mgr/node3/data /mysql/mgr/node3/conf /mysql/mgr/node3/log#運行mysql容器
docker run  -d  \
--name mgr-node1 \
--privileged=true \
--restart=always \
--network  mgr-network \
-p 3321:3306 \
-v /mysql/mgr/node1/data:/var/lib/mysql \
-v /mysql/mgr/node1/conf:/etc/mysql/conf.d  \
-v /mysql/mgr/node1/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1  docker run  -d  \
--name mgr-node2 \
--privileged=true \
--restart=always \
--network  mgr-network \
-p 3322:3306 \
-v /mysql/mgr/node2/data:/var/lib/mysql \
-v /mysql/mgr/node2/conf:/etc/mysql/conf.d  \
-v /mysql/mgr/node2/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1  docker run  -d  \
--name mgr-node3 \
--privileged=true \
--restart=always \
--network  mgr-network \
-p 3323:3306 \
-v /mysql/mgr/node3/data:/var/lib/mysql \
-v /mysql/mgr/node3/conf:/etc/mysql/conf.d  \
-v /mysql/mgr/node3/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1  

2)配置組復制實例
以mgr-node1配置為例,創建/mysql/mgr/node1/conf/custom.cnf,添加以下配置:

[mysql]
# 設置mysql客戶端默認編碼
default-character-set=utf8[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysqlsecure-file-priv= NULL# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0#對于Group Replication,數據必須存儲在InnoDB事務存儲引擎中
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"#指定sever_id,三個Mysql實例需要分別改為對應的sever_id
server_id=1
# 必須開啟GTID支持
gtid_mode=ON
enforce_gtid_consistency=ON# 啟用二進制日志
log-bin=mysql-bin#組復制設置
#實例啟動時會將組復制插件加載到插件列表中
plugin_load_add='group_replication.so'
# 組名三個節點必須保證一致,必須是UUID,可以使用 SELECT UUID()生成一個
group_replication_group_name="117dc7ea-b9bd-11ee-9bdb-0242ac120002"
# 插件在服務器啟動時不自動啟動,可以等配置好服務器之后手動啟動
group_replication_start_on_boot=off
# 配置與組內其他成員通信是使用的主機名和端口,內部通訊端口,推薦使用 33061
group_replication_local_address= "mgr-node1:33061"
# 設置組成員的主機名和端口
group_replication_group_seeds= "mgr-node1:33061,mgr-node2:33061,mgr-node3:33061"
# 通常會在實例運行時配置group_replication_bootstrap_group,以確保只有一個成員實際引導組
group_replication_bootstrap_group=off# 最大連接數 
max_connections=1000# 設置默認時區
default-time_zone='+8:00'# 0:區分大小寫
# 1:不區分大小寫
lower_case_table_names=1!includedir /etc/mysql/conf.d/

mgr-node2和mgr-node3同上,注意配置文件路徑和修改server_id和group_replication_local_address
在這里插入圖片描述

3)配置用于分布式恢復的用戶憑證
mgr-node1上配置
3.1)重新啟動mysql實例

docker restart mgr-node1 mgr-node2 mgr-node3

3.2)禁用二進制日志記錄,以便在每個實例上分別創建復制用戶

# mgr-node1為例
[root@192-168-65-185 ~]# docker exec -it mgr-node1 /bin/bash
root@0955d5f390c6:/# mysql -uroot -p123456
mysql> SET SQL_LOG_BIN=0;

3.3) 創建MySQL用戶,授予所需的權限

mysql>  CREATE USER fox@'%' IDENTIFIED  BY '123456';GRANT REPLICATION SLAVE ON *.* TO fox@'%';GRANT CONNECTION_ADMIN ON *.* TO fox@'%';GRANT BACKUP_ADMIN ON *.* TO fox@'%';GRANT GROUP_REPLICATION_STREAM ON *.* TO fox@'%';FLUSH PRIVILEGES;

3.4)如果前面禁用了二進制日志,再次啟用二進制日志

mysql> SET SQL_LOG_BIN=1;

3.5)創建復制用戶后,必須向服務器提供用于分布式恢復的用戶憑據。

  • 使用CHANGE REPLICATION SOURCE TO | CHANGE MASTER TO設置的用戶憑據以明文形式存儲在服務器上的復制元數據存儲庫中。當組復制啟動時,它們將被應用,包括當系統變量group_replication_start_on_boot設置為ON時自動啟動。
  • 在START GROUP_REPLICATION上指定的用戶憑據僅保存在內存中,并通過STOP GROUP_REPLICATION語句或服務器關閉來刪除。必須發出START GROUP_REPLICATION語句來再次提供憑據,因此無法使用這些憑據自動啟動Group Replication。這種指定用戶憑據的方法有助于保護組復制服務器免受未經授權的訪問。此功能從MySQL 8.0.21開始支持。
#from MySQL 8.0.23:
mysql> CHANGE REPLICATION SOURCE TO SOURCE_USER='fox', SOURCE_PASSWORD='123456' FOR CHANNEL 'group_replication_recovery';

注意:如果不想配置ssl,可以配置參數group_replication_recovery_public_key_path=ON來在請求復制用戶密鑰時給公鑰

#實例加入集群需要獲取公鑰
set global group_replication_recovery_get_public_key=on;
  1. 指定mgr-node1引導組(primary節點),啟用組復制
    為了安全地引導組,連接到mgr-node1并執行以下語句:
#mgr-node1啟動組復制,并且作為primary
mysql> SET GLOBAL group_replication_bootstrap_group=ON;START GROUP_REPLICATION;SET GLOBAL group_replication_bootstrap_group=OFF;
# 查詢組成員信息,mgr-node1是否是primary               
mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;           

在這里插入圖片描述

為了驗證后續其他節點入組情況,下面將創建一個表并向其中添加一些數據進行驗證。

CREATE DATABASE IF NOT EXISTS test;
USE test; 
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
INSERT INTO t1 VALUES (1, 'Fox');

5)向組中添加實例mgr-node2和mgr-node3

#添加復制用戶
SET SQL_LOG_BIN=0;
CREATE USER fox@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO fox@'%';
GRANT CONNECTION_ADMIN ON *.* TO fox@'%';
GRANT BACKUP_ADMIN ON *.* TO fox@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO fox@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
CHANGE REPLICATION SOURCE TO SOURCE_USER='fox', SOURCE_PASSWORD='123456' FOR CHANNEL 'group_replication_recovery';
set global group_replication_recovery_get_public_key=on;# mgr-node2和mgr-node3啟用主復制
mysql> START GROUP_REPLICATION;
# 查詢組成員信息
mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;

確認數據同步情況
在這里插入圖片描述

SELECT * FROM test.user;

在這里插入圖片描述

如果數據沒有正常同步,可以通過docker log -f mgr-node2排查問題

多主模式

文檔:https://dev.mysql.com/doc/refman/8.0/en/group-replication-multi-primary-mode.html
在多主模式下(group_replication_single_primary_mode=OFF),沒有成員具有特殊的角色。任何與其他組成員兼容的成員在加入組時都被設置為讀寫模式,并且可以處理寫事務,即使它們是并發發布的。
如果一個成員停止接受寫事務,例如,在服務器意外退出的情況下,連接到它的客戶端可以被重定向或故障轉移到處于讀寫模式的任何其他成員。Group Replication本身不處理客戶端故障轉移,因此需要使用中間件框架(如MySQL Router 8.0)、代理、連接器或應用程序本身來安排。
在這里插入圖片描述

多主模式部署示例

在前面單主模式基礎上改為多主模式
1)三個節點都關閉單主模式
三個節點都執行如下操作

# 停止組復制
stop GROUP_REPLICATION;# 關閉單主模式
set global group_replication_single_primary_mode=off; # 開啟多主一致性檢查
set global group_replication_enforce_update_everywhere_checks=ON; 

在這里插入圖片描述

2)選擇mgr-node1引導組復制

# 開啟組復制引導
set global group_replication_bootstrap_group=on;  
# 開啟組復制
start group_replication;  # 關閉組復制引導
set global group_replication_bootstrap_group=off;

在這里插入圖片描述

3)mgr-node2和mgr-node3開啟組復制

# 開啟組復制
start group_replication;  # 查看到添加到組復制集群的服務器信息
select * from performance_schema.replication_group_members;

在這里插入圖片描述

  1. 測試
  • mgr-node1插入數據,看mgr-node2是否可以查到
#mgr-node1
INSERT INTO test.t1 VALUES (2, 'aaa');
#mgr-node2
select * from test.t1;

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95714.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95714.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95714.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[UT]記錄case中seq.start(sequencer)的位置變化帶來的執行行為的變化

現象: 代碼選擇打開57行,注釋掉60行執行,結果58行不會打印。 代碼選擇打開60行,注釋57行執行,結果58行正常打印。 sequence的執行需要時間!!! SV中代碼57行切換到60行的區別&#xf…

利用keytool實現https協議(生成自簽名證書)

利用keytool實現https協議(生成自簽名證書)什么是https協議?https(安全超文本傳輸協議)是 HTTP 的安全版本,通過 SSL/TLS 加密技術,在客戶端(如瀏覽器)和服務器之間建立加…

拆解 AI 大模型 “思考” 邏輯:從參數訓練到語義理解的核心鏈路

一、引言:揭開 AI 大模型 “思考” 的神秘面紗?日常生活中的 AI 大模型 “思考” 場景呈現(如 ChatGPT 對話、AI 寫作輔助、智能客服應答)?提出核心問題:看似具備 “思考” 能力的 AI 大模型,其背后的運作邏輯究竟是…

element plus 使用細節 (二)

接上一篇文章: element plus 使用細節 最近菜鳥忙于系統開發,都沒時間總結項目中使用的問題,幸好還是在空閑之余總結了一點(后續也會來補充),希望能給大家帶來幫助! 文章目錄table fixed 的 v…

【機器學習學習筆記】numpy基礎2

零基礎小白的 NumPy 入門指南如果你想用電競(打游戲)的思路理解編程:Python 是基礎操作鍵位,而 NumPy 就是 “英雄專屬技能包”—— 專門幫你搞定 “數值計算” 這類復雜任務,比如算游戲里的傷害公式、地圖坐標&#x…

從自動化到智能化:家具廠智能化產線需求與解決方案解析

伴隨著工業4.0浪潮和智能制造技術的成熟,家具行業正逐步從傳統的自動化生產邁向智能化生產。智能化產線的構建不僅可以提升生產效率,還能滿足個性化定制和柔性制造的需求。本文以某家具廠為例,詳細解析智能化產線的核心需求,并提出…

macOS下基于Qt/C++的OpenGL開發環境的搭建

系統配置 MacBook Pro 2015 Intel macOS 12Xcode 14 Qt開發環境搭建 Qt Creator的下載與安裝 在Qt官網的下載頁面上下載,即Download Qt Online Installer for macOS。下載完成就得到一個文件名類似于qt-online-installer-macOS-x64-x.y.z.dmg的安裝包。 下一步 …

當液態玻璃計劃遭遇反叛者:一場 iOS 26 界面的暗戰

引子 在硅谷的地下代碼俱樂部里,流傳著一個關于 “液態玻璃” 的傳說 —— 那是 Apple 秘密研發的界面改造計劃,如同電影《變臉》中那張能改變命運的面具,一旦啟用,所有 App 都將被迫換上流光溢彩的新面孔。 而今天,我…

探究Linux系統的SSL/TLS證書機制

一、SSL/TLS證書的基本概念 1.1 SSL/TLS協議簡介 SSL/TLS是一種加密協議,旨在為網絡通信提供機密性、完整性和身份驗證。它廣泛應用于HTTPS網站、電子郵件服務、VPN以及其他需要安全通信的場景。SSL(安全套接字層)是TLS(傳輸層安全…

python和java爬蟲優劣對比

Python和Java作為爬蟲開發的兩大主流語言,核心差異源于語法特性、生態工具鏈、性能表現的不同,其優勢與劣勢需結合具體場景(如開發效率、爬取規模、反爬復雜度)判斷。以下從 優勢、劣勢、適用場景 三個維度展開對比,幫…

Unity 槍械紅點瞄準器計算

今天突然別人問我紅點瞄準器在鏡子上如何計算,之前的吃雞項目做過不記得,今天寫個小用例整理下。 主體思想記得是目標位置到眼睛穿過紅點瞄準器獲取當前點的位置就可以。應該是這樣吧,:) 武器測試結構 首先整個結構&am…

題解 洛谷P13778 「o.OI R2」=+#-

文章目錄題解代碼居然沒有題解?我來寫一下我的抽象做法。 題解 手玩一下,隨便畫個他信心的折線圖,如下: 可以發現,如果我們知道終止節點,那么我們就可以知道中間有多少個上升長度。(因為它只能…

RTSP流端口占用詳解:TCP模式與UDP模式的對比

在音視頻傳輸協議中,RTSP(Real-Time Streaming Protocol,實時流傳輸協議)被廣泛用于點播、直播、監控等場景。開發者在實際部署或調試時,常常會遇到一個問題:一路 RTSP 流到底占用多少個端口? 這…

websocket的key和accept分別是多少個字節

WebSocket的Sec-WebSocket-Key是24字節(192位)的Base64編碼字符串,解碼后為16字節(128位)的原始隨機數據;Sec-WebSocket-Accept是28字節(224位)的Base64編碼字符串,解碼后…

單片機開發----一個簡單的Boot

文章目錄一、設計思路**整體框架設計****各文件/模塊功能解析**1. main.c(主程序入口,核心控制)2. 隱含的核心模塊(框架中未展示但必備)**設計亮點**二、代碼bootloader.hbootloader.cflash.cmain.c一、設計思路 整體…

Day2p2 夏暮客的Python之路

day2p2 The Hard Way to learn Python 文章目錄day2p2 The Hard Way to learn Python前言一、提問和提示1.1 關于raw_input()1.2 關于input()二、參數、解包、變量2.1 解讀參數2.2 解讀解包2.3 解讀變量2.4 實例2.5 模塊和功能2.6 練習前言 author:SummerEnd date…

【C++設計模式】第二篇:策略模式(Strategy)--從基本介紹,內部原理、應用場景、使用方法,常見問題和解決方案進行深度解析

C設計模式系列文章目錄 【第一篇】C單例模式–懶漢與餓漢以及線程安全 【C設計模式】第二篇:策略模式(Strategy)--從基本介紹,內部原理、應用場景、使用方法,常見問題和解決方案進行深度解析一、策略模式的基本介紹1.…

四十歲編程:熱愛、沉淀與行業的真相-優雅草卓伊凡

四十歲編程:熱愛、沉淀與行業的真相-優雅草卓伊凡今日卓伊凡收到一個問題:「如何看待40歲還在擼代碼的程序員?」這讓我不禁思考:從何時起,年齡成了程序員職業中的敏感詞?在互聯網的某些角落,彌漫…

pycharm解釋器使用anaconda建立的虛擬環境里面的python,無需系統里面安裝python。

Anaconda建立的虛擬環境可以在虛擬環境里設置任何的python版本,pycharm解釋器使用anaconda建立的虛擬環境里面的python,比如anaconda建立的虛擬環境1、虛擬環境2,pycharm解釋器使用anaconda建立虛擬環境1也可以使用虛擬環境2,根本…

機器學習:后篇

目錄 一、KNN算法-分類 樣本距離 KNN算法原理 缺點 API 二、模型選擇與調優 交叉驗證 保留交叉驗證(HoldOut) k-折交叉驗證(K-fold) 分層k-折交叉驗證(Stratified k-fold) 其他交叉驗證 三、樸素貝葉斯-分類 理論介紹 拉普拉斯平滑系數 API 四、決策樹-分類 理論…