什么是雙主復(fù)制
在傳統(tǒng)的主從復(fù)制架構(gòu)中,從庫僅僅是作為主庫數(shù)據(jù)的備份,當(dāng)主庫發(fā)生故障時,數(shù)據(jù)庫將停止對外提供服務(wù),并且主庫故障后手動進(jìn)行主從切換的過程也較為繁瑣。為了解決這個問題,可以采用 MySQL 雙主模式,其中一臺主庫提供服務(wù),另一臺作為熱備。結(jié)合 keepalived 使用虛擬 IP 對外提供服務(wù),一旦主庫發(fā)生故障,備庫可以在很短的時間內(nèi)接管服務(wù)。
機(jī)器規(guī)劃
搭建 MySQL 雙主同步
準(zhǔn)備工作
創(chuàng)建相關(guān)目錄
#創(chuàng)建用戶
userdel -r mysql
groupadd mysql
useradd -r -g mysql -s /bin/false mysql
#創(chuàng)建目錄
# /mysql/App/ MySQL 數(shù)據(jù)庫軟件根目錄
# /mysql/data/3306/data/ MySQL 數(shù)據(jù)文件目錄
# /mysql/log/3306/binlog MySQL 二進(jìn)制日志目錄
# /mysql/log/3306/relaylog MySQL 中繼日志目錄
# /mysql/backup/3306/xtrabackup/target_dir MySQL xtrabackup 物理備份目錄
# /mysql/backup/3306/mysqldump MySQL mysqldump 邏輯備份目錄
# /mysql/script MySQL 常用腳本存放目錄
mkdir -p /mysql/app/
mkdir -p /mysql/data/3308/data/
mkdir -p /mysql/log/3308/binlog
mkdir -p /mysql/log/3308/relaylog
mkdir -p /mysql/backup/3308/xtrabackup/target_dir
mkdir -p /mysql/backup/3308/mysqldump
mkdir -p /mysql/script
#給目錄授權(quán)
chown -R mysql:mysql /mysql
復(fù)制代碼
下載并解壓 MySQL 安裝包
MySQL 壓縮包下載地址:https://dev.mysql.com/downloads/mysql/5.7.html
#解壓壓縮包
tar zxvf mysql-5.7.29-linux-glibc2.12-x86_64.tar.gz -C /mysql/app
mv /mysql/app/mysql-5.7.29-linux-glibc2.12-x86_64 /mysql/app/mysql
chown -R mysql:mysql /mysql
復(fù)制代碼
配置環(huán)境變量
##將MySQL目錄添加環(huán)境變量##
cat >> ~/.bash_profile <<-EOF
export PATH=$PATH:/mysql/app/mysql/bin
EOF
source ~/.bash_profile
復(fù)制代碼
初始化主庫 A
主庫 A 重要配置如下:
-
開啟 binlog:
log_bin=binlog 目錄
-
設(shè)置 server_id:
server_id = 1
,主庫 A 的 server_id 和主庫 B 要不一樣。 -
針對 GTIP 的方式同步有兩個參數(shù)必須設(shè)置:
-
gtid_mode=on
-
enforce_gtid_consistency=on
-
防止主鍵沖突:
-
設(shè)置自增主鍵步長,通常有幾個主庫 就寫幾,避免主鍵沖突:
auto_increment_increment=2
-
設(shè)置自增主鍵起始值,第一個主庫為 1,第二個主庫為 2,以此類推:
auto_increment_offset=1
關(guān)于防止主鍵沖突的兩個參數(shù)的詳解可以查看 這篇博客。
#主機(jī)名和端口號作為目錄名的一部分
HostName=`hostname`
MySql_Port=3308
#IP地址
Ip=192.168.1.36
#master server_id 要和 slave 不一樣
Server_Id=1
cat > /mysql/data/$MySql_Port/my.cnf <<-EOF
#------------------------------------
#客戶端設(shè)置
#------------------------------------
[client]
port=$MySql_Port
socket =/mysql/data/$MySql_Port/mysql.sock
default-character-set=utf8
#------------------------------------
#mysql連接工具設(shè)置
#------------------------------------
[mysql]
prompt="\u@\h \d \r:\m:\s>" #登錄時顯示登錄的用戶名、服務(wù)器地址、默認(rèn)數(shù)據(jù)庫名、當(dāng)前時間
auto-rehash #讀取表信息和列信息,可以在連上終端后開啟tab補(bǔ)齊功能。
default-character-set=utf8 #默認(rèn)字符集
#------------------------------------
#基本設(shè)置
#------------------------------------
[mysqld]
bind_address=0.0.0.0 #監(jiān)聽本地所有地址
port=$MySql_Port #端口號
user=mysql #用戶
basedir=/mysql/app/mysql #安裝路徑
datadir=/mysql/data/$MySql_Port/data #MySQL數(shù)據(jù)目錄
socket=/mysql/data/$MySql_Port/mysql.sock #用于本地連接的socket文件目錄
pid-file=/mysql/data/$MySql_Port/mysql.pid #進(jìn)程ID文件的目錄。
character-set-server=utf8 #默認(rèn)字符集
#------------------------------------
#log setting 日志設(shè)置
#------------------------------------
long_query_time=10 #慢查詢時間,超過 10 秒則認(rèn)為是慢查詢
slow_query_log=ON #啟用慢查詢?nèi)罩?/code>slow_query_log_file=/mysql/log/$MySql_Port/${HostName}-query.log #慢查詢?nèi)罩灸夸?/code>log_queries_not_using_indexes=1 #記錄未使用索引的語句
log_slow_admin_statements=1 #慢查詢也記錄那些慢的optimize table,analyze table和alter table語句
log-error=/mysql/log/$MySql_Port/${HostName}-error.log #錯誤日志目錄
#------------------------------------
#master modify parameter 主庫A復(fù)制更改參數(shù)
#------------------------------------
server_id=$Server_Id #master和slave server_id 需要不同
#------------------------------------
#slave parameter 主庫B參數(shù)
#------------------------------------
relay_log=/mysql/log/$MySql_Port/relaylog/${HostName}-relaylog #中繼日志目錄
relay-log-index=/mysql/log/$MySql_Port/relaylog/${HostName}-relay.index #中繼日志索引目錄
log_slave_updates=1 #主庫B從主庫A復(fù)制的數(shù)據(jù)會寫入主庫B binlog 日志文件里,默認(rèn)是不寫入
read_only=0 #主庫B讀寫權(quán)限
relay_log_purge=1 #自動清空不再需要中繼日志
#二進(jìn)制日志參數(shù)配置
log_bin=/mysql/log/$MySql_Port/binlog/${HostName}-binlog #binlog目錄
log_bin_index=/mysql/log/$MySql_Port/binlog/${HostName}-binlog.index #指定索引文件的位置
binlog_format=row #行模式復(fù)制,默認(rèn)是 row
binlog_rows_query_log_events=on #在 row 模式下,開啟該參數(shù),可以將把 sql 語句打印到 binlog 日志里面,方便查看
binlog_cache_size=1M #事務(wù)能夠使用的最大 binlog 緩存空間。
max_binlog_size=2048M #binlog 文件最大空間,達(dá)到該大小時切分文件
expire_logs_days=7 #設(shè)置自動刪除 binlog 文件的天數(shù)。
sync_binlog=1 #表示每次事務(wù)的 binlog 都會fsync持久化到磁盤,MySQL 5.7.7 之后默認(rèn)為1,之前的版本默認(rèn)為0
innodb_flush_log_at_trx_commit=1 #表示每次事務(wù)的 redo log 都直接持久化到磁盤,默認(rèn)值為1
#------------------------------------
#GTID Settings GTID 同步復(fù)制設(shè)置
#------------------------------------
gtid_mode=on #開啟GTID同步
enforce_gtid_consistency=on #強(qiáng)制事務(wù)一致,確保 GTID 的安全,在事務(wù)中就不能創(chuàng)建和刪除臨時表
binlog_gtid_simple_recovery=1 #這個變量用于在 MySQL 重啟或啟動的時候?qū)ふ?GTIDs 過程中,控制 binlog 如何遍歷的算法
#------------------------------------
#避免主鍵沖突設(shè)置
#------------------------------------
auto_increment_increment=2 #自增主鍵步長,通常有幾個主庫A就寫幾,避免主鍵沖突
auto_increment_offset=1 #設(shè)置自增主鍵起始值,第一個主庫A為1,第二個主庫A為2,以此類推
EOF
復(fù)制代碼
初始化主庫 A:
mysqld
--defaults-file=/mysql/data/3308/my.cnf
--initialize --user=mysql
--basedir=/mysql/app/mysql
--datadir=/mysql/data/3308/data
復(fù)制代碼
配置 MySQL 啟動腳本:
cp /mysql/app/mysql/support-files/mysql.server /etc/init.d/mysql_3308
ln -sf /etc/init.d/mysql_3308 /usr/lib/systemd/system/mysql_3308
#修改啟動腳本##
vim /etc/init.d/mysql_3308
basedir=/mysql/app/mysql
datadir=/mysql/data/3308/data
mysqld_pid_file_path=/mysql/data/3308/mysql.pid
#在$bindir/mysqld_safe 后面添加,注意 --defaults-file 要放在第一個
--defaults-file="/mysql/data/3308/my.cnf"
systemctl daemon-reload
復(fù)制代碼
啟動 MySQL,修改密碼,運(yùn)行遠(yuǎn)程登錄:
#啟動、MySQL服務(wù)
systemctl start mysql_3308
#獲取MySQL臨時密碼
Passwd=`cat /mysql/log/3308/*-error.log |grep "root@localhost:"|awk -F ' ' '{print $11}'`
echo $Passwd
#通過本地 socket 登錄、修改密碼
mysql -uroot -p$Passwd -S /mysql/data/3308/mysql.sock
alter user 'root'@'localhost' identified by "123456";
#允許遠(yuǎn)程登錄
grant all privileges on *.* to root@'%' identified by '123456';
#刷新權(quán)限
flush privileges;
復(fù)制代碼
初始化主庫 B
主庫 B 配置文件,主要是 ip 地址,server_id 以及 auto_increment_offset 的配置和主庫 A 不一樣,其余配置和主庫 A 一樣。
#主機(jī)名和端口號作為目錄名的一部分
HostName=`hostname`
MySql_Port=3308
#IP地址
Ip=192.168.1.37
#master server_id 要和 slave 不一樣
Server_Id=2
cat > /mysql/data/$MySql_Port/my.cnf <<-EOF
#------------------------------------
#客戶端設(shè)置
#------------------------------------
[client]
port=$MySql_Port
socket =/mysql/data/$MySql_Port/mysql.sock
default-character-set=utf8
#------------------------------------
#mysql連接工具設(shè)置
#------------------------------------
[mysql]
prompt="\u@\h : \d\r:\m:\s>" #登錄時顯示登錄的用戶名、服務(wù)器地址、默認(rèn)數(shù)據(jù)庫名、當(dāng)前時間
auto-rehash #讀取表信息和列信息,可以在連上終端后開啟tab補(bǔ)齊功能。
default-character-set=utf8 #默認(rèn)字符集
#------------------------------------
#基本設(shè)置
#------------------------------------
[mysqld]
bind_address=0.0.0.0 #監(jiān)聽本地所有地址
port=$MySql_Port #端口號
user=mysql #用戶
basedir=/mysql/app/mysql #安裝路徑
datadir=/mysql/data/$MySql_Port/data #MySQL數(shù)據(jù)目錄
socket=/mysql/data/$MySql_Port/mysql.sock #用于本地連接的socket文件目錄
pid-file=/mysql/data/$MySql_Port/mysql.pid #進(jìn)程ID文件的目錄。
character-set-server=utf8 #默認(rèn)字符集
#------------------------------------
#log setting 日志設(shè)置
#------------------------------------
long_query_time=10 #慢查詢時間,超過 10 秒則認(rèn)為是慢查詢
slow_query_log=ON #啟用慢查詢?nèi)罩?/code>slow_query_log_file=/mysql/log/$MySql_Port/${HostName}-query.log #慢查詢?nèi)罩灸夸?/code>log_queries_not_using_indexes=1 #記錄未使用索引的語句
log_slow_admin_statements=1 #慢查詢也記錄那些慢的optimize table,analyze table和alter table語句
log-error=/mysql/log/$MySql_Port/${HostName}-error.log #錯誤日志目錄
#------------------------------------
#master modify parameter 主庫A復(fù)制更改參數(shù)
#------------------------------------
server_id=$Server_Id #master和slave server_id 需要不同
#二進(jìn)制日志參數(shù)配置
log_bin=/mysql/log/$MySql_Port/binlog/${HostName}-binlog #binlog目錄
log_bin_index=/mysql/log/$MySql_Port/binlog/${HostName}-binlog.index #指定索引文件的位置
binlog_format=row #行模式復(fù)制,默認(rèn)是 row
binlog_rows_query_log_events=on #在 row 模式下,開啟該參數(shù),可以將把 sql 語句打印到 binlog 日志里面,方便查看
binlog_cache_size=1M #事務(wù)能夠使用的最大 binlog 緩存空間。
max_binlog_size=2048M #binlog 文件最大空間,達(dá)到該大小時切分文件
expire_logs_days=7 #設(shè)置自動刪除 binlog 文件的天數(shù)。
sync_binlog=1 #表示每次事務(wù)的 binlog 都會fsync持久化到磁盤,MySQL 5.7.7 之后默認(rèn)為1,之前的版本默認(rèn)為0
innodb_flush_log_at_trx_commit=1 #表示每次事務(wù)的 redo log 都直接持久化到磁盤,默認(rèn)值為1
#------------------------------------
#slave parameter 主庫B參數(shù)
#------------------------------------
relay_log=/mysql/log/$MySql_Port/relaylog/${HostName}-relaylog #中繼日志目錄
relay-log-index=/mysql/log/$MySql_Port/relaylog/${HostName}-relay.index #中繼日志索引目錄
log_slave_updates=1 #主庫B從主庫A復(fù)制的數(shù)據(jù)會寫入主庫B binlog 日志文件里,默認(rèn)是不寫入
read_only=0 #主庫B讀寫權(quán)限
relay_log_purge=1 #自動清空不再需要中繼日志
# 并行復(fù)制參數(shù)
#主庫A上面怎么并行,主庫B上面就怎么回放,基于邏輯時鐘的概念
#binlog 會記錄組提交的信息,從回放的時候就可以知道哪些事務(wù)是一組里面的,
#一組里面的就丟到不同線程去回放,不是一組里的就等待,以此來提升并行度
slave-parallel-type=LOGICAL_CLOCK
#多線程復(fù)制
slave-parallel-workers=4
#slave 上commit 的順序保持一致,否則可能會有間隙鎖產(chǎn)生
slave-preserve-commit_order=1
master_info_repository=TABLE #默認(rèn)每接收到10000個事件,寫一次master-info,默認(rèn)是寫在文件中的
#修改 relay_log_info_repository 的好處
#1.relay.info 明文存儲不安全,把 relay.info 中的信息記錄在 table 中相對安全。
#2.可以避免 relay.info 更新不及時,slave 重啟后導(dǎo)致的主從復(fù)制出錯。
relay_log_info_repository=TABLE #將回放信息記錄在 slave_relay_log_info 表中,默認(rèn)是記錄在 relay-info.log 文件中
relay_log_recovery=1 #當(dāng)slave重啟時,將所有 relay log 刪除,通過 sql 線程重放的位置點(diǎn)去重新拉日志
#------------------------------------
#Replication Filter 主庫B復(fù)制過濾參數(shù)
#------------------------------------
#(過濾某個數(shù)據(jù)庫、數(shù)據(jù)庫.表)
#replicate_do_db=yzjtestdb
#replicate_wild_do_table=yzjtestdb.%
#replicate_do_table=yzjtestdb.yzjtest_yg
#replicate_wild_do_table=yzjtestdb.yzjtest_yg
#------------------------------------
#GTID Settings GTID 同步復(fù)制設(shè)置
#------------------------------------
gtid_mode=on #開啟GTID同步
enforce_gtid_consistency=on #強(qiáng)制事務(wù)一致,確保 GTID 的安全,在事務(wù)中就不能創(chuàng)建和刪除臨時表
binlog_gtid_simple_recovery=1 #這個變量用于在 MySQL 重啟或啟動的時候?qū)ふ?GTIDs 過程中,控制 binlog 如何遍歷的算法
#------------------------------------
#避免主鍵沖突設(shè)置
#------------------------------------
auto_increment_increment=2 #自增主鍵步長,通常有幾個主庫A就寫幾,避免主鍵沖突
auto_increment_offset=2 #設(shè)置自增主鍵起始值,第一個主庫A為1,第二個主庫A為2,以此類推
EOF
復(fù)制代碼
初始化主庫 B:
mysqld
--defaults-file=/mysql/data/3308/my.cnf
--initialize --user=mysql
--basedir=/mysql/app/mysql
--datadir=/mysql/data/3308/data
復(fù)制代碼
配置 MySQL 啟動腳本:
cp /mysql/app/mysql/support-files/mysql.server /etc/init.d/mysql_3308
ln -sf /etc/init.d/mysql_3308 /usr/lib/systemd/system/mysql_3308
#修改啟動腳本##
vi /etc/init.d/mysql_3308
basedir=/mysql/app/mysql
datadir=/mysql/data/3308/data
mysqld_pid_file_path=/mysql/data/3308/mysql.pid
#在$bindir/mysqld_safe 后面添加,注意 --defaults-file 要放在第一個
--defaults-file="/mysql/data/3308/my.cnf"
systemctl daemon-reload
復(fù)制代碼
啟動 MySQL,修改密碼,運(yùn)行遠(yuǎn)程登錄:
#啟動、MySQL服務(wù)
systemctl start mysql_3308
#獲取MySQL臨時密碼
Passwd=`cat /mysql/log/3308/*-error.log |grep "root@localhost:"|awk -F ' ' '{print $11}'`
echo $Passwd
#通過本地 socket 登錄、修改密碼
mysql -uroot -p$Passwd -S /mysql/data/3308/mysql.sock
alter user 'root'@'localhost' identified by "123456";
#允許遠(yuǎn)程登錄
grant all privileges on *.* to root@'%' identified by '123456';
#刷新權(quán)限
flush privileges;
復(fù)制代碼
創(chuàng)建復(fù)制用戶
分別在主庫 A 和主庫 B 上創(chuàng)建一個用于數(shù)據(jù)復(fù)制的用戶。
grant replication slave on *.*
to 'repuser'@'%' identified by 'repuser123';
復(fù)制代碼
建立主從關(guān)系
主庫 A 和主庫 B 都先清除下 binlog。
reset master;
復(fù)制代碼
主庫 A 配置主從,指向主庫 B。
stop slave;
change master to
master_host='192.168.1.36',
master_port=3308,
master_user='repuser',
master_password='repuser123',
master_auto_position=1;
start slave;
復(fù)制代碼
主庫 B 配置主從,指向主庫 A。
stop slave;
change master to
master_host='192.168.1.37',
master_port=3308,
master_user='repuser',
master_password='repuser123',
master_auto_position=1;
start slave;
復(fù)制代碼
使用 show slave statusG
命令查看主從同步狀態(tài),IO 線程和 SQL 線程都為 YES 表示同步正常,主庫 A 和主庫 B 互為主從。
部署 Keepalived
下載并解壓安裝包
wget https://www.keepalived.org/software/keepalived-2.2.4.tar.gz
tar -xzvf keepalived-2.2.4.tar.gz
復(fù)制代碼
安裝相關(guān)依賴
yum install kernel-devel openssl-devel popt-devel -y
復(fù)制代碼
安裝 keepalived,設(shè)置開機(jī)自動啟動
mkdir /software/keepalived
cd keepalived-2.2.4
./configure --prefix=/software/keepalived
make && make install
systemctl enable keepalived
mkdir /etc/keepalived
復(fù)制代碼
配置 Keepalived
主庫 A 配置 Keepalived
主庫 A keepalived 配置文件,編輯 /etc/keepalived/keepalived.conf 文件:
global_defs {
router_id keep_mysql_repl_g1 # 負(fù)載均衡標(biāo)識,在局域網(wǎng)內(nèi)應(yīng)該是唯一的
}
# vrrp_script 級別和 vrrp_instance 一樣
vrrp_script chk_mysql { # 配置虛擬腳本 chk_mysql
script "/etc/keepalived/check_mysql.sh" # 執(zhí)行腳本,檢查 mysql 服務(wù)是否存活
interval 3 # 腳本執(zhí)行間隔:秒
}
# vrrp_instance
vrrp_instance v_mysql_1 {
state BACKUP # 指定該 keepalived 節(jié)點(diǎn)的初始狀態(tài)(MASTER|BACKUP)
interface ens192 # VRRP 實(shí)例綁定的網(wǎng)口,用于發(fā)送 VRRP 包
virtual_router_id 200 # 路由 ID,范圍是 0-255,主備都一樣
priority 100 # 指定優(yōu)先級,優(yōu)先級高的將成為 MASTER
advert_int 1 # 指定發(fā)送 VRRP 廣播的間隔。單位是秒
nopreempt # 設(shè)置為不搶占。默認(rèn)是搶占的
authentication { # 身份驗(yàn)證
auth_type PASS # 指定認(rèn)證方式
auth_pass mysql # 指定認(rèn)證所使用的密碼 mysql ,主備都一樣
}
track_script { # 調(diào)用"vrrp_script"的腳本
chk_mysql # 增加一個跟蹤腳本到網(wǎng)口上
}
virtual_ipaddress { # 虛擬 IP
192.168.1.38/24
}
}
復(fù)制代碼
主庫 A 檢查腳本,編輯 /etc/keepalived/check_mysql.sh 文件:
#!/bin/bash
#/etc/keepalived/check_mysql.sh
#chmod u+x /etc/keepalived/check_mysql.sh
#Linux 7 使用,如果是配置Linux 6 需要修改腳本
# MySQL賬號密碼
mysql_user="root"
mysql_pass="123456"
# MySQL錯誤日志輸出
mysql_err="/mysql/log/3308/check_mysql_err.log"
# MySQL殺進(jìn)程腳本
mysql_kill_session="/tmp/kill.sql"
# MySQL連接字符串
mysql_con="mysql -u${mysql_user} -p${mysql_pass} -S /mysql/data/3308/mysql.sock"
source ~/.bash_profile
if [ `ps -ef|grep -w "$0"|grep "/bin/sh*"|grep "?"|grep "?"|grep -v "grep"|wc -l` -gt 2 ];then #
exit 0
fi
function excute_query {
$mysql_con -e "select 1 from dual;" 2>> $mysql_err
}
function service_error {
echo -e "`date "+%F %H:%M:%S"` -----mysql service error,now stop keepalived-----" >> $mysql_err
echo -e "n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@n" >> $mysql_err
}
function query_error {
echo -e "`date "+%F %H:%M:%S"` -----query error, but mysql service ok, retry after 30s-----" >> $mysql_err
sleep 30
excute_query
if [ $? -ne 0 ];then
echo -e "`date "+%F %H:%M:%S"` -----still can't execute query-----" >> $mysql_err
echo -e "`date "+%F %H:%M:%S"` -----set read_only = 1 on DB1-----" >> $mysql_err
$mysql_con -e "set global read_only = 1;" 2>> $mysql_err
echo -e "`date "+%F %H:%M:%S"` -----kill current client thread-----" >> $mysql_err
rm -f $mysql_kill_session &>/dev/null
$mysql_con -NB -e 'select concat("kill ",id,";") from information_schema.PROCESSLIST where command="Query" or command="Execute"' > $mysql_kill_session
$mysql_con -e "source $mysql_kill_session"
sleep 2
echo -e "`date "+%F %H:%M:%S"` -----stop keepalived-----" >> $mysql_err
systemctl stop keepalived &>> $mysql_err
echo -e "n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@n" >> $mysql_err
else
echo -e "`date "+%F %H:%M:%S"` -----query ok after 30s-----" >> $mysql_err
echo -e "n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@n" >> $mysql_err
fi
}
excute_query
if [ $? -ne 0 ];then
systemctl status mysql &>/dev/null
if [ $? -ne 0 ];then
service_error
else
query_error
fi
fi
復(fù)制代碼
給檢查腳本賦與執(zhí)行權(quán)限:
chmod u+x /etc/keepalived/check_mysql.sh
復(fù)制代碼
主庫 B 配置 Keepalived
主庫 B keepalived 配置文件,編輯 /etc/keepalived/keepalived.conf 文件:
global_defs {
router_id keep_mysql_repl_g1 # 負(fù)載均衡標(biāo)識,在局域網(wǎng)內(nèi)應(yīng)該是唯一的
}
# vrrp_instance
vrrp_instance v_mysql_1 {
state BACKUP # 指定該 keepalived 節(jié)點(diǎn)的初始狀態(tài)(MASTER|BACKUP)
interface ens192 # VRRP 實(shí)例綁定的網(wǎng)口,用于發(fā)送 VRRP 包
virtual_router_id 200 # 路由ID,范圍是0-255,主備都一樣
priority 90 # 指定優(yōu)先級,優(yōu)先級高的將成為 MASTER
advert_int 1 # 指定發(fā)送VRRP廣播的間隔。單位是秒
nopreempt # 設(shè)置為不搶占。默認(rèn)是搶占的
authentication { # 身份驗(yàn)證
auth_type PASS # 指定認(rèn)證方式
auth_pass mysql # 指定認(rèn)證所使用的密碼 mysql ,主備都一樣
}
notify_master /etc/keepalived/notify_master_mysql.sh # 轉(zhuǎn)換成 master 時,執(zhí)行的腳本
virtual_ipaddress {
192.168.1.38/24
}
}
復(fù)制代碼
主庫 B 腳本,當(dāng)發(fā)生主從切換時,會執(zhí)行該腳本。編輯 /etc/keepalived/notify_master_mysql.sh 文件:
#!/bin/bash
#/etc/keepalived/notify_master_mysql.sh
#chmod u+x /etc/keepalived/notify_master_mysql.sh
# MySQL賬號密碼
mysql_user="root"
mysql_pass="123456"
# 配置更變?nèi)罩?/code>change_log="/mysql/log/3308/state_change.log"
# 主庫B狀態(tài)日志
slave_status_log="/mysql/log/3308/slave_status_log.log"
# MySQL連接字符串
mysql_conn="mysql -u${mysql_user} -p${mysql_pass} -S /mysql/data/3308/mysql.sock"
source ~/.bash_profile
echo -e "`date "+%F %H:%M:%S"` -----keepalived change to MASTER-----" >> $change_log
echo -e "`date "+%F %H:%M:%S"` ----------" >> $slave_status_log
$mysql_conn -e "show slave statusG;" >> $slave_status_log
Slave_IO_Running=`$mysql_conn -e "show slave statusG;"|egrep -w "Slave_IO_Running|Slave_SQL_Running"|awk 'NR==1{print}' | awk '{print $2}'`
Slave_SQL_Running=`$mysql_conn -e "show slave statusG;"|egrep -w "Slave_IO_Running|Slave_SQL_Running"|awk 'NR==2{print}' | awk '{print $2}'`
Master_Log_File=`$mysql_conn -e "show slave statusG;" |egrep -w "Master_Log_File|Read_Master_Log_Pos|Exec_Master_Log_Pos"|awk 'NR==1{print}' | awk '{print $2}'`
Read_Master_Log_Pos=`$mysql_conn -e "show slave statusG;" |egrep -w "Master_Log_File|Read_Master_Log_Pos|Exec_Master_Log_Pos"|awk 'NR==2{print}' | awk '{print $2}'`
Exec_Master_Log_Pos=`$mysql_conn -e "show slave statusG;" |egrep -w "Master_Log_File|Read_Master_Log_Pos|Exec_Master_Log_Pos"|awk 'NR==3{print}' | awk '{print $2}'`
action() {
echo -e "`date "+%F %H:%M:%S"` -----set read_only = 0 on `hostname`-slave-----" >> $change_log
$mysql_conn -e "set global read_only = 0;" 2>> $change_log
$mysql_conn -e "stop slave;" 2>> $change_log
echo "`hostname`-slave keepalived 轉(zhuǎn)為 MASTER 狀態(tài),線上數(shù)據(jù)庫切換至`hostname`-slave" >> $change_log
echo -e "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@n" >> $change_log
}
if [ "$Slave_IO_Running" = "Yes" -a "$Slave_SQL_Running" = "Yes" ];then
if [ $Read_Master_Log_Pos = $Exec_Master_Log_Pos ];then
echo -e "`date "+%F %H:%M:%S"` -----Master_Log_File=$Master_Log_File . Exec_Master_Log_Pos($Exec_Master_Log_Pos) is equal Read_Master_Log_Pos($Read_Master_Log_Pos)" >> $change_log
action
$mysql_conn -e "reset slave all;" 2>> $change_log
else
echo -e "`date "+%F %H:%M:%S"` -----Master_Log_File=$Master_Log_File . Exec_Master_Log_Pos($Exec_Master_Log_Pos) is behind Read_Master_Log_Pos($Read_Master_Log_Pos), The wAIts time is more than 10s,now force change." >> $change_log
sleep 10
action
$mysql_conn -e "reset slave all;" 2>> $change_log
exit 0
fi
action
else
echo -e "`hostname`-slave's slave status is wrong,now force change. Master_Log_File=$Master_Log_File Read_Master_Log_Pos=$Read_Master_Log_Pos Exec_Master_Log_Pos=$Exec_Master_Log_Pos" >> $change_log
action
fi
復(fù)制代碼
給腳本賦與執(zhí)行權(quán)限:
chmod u+x /etc/keepalived/notify_master_mysql.sh
復(fù)制代碼
啟動 Keepalived
在主庫 A 和主庫 B 上分別啟動 keepalived。
systemctl start keepalived
復(fù)制代碼
查看 keepalived 狀態(tài)。
[root@mysql-master keepalived-2.2.4]# systemctl status keepalived.service
● keepalived.service - LVS and VRRP High Availability Monitor
Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)
Active: active (running) since 五 2021-09-10 21:13:37 CST; 18s ago
Docs: man:keepalived(8)
man:keepalived.conf(5)
man:genhash(1)
https://keepalived.org
Process: 8184 ExecStart=/software/keepalived/sbin/keepalived $KEEPALIVED_OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 8186 (keepalived)
Memory: 680.0K
CGroup: /system.slice/keepalived.service
├─8186 /software/keepalived/sbin/keepalived -D
└─8187 /software/keepalived/sbin/keepalived -D
9月 10 21:13:41 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:41 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:41 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:41 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:46 mysql-master Keepalived_vrrp[8187]: (v_mysql_1) Sending/queueing gratuitous ARPs on ens192 for 192.168.1.38
9月 10 21:13:46 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:46 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:46 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:46 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:13:46 mysql-master Keepalived_vrrp[8187]: Sending gratuitous ARP on ens192 for 192.168.1.38
復(fù)制代碼
查看網(wǎng)卡地址,此時虛擬 IP 在主庫 A 上。
[root@mysql-master keepalived-2.2.4]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
.NET 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
link/ether 00:50:56:8b:1b:ca brd ff:ff:ff:ff:ff:ff
inet 192.168.1.36/24 brd 192.168.1.255 scope global ens192
valid_lft forever preferred_lft forever
#虛擬 IP
inet 192.168.1.38/24 scope global secondary ens192
valid_lft forever preferred_lft forever
inet6 fe80::2556:f369:b4e7:fb64/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
inet6 fe80::6b8d:29f7:a5fe:dbee/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
inet6 fe80::f387:57a3:4975:d8f2/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
復(fù)制代碼
驗(yàn)證高可用
客戶端通過虛擬 IP 192.168.1.38 連接數(shù)據(jù)庫,通過 select @@hostname
命令可以看到當(dāng)前連接的為主庫 A。
? mysql -uroot -h 192.168.1.38 -P 3308 -p123456;
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 268
Server version: 5.7.29-log MySQL Community Server (GPL)
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
root@192.168.1.38 (none) 09:11:13>select @@hostname;
+--------------+
| @@hostname |
+--------------+
| mysql-master |
+--------------+
1 row in set (0.01 sec)
root@192.168.1.38 (none) 09:11:26>
復(fù)制代碼
客戶端創(chuàng)建表并插入數(shù)據(jù)。
create database testdb;
create table testdb.data01(
id int not null primary key auto_increment,
name varchar(60),
age int);
insert into testdb.data01 (name,age) values
('tom',18),
('jack',17),
('rock',16),
('james',15),
('cris',20);
復(fù)制代碼
此時分別登錄主庫 A 和主庫 B 查看 testdb.data01 表中的數(shù)據(jù),可以確定主庫 A 和主庫 B 目前數(shù)據(jù)是同步的。并且查看表中的內(nèi)容可以發(fā)現(xiàn)主鍵是以 2 為間隔遞增的,這是為了防止主從切換時插入數(shù)據(jù)產(chǎn)生主鍵沖突。主庫 A 的主鍵會以 1,3,5,7,9 的序號遞增。假如在序號為 9 時發(fā)生主從切換,新的主庫(主庫 A)的主鍵會以 10,12,14,16,18 的序號遞增。
停止主庫 A,模擬故障切換
[root@mysql-master ~]# systemctl stop mysql_3308.service
復(fù)制代碼
在主庫 A 的機(jī)器上查看 keepalived 狀態(tài),可以看到 keepalived 的優(yōu)先級被設(shè)置為 0,此時虛擬 IP 將會飄到主庫 B 的機(jī)器上。
[root@mysql-master tmp]# systemctl status keepalived.service
● keepalived.service - LVS and VRRP High Availability Monitor
Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)
Active: active (running) since 五 2021-09-10 21:42:11 CST; 3min 59s ago
Docs: man:keepalived(8)
man:keepalived.conf(5)
man:genhash(1)
https://keepalived.org
Process: 13365 ExecStart=/software/keepalived/sbin/keepalived $KEEPALIVED_OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 13367 (keepalived)
Memory: 1.0M
CGroup: /system.slice/keepalived.service
├─13367 /software/keepalived/sbin/keepalived -D
├─13368 /software/keepalived/sbin/keepalived -D
├─14761 /bin/bash /etc/keepalived/check_mysql.sh
└─14778 sleep 30
9月 10 21:42:20 mysql-master Keepalived_vrrp[13368]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:42:20 mysql-master Keepalived_vrrp[13368]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:42:20 mysql-master Keepalived_vrrp[13368]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:42:20 mysql-master Keepalived_vrrp[13368]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:42:20 mysql-master Keepalived_vrrp[13368]: Sending gratuitous ARP on ens192 for 192.168.1.38
9月 10 21:45:47 mysql-master Keepalived_vrrp[13368]: Track script chk_mysql is already running, expect idle - skipping run
9月 10 21:45:47 mysql-master Keepalived_vrrp[13368]: VRRP_Script(chk_mysql) timed_out
9月 10 21:45:47 mysql-master Keepalived_vrrp[13368]: (v_mysql_1) Entering FAULT STATE
9月 10 21:45:47 mysql-master Keepalived_vrrp[13368]: (v_mysql_1) sent 0 priority
9月 10 21:45:47 mysql-master Keepalived_vrrp[13368]: (v_mysql_1) removing VIPs.
復(fù)制代碼
查看主庫 B 機(jī)器網(wǎng)卡的地址,發(fā)現(xiàn)虛擬 IP 已經(jīng)切換到主庫 B 上了。當(dāng)發(fā)生主從切換時,主庫 B 的腳本會執(zhí)行 reset slave all
,停止向主庫 A 的同步,防止原主庫 A 恢復(fù)后數(shù)據(jù)意外同步。
[root@mysql-slave keepalived]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
link/ether 00:50:56:8b:71:df brd ff:ff:ff:ff:ff:ff
inet 192.168.1.37/24 brd 192.168.1.255 scope global ens192
#虛擬 IP
valid_lft forever preferred_lft forever
inet 192.168.1.38/24 scope global secondary ens192
valid_lft forever preferred_lft forever
inet6 fe80::2556:f369:b4e7:fb64/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
inet6 fe80::6b8d:29f7:a5fe:dbee/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
inet6 fe80::f387:57a3:4975:d8f2/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
復(fù)制代碼
客戶端發(fā)生了重連,通過 select @@hostname
查看可以看到此時連接的是主庫 B。
root@192.168.1.38 (none) 09:42:14>select @@hostname;
#重連
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 19
Current database: *** NONE ***
+-------------+
| @@hostname |
+-------------+
| mysql-slave |
+-------------+
1 row in set (0.04 sec)
復(fù)制代碼
客戶端插入幾條數(shù)據(jù):
insert into testdb.data01 (name,age) values
('peter',28),
('mark',27),
('marry',26),
('hule',25),
('handson',20);
復(fù)制代碼
查詢數(shù)據(jù),可以看到在原主庫 B 上插入的數(shù)據(jù)主鍵會以 10,12,14,16,18 的序號遞增。
root@192.168.1.38 (none) 09:44:19>select * from testdb.data01;
+----+---------+------+
| id | name | age |
+----+---------+------+
| 1 | tom | 18 |
| 3 | jack | 17 |
| 5 | rock | 16 |
| 7 | james | 15 |
| 9 | cris | 20 |
| 10 | peter | 28 |
| 12 | mark | 27 |
| 14 | marry | 26 |
| 16 | hule | 25 |
| 18 | handson | 20 |
+----+---------+------+
10 rows in set (0.01 sec)
復(fù)制代碼
重新啟動主庫 A,觀察數(shù)據(jù)同步
由于我們關(guān)閉了搶占模式,當(dāng)主庫 A 重新啟動時,主從不會發(fā)送切換。
[root@mysql-master]# systemctl start mysql_3308.service
復(fù)制代碼
主庫 A 的數(shù)據(jù)可以和主庫 B 同步。
參考資料
-
https://www.cnblogs.com/kerrycode/p/11150782.html
-
http://www.linuxe.cn/post-492.html
-
https://blog.csdn.net/JesseYoung/article/details/41942809