首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

基於 Keepalived 搭建 MySQL 高可用集群:雙主架構實戰指南

     編輯:MySQL 綜合教學

前言

MySQL 高可用方案眾多,常見的有 Keepalived+雙主、MHA、MMM、Heartbeat+DRBD、PXC 及 Galera Cluster。對於中小型企業,Keepalived + MySQL 雙主(單活模式)以其架構簡單、維護成本低、故障切換快等優勢,成為首選方案。本文將詳細記錄基於 CentOS 7 環境,利用 Keepalived 實現 MySQL 雙主高可用的完整部署流程、腳本邏輯及生產環境優化建議。

一、 環境規劃與準備

角色主機 IP主機名作業系統軟體版本
VIP (虛擬 IP)192.168.244.10---
Master1 (主庫)192.168.244.145master1CentOS 7.1MySQL 5.6.26, Keepalived v1.2.13
Master2 (備庫)192.168.244.146master2CentOS 7.1MySQL 5.6.26, Keepalived v1.2.13

核心架構說明: 採用「雙主單活」模式,Master1 為主服務節點(Priority 100),Master2 為備用節點。正常情況下 VIP 綁定在 Master1,Master2 設置 read_only=1 拒絕寫入。當 Master1 故障時,Keepalived 將 VIP 漂移至 Master2,並通過腳本將其設為可讀寫;主庫恢復後,需人工干預或腳本協助將 VIP 切回,避免頻繁漂移造成數據不一致風險。

二、 配置 MySQL 雙主復制

1. 修改配置文件

分別編輯兩台主機的 /etc/my.cnf,關鍵參數如下:

# Master1 (192.168.244.145)
[mysqld]
log-bin=mysql-bin
server-id=1
log_slave_updates=1

# Master2 (192.168.244.146)
[mysqld]
log-bin=mysql-bin
server-id=2
log_slave_updates=1
read_only=1  # 備庫預設唯讀,防止誤寫入

參數解析: log_slave_updates=1 確保從庫收到的 Binlog 會寫入自身的 Binlog,實現 A->B->A 的級聯傳遞,這是雙主架構的基石。

2. 創建復制用戶並建立雙向同步

# 在 Master1 上執行
CREATE USER 'repl'@'192.168.244.146' IDENTIFIED BY 'mysql';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.244.146';

# 在 Master2 上執行
CREATE USER 'repl'@'192.168.244.145' IDENTIFIED BY 'mysql';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.244.145';

3. 配置 CHANGE MASTER TO 並啟動同步

由于是全新搭建,無需鎖表導出數據,直接根據 SHOW MASTER STATUS 輸出指定位置:

# Master1 指向 Master2
CHANGE MASTER TO MASTER_HOST='192.168.244.146', MASTER_USER='repl', MASTER_PASSWORD='mysql', MASTER_LOG_FILE='mysql-bin.000004', MASTER_LOG_POS=64729;

# Master2 指向 Master1
CHANGE MASTER TO MASTER_HOST='192.168.244.145', MASTER_USER='repl', MASTER_PASSWORD='mysql', MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=68479;

分別執行 START SLAVE;,通過 SHOW SLAVE STATUS\G 確認 Slave_IO_Running: Yes 且 Slave_SQL_Running: Yes 即雙主復制建立成功。

三、 部署 Keepalived 實現 VIP 漂移與故障轉移

1. 安裝與基礎配置

yum install -y keepalived

2. Master1 配置詳解

編輯 /etc/keepalived/keepalived.conf,核心採用 BACKUP + nopreempt (非搶佔) 模式,配合優先級區分主備:

vrrp_script chk_mysql {
    script "/etc/keepalived/check_mysql.sh"
    interval 30
}

vrrp_instance VI_1 {
    state BACKUP
    interface eno16777736          # 根據實際網卡名修改
    virtual_router_id 51
    priority 100                   # 高優先級,作為主節點
    advert_int 1
    nopreempt                      # 關鍵:防止主庫恢復後自動搶佔 VIP,減少切換風險
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_mysql
    }
    virtual_ipaddress {
        192.168.244.10/24
    }
}

3. Master1 核心檢查腳本

/etc/keepalived/check_mysql.sh 邏輯:連通性檢查 -> 服務狀態判斷 -> 重試機制 -> 強制降級 (read_only + kill 連接 + 停止 keepalived)。

#!/bin/bash
# 防止腳本并发执行
if [ `ps -ef|grep -w "$0"|grep -v "grep"|wc -l` -gt 2 ];then exit 0; fi

mysql_con='mysql -uroot -p123456'
error_log="/etc/keepalived/logs/check_mysql.err"

function excute_query { ${mysql_con} -e "select 1;" 2>> ${error_log}; }

function service_error {
    echo "`date '+%F %H:%M:%S'` -----mysql service error,stop keepalived-----" >> ${error_log}
    service keepalived stop &>> ${error_log}
    echo "DB1 keepalived stopped" | mail -s "Alert: DB1 keepalived stopped" admin@example.com 2>> ${error_log}
}

