MySQL高可用-使用Docker部署MGR

MySQL 的高可用方案很多,有 MHA、MGR、MySQL InnoDB Cluster 等。起初想學(xué)習(xí)下 MHA,了解后發(fā)現(xiàn) MHA 在 Github 上的倉庫已經(jīng)沒有更新了。不過 MHA 的兼容性比較好,有老版本的 MySQL 需要做高可用,還是值得一試的。
本文介紹下 MGR 的部署。
MGR (MySQL Group Replication) 是 MySQL 5.7.17 提出的,既可以很好的保證數(shù)據(jù)一致性又可以自動(dòng)切換,具備故障檢測功能、支持多節(jié)點(diǎn)寫入。是以插件的形式提供,可以靈活部署。
MySQL MGR 集群是多個(gè) MysQL Server 節(jié)點(diǎn)共同組成的分布式集群,每個(gè) Server 都有完整的副本,它是基于 ROW 格式的二進(jìn)制日志文件和 GTID 特性來實(shí)現(xiàn)的。
MGR 的優(yōu)點(diǎn)
- 強(qiáng)一致性:基于原生復(fù)制及 Paxos 協(xié)議的組復(fù)制技術(shù)(以插件形式提供),確保數(shù)據(jù)的嚴(yán)格一致性。
 - 高容錯(cuò)性:在少數(shù)節(jié)點(diǎn)故障時(shí)仍可正常運(yùn)行,具備自動(dòng)故障檢測機(jī)制。節(jié)點(diǎn)間資源沖突采用無鎖設(shè)計(jì)(如先到者優(yōu)先)處理,避免錯(cuò)誤。
 - 高擴(kuò)展性:支持節(jié)點(diǎn)動(dòng)態(tài)自動(dòng)加入與移除。新節(jié)點(diǎn)加入后自動(dòng)同步數(shù)據(jù)至一致狀態(tài);節(jié)點(diǎn)移除后,集群自動(dòng)更新并維護(hù)組配置信息。
 - 高靈活性:支持單主模式與多主模式。單主模式下自動(dòng)選舉主節(jié)點(diǎn),所有寫操作路由至主節(jié)點(diǎn);多主模式下,所有節(jié)點(diǎn)均可并發(fā)處理寫操作。
 
MGR 的一些限制
- 僅支持 InnoDB 表,并且每個(gè)表一定要有一個(gè)主鍵。
 - 必須打開 GTID 特性,二進(jìn)制日志格式必須設(shè)置為 ROW 。
 - MGR 不支持大事務(wù)。
 - 僅支持 IPv4 網(wǎng)絡(luò),組大小限制為最少3個(gè)節(jié)點(diǎn)、最多9個(gè)節(jié)點(diǎn)。
 - 不支持外鍵。
 - 二進(jìn)制日志不支持 Binlog Event Checksum 。
 - 所有節(jié)點(diǎn) server_id 和 server_uuid 需唯一。
 
部署前準(zhǔn)備
- 為了方便,mysql 的主節(jié)點(diǎn)和從節(jié)點(diǎn)在一臺虛擬機(jī)中進(jìn)行測試
 - docker 版本:20.10.0
 - docker compose 版本:2.26.2
 - mysql 版本:8.0.39
 
開始部署
采用 docker-compose 的方式進(jìn)行部署,部署目錄 mysql-mgr 的文件結(jié)構(gòu)如下圖:

- docker-compose.yml:容器編排文件,配置一個(gè)主和兩個(gè)從的 mysql 節(jié)點(diǎn)。
 - mysql-config:此目錄中是主和從的 mysql 配置文件
 - mysql-init-srcipts:此目錄中是 mysql 啟動(dòng)時(shí)需要?jiǎng)?chuàng)建復(fù)制賬號以及安裝 mgr 插件
 - start-mgr.sh:三個(gè) mysql 節(jié)點(diǎn)啟動(dòng)后,執(zhí)行該文件進(jìn)行 mgr 集群的啟動(dòng)。
 
將 mysql-mgr 拷貝到服務(wù)器,進(jìn)入 mysql-mgr 目錄執(zhí)行 docker-compose up -d 。
等三個(gè) mysql 節(jié)點(diǎn)的狀態(tài)正常,執(zhí)行 ./start-mgr.sh ,成功執(zhí)行如下圖所示。

進(jìn)入任意一個(gè) mysql 中,執(zhí)行 SELECT * FROM performance_schema.replication_group_members; ,三個(gè)節(jié)點(diǎn)的 MEMBER_STATE 為 ONLINE 說明部署成功。

現(xiàn)在可以連上主節(jié)點(diǎn),進(jìn)行庫表和數(shù)據(jù)的創(chuàng)建,來驗(yàn)證是否正常同步到從節(jié)點(diǎn)。
關(guān)鍵點(diǎn)說明
docker-compose 配置
docker-compose.yml 文件定義了三個(gè) MySQL 節(jié)點(diǎn),分別是一個(gè)主節(jié)點(diǎn)和兩個(gè)從節(jié)點(diǎn):
version: '3.8'
services:
  mysql-master:
    image: mysql:8.0.39
    container_name: mysql-master
    hostname: mysql-master
    command: ["mysqld"]
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: mydb
    volumes:
      - mysql-master-data:/var/lib/mysql
      - ./mysql-init-scripts:/docker-entrypoint-initdb.d
      - ./mysql-config/mysql-master.cnf:/etc/mysql/conf.d/mysql-master.cnf
    ports:
      - "3306:3306"
      - "33061:33061"
    networks:
      - mysql-mgr-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
      interval: 10s
      timeout: 5s
      retries: 5
  mysql-slave-1:
    image: mysql:8.0.39
    container_name: mysql-slave-1
    hostname: mysql-slave-1
    command: ["mysqld"]
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: mydb
    volumes:
      - mysql-slave-1-data:/var/lib/mysql
      - ./mysql-init-scripts:/docker-entrypoint-initdb.d
      - ./mysql-config/mysql-slave-1.cnf:/etc/mysql/conf.d/mysql-slave-1.cnf
    ports:
      - "3307:3306"
      - "33062:33061"
    networks:
      - mysql-mgr-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
      interval: 10s
      timeout: 5s
      retries: 5
    depends_on:
      - mysql-master
  mysql-slave-2:
    image: mysql:8.0.39
    container_name: mysql-slave-2
    hostname: mysql-slave-2
    command: ["mysqld"]
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: mydb
    volumes:
      - mysql-slave-2-data:/var/lib/mysql
      - ./mysql-init-scripts:/docker-entrypoint-initdb.d
      - ./mysql-config/mysql-slave-2.cnf:/etc/mysql/conf.d/mysql-slave-2.cnf
    ports:
      - "3308:3306"
      - "33063:33061"
    networks:
      - mysql-mgr-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
      interval: 10s
      timeout: 5s
      retries: 5
    depends_on:
      - mysql-master
networks:
  mysql-mgr-network:
    driver: bridge
volumes:
  mysql-master-data:
  mysql-slave-1-data:
  mysql-slave-2-data:卷掛載,以主節(jié)點(diǎn)為例:
- 數(shù)據(jù)持久化:
mysql-master-data:/var/lib/mysql - 初始化腳本:
./mysql-init-scripts:/docker-entrypoint-initdb.d - 配置文件:
./mysql-config/mysql-master.cnf:/etc/mysql/conf.d/mysql-master.cnf 
端口映射:
- MySQL 節(jié)點(diǎn)端口:
3306:3306(主節(jié)點(diǎn)),3307:3306(從節(jié)點(diǎn)1),3308:3306(從節(jié)點(diǎn)2) - MGR 通信端口:
33061:33061(主節(jié)點(diǎn)),33062:33061(從節(jié)點(diǎn)1),33063:33061(從節(jié)點(diǎn)2) 
網(wǎng)絡(luò)配置
- 所有節(jié)點(diǎn)都連接到同一個(gè)網(wǎng)絡(luò) 
mysql-mgr-network,確保節(jié)點(diǎn)間可以相互通信 
MySQL 的配置文件
[mysqld]
server-id=1
log-bin=mysql-bin-1.log
binlog-format=ROW
gtid-mode=ON
enforce-gtid-cnotallow=ON
log-slave-updates=ON
binlog-checksum=NONE
master-info-repository=TABLE
relay-log-info-repository=TABLE
transaction-write-set-extractinotallow=XXHASH64- server-id:每個(gè)節(jié)點(diǎn)的必須唯一。
 - binglog-format:行級 binlog,MGR 硬性要求 ROW 格式。
 - gtid-mode:啟用 GTID(全局事務(wù)標(biāo)識),MGR 內(nèi)部完全基于 GTID 做事務(wù)認(rèn)證和沖突檢測。
 - enforce-gtid-consistency:禁止任何會破壞 GTID 一致性的語句(如 
CREATE TEMPORARY TABLE與CREATE TABLE ... SELECT),MGR 強(qiáng)制要求打開,否則無法啟動(dòng)組復(fù)制。 - binlog-checksum:MySQL 8.0.20 之前的 MGR 要求關(guān)閉 binlog 校驗(yàn)和(因?yàn)樵缙诮M通信層不支持),8.0.20 及以后可以改為 
CRC32,保持默認(rèn)即可,但舊版本必須顯式設(shè)置為NONE。 
這些參數(shù)共同確保節(jié)點(diǎn)開啟 binlog、使用 ROW + GTID、寫入復(fù)制元數(shù)據(jù)到 InnoDB 表,并為 MGR 提供事務(wù)寫集合,從而滿足組復(fù)制的所有前置條件。
除此之外,還需要進(jìn)行 MGR 的組復(fù)制配置,如下:
loose-group-replication-group-name=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
loose-group-replication-start-on-boot=OFF
loose-group-replication-local-address=mysql-master:33061
loose-group-replication-group-seeds=mysql-master:33061,mysql-slave-1:33061,mysql-slave-2:33061
loose-group-replication-bootstrap-group=OFF
loose-group-replication-single-primary-mode=ON
loose-group-replication-enforce-update-everywhere-checks=OFF- 給整個(gè) MGR 組起一個(gè)全局唯一的名字(UUID 格式),三個(gè)節(jié)點(diǎn)相同。
 - MySQL 實(shí)例啟動(dòng)時(shí)不自動(dòng)啟動(dòng) Group Replication 插件,這個(gè)參數(shù)如果設(shè)置為 OFF,當(dāng)壞掉的節(jié)點(diǎn)修復(fù)后,不會自動(dòng)加入集群。
 - 本節(jié)點(diǎn)在組內(nèi)通信時(shí)使用的本地監(jiān)聽地址(IP 或主機(jī)名:端口)。
 - 種子列表,告訴當(dāng)前節(jié)點(diǎn)“初次加入組時(shí)可以去找誰”。只要列表中的任意一個(gè)節(jié)點(diǎn)在線,新節(jié)點(diǎn)就能拿到完整的成員信息并加入組。通常把所有成員都寫上,方便任何順序啟動(dòng)。
 - 只在第一個(gè)節(jié)點(diǎn)第一次啟動(dòng)時(shí)設(shè)為 ON,用來創(chuàng)建組;之后必須立即改回 OFF 并重啟,否則會出現(xiàn)“腦裂”或成員沖突。
 - 打開單主模式(Single-Primary),組內(nèi)只有一臺節(jié)點(diǎn)可寫(primary),其余為只讀(secondaries),主節(jié)點(diǎn)故障時(shí)自動(dòng)重新選舉。
 - 在多主模式下才生效;單主模式可保持 OFF。
 
初始化腳本
-- 01-create-replication-user.sql
-- 創(chuàng)建復(fù)制用戶
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'replpass';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
GRANT BACKUP_ADMIN ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
reset master;上面的腳本有兩個(gè)點(diǎn)需要注意:
- WITH mysql_native_password 必須要加上
 - 最后的 reset master 不要忘記了
 
-- 02-install-plugin.sql
INSTALL PLUGIN group_replication SONAME 'group_replication.so';這些腳本在容器首次啟動(dòng)時(shí)自動(dòng)執(zhí)行:
- 創(chuàng)建具有復(fù)制權(quán)限的用戶 repl
 - 安裝 Group Replication 插件
 
啟動(dòng) MGR 集群
啟動(dòng) MGR 集群的過程由 start-mgr.sh 腳本完成:
#!/bin/bash
# 等待所有MySQL實(shí)例啟動(dòng)完成
sleep 30
# 在主節(jié)點(diǎn)上引導(dǎo)組并啟動(dòng)組復(fù)制
docker exec mysql-master mysql -uroot -prootpassword -e "
  CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='replpass' FOR CHANNEL 'group_replication_recovery';
  SET GLOBAL group_replication_bootstrap_group=ON;
  START GROUP_REPLICATION;
  SET GLOBAL group_replication_bootstrap_group=OFF;
  SELECT * FROM performance_schema.replication_group_members;"
# 等待主節(jié)點(diǎn)組復(fù)制啟動(dòng)完成
sleep 20
# 在從節(jié)點(diǎn)上啟動(dòng)組復(fù)制
docker exec mysql-slave-1 mysql -uroot -prootpassword -e "
  CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='replpass' FOR CHANNEL 'group_replication_recovery';
  START GROUP_REPLICATION;
  SELECT * FROM performance_schema.replication_group_members;"
docker exec mysql-slave-2 mysql -uroot -prootpassword -e "
  CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='replpass' FOR CHANNEL 'group_replication_recovery';
  START GROUP_REPLICATION;
  SELECT * FROM performance_schema.replication_group_members;"
# 檢查MGR狀態(tài)
sleep 5
echo "檢查MGR集群狀態(tài):"
docker exec mysql-master mysql -uroot -prootpassword -e "SELECT * FROM performance_schema.replication_group_members;"主節(jié)點(diǎn):
- 配置復(fù)制恢復(fù)通道的用戶名和密碼。
 - 設(shè)置 group_replication_bootstrap_group=ON 引導(dǎo)組。
 - 啟動(dòng)組復(fù)制 START GROUP_REPLICATION。
 - 關(guān)閉引導(dǎo)模式 group_replication_bootstrap_group=OFF。
 - 只有第一個(gè)節(jié)點(diǎn)需要引導(dǎo)組,其他節(jié)點(diǎn)只需加入。
 
從節(jié)點(diǎn):
- 配置相同的復(fù)制恢復(fù)通道。
 - 直接啟動(dòng)組復(fù)制,自動(dòng)加入已存在的組。
 
常見問題
遇到問題很正常,出現(xiàn)任何錯(cuò)誤優(yōu)先查看 mysql 節(jié)點(diǎn)的日志,根據(jù)錯(cuò)誤信息讓 AI 分析并給出解決方案,通常都是可以解決的。
我部署過程中就遇到執(zhí)行 ./start-mgr.sh 后,查詢狀態(tài)如下圖:

只有主節(jié)點(diǎn)是 ONLINE。我對主從節(jié)嘗試手動(dòng)停止和啟用組復(fù)制后解決。
STOP GROUP_REPLICATION; 
START GROUP_REPLICATION;在學(xué)習(xí)過程中出現(xiàn)問題其實(shí)是好事,可以從解決問題的過程中去提高。之前碰到有同事遇到問題就重置服務(wù)器,其實(shí)是在走捷徑,能力得不到提升,而且不是所有的環(huán)境都能重置的。搞懂所有細(xì)枝末節(jié)才能以不變應(yīng)萬變。
希望本文對您有所幫助!















 
 
 
















 
 
 
 