function query_error {
    echo "`date '+%F %H:%M:%S'` -----query error, retry after 30s-----" >> ${error_log}
    sleep 30
    excute_query
    if [ $? -ne 0 ]; then
        echo "`date '+%F %H:%M:%S'` -----still failing, set read_only=1 & kill connections-----" >> ${error_log}
        ${mysql_con} -e "set global read_only = 1;" 2>> ${error_log}
        # 批量 kill 正在執行的查詢線程
        ${mysql_con} -e 'select concat("kill ",id,";") from information_schema.PROCESSLIST where command in ("Query","Execute") into outfile "/tmp/kill.sql";'
        ${mysql_con} -e "source /tmp/kill.sql"
        sleep 2
        service keepalived stop &>> ${error_log}
        echo "DB1 keepalived stopped" | mail -s "Alert: DB1 keepalived stopped" admin@example.com 2>> ${error_log}
    fi
}

excute_query
if [ $? -ne 0 ]; then
    service mysqld status &>/dev/null
    if [ $? -ne 0 ]; then service_error; else query_error; fi
fi

4. Master2 配置與切換腳本

Master2 優先級較低,不啟用 track_script,而是通過 notify_master 腳本在成為 MASTER 時執行數據一致性檢查並開放寫入:

# /etc/keepalived/keepalived.conf (Master2)
vrrp_instance VI_1 {
    state BACKUP
    interface eno16777736
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication { auth_type PASS; auth_pass 1111; }
    notify_master /etc/keepalived/notify_master_mysql.sh  # 關鍵:狀態變為 MASTER 時觸發
    virtual_ipaddress { 192.168.244.10/24; }
}

notify_master_mysql.sh 核心邏輯:檢查 Slave_SQL_Running -> 等待 Relay Log 追上 Master Log (最長 10s) -> 解除 read_only -> 發送告警。

#!/bin/bash
change_log=/etc/keepalived/logs/state_change.log
mysql_con='mysql -uroot -p123456'

slave_info() {
    # 解析 show slave status 關鍵指標
    # $1=slave_status 獲取 IO/SQL 線程狀態
    # $1=log_file,$2=pos 獲取 Master_Log_File, Read_Master_Log_Pos, Relay_Master_Log_File, Exec_Master_Log_Pos
}

action() {
    ${mysql_con} -e "set global read_only = 0;" 2>> $change_log
    echo "DB2 promoted to MASTER" | mail -s "DB2 Promoted" admin@example.com 2>> $change_log
}

# 1. 檢查 SQL 線程
slave_info slave_status
if [ "$Slave_SQL_Running" = "Yes" ]; then
    i=0
    # 2. 等待從庫應用追上主庫接收位置 (防止數據丟失)
    slave_info log_file pos
    until [ "$Master_Log_File" = "$Relay_Master_Log_File" -a "$Read_Master_Log_Pos" = "$Exec_Master_Log_Pos" ]; do
        if [ $i -lt 10 ]; then sleep 1; i=$((i+1)); slave_info log_file pos; 
        else echo "Wait timeout, force promote"; action; exit 0; fi
    done
    action
else
    # SQL 線程異常,記錄日誌並強制提升 (風險較大,生產建議人工介入)
    slave_info log_file pos
    echo "Slave SQL Error, force promote" >> $change_log
    action
fi

四、 故障恢復:手動將 VIP 切回 Master1

由於啟用了 nopreempt,主庫恢復後不會自動搶佔 VIP。需執行手動切回腳本(建議在業務低峰期操作):

  1. 在 Master2 上執行:設置 read_only=1、Kill 連接、停止 Keepalived (VIP 漂回 Master1)。
  2. 驗證 Master1 同步追上 (Exec_Master_Log_Pos = Read_Master_Log_Pos)。
  3. 在 Master1 上設置 read_only=0 並啟動 Keepalived。
#!/bin/bash
# 手動切回腳本邏輯摘要
mysql_con='mysql -uroot -p123456'
# 1. 鎖定 Master2
$mysql_con -e "set global read_only = 1;"
# Kill Master2 連接...
# 2. 檢查 Master1 同步進度 (通過 repl 用戶連 Master1 查詢)
# 3. 解鎖 Master1 並啟動 keepalived
ssh 192.168.244.145 'mysql -uroot -p123456 -e "set global read_only=0;" && /etc/init.d/keepalived start'
# 4. 重啟 Master2 keepalived 使其進入 BACKUP 狀態
/sbin/service keepalived restart

五、 生產環境踩坑總結與優化建議

1. Keepalived 進程無法停止問題

現象: 腳本執行 service keepalived stop 但進程殘留,導致 VIP 不漂移。
優化: 停止命令後增加檢查邏輯,等待 5 秒若進程仍在則 kill -9 強制終止。

2. 日誌管理優化

預設日誌輸出至 /var/log/messages 查閱困難。建議獨立日誌文件:

# 1. 修改 /etc/sysconfig/keepalived
KEEPALIVED_OPTIONS="-D -d -S 0"  # -S 0 使用 local0 設施

# 2. 修改 /etc/rsyslog.conf (CentOS 7)
local0.* /var/log/keepalived.log

# 3. 重啟 rsyslog
systemctl restart rsyslog

3. 雙主單活 vs 雙主雙活

本方案為 雙主單活,同一時間僅一個節點提供寫入服務,避免了雙主雙活帶來的自增 ID 衝突、數據衝突難題。雖然資源利用率較低(備庫閒置),但數據一致性有強保障,適合寫入壓力不極大、對數據正確性要求高的業務。

4. 腳本權限與測試

所有 .sh 腳本務必執行 chmod +x。未授權會導致 Master1 無法綁定 VIP,日誌報 Entering FAULT state。上線前必須進行:主庫掉電、MySQL 進程殺死、網絡分區、主庫恢復切回 全流程演練。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved