看板 KnucklesNote
作者 標題 [MySQL] 設定Master/Slave資料庫備援(Replication)
時間 2018-12-25 Tue. 02:27:37
本文使用的作業系統: CentOS 7,
資料庫使用 MariaDB 5.5 (與 MySQL 5.5 相容)
資料庫可以使用 Master/Slave 架構,
讓主資料庫 Master 主要負責資料寫入的動作,
然後自動將所有更新同步到備援資料庫 Slave
讓備援資料庫 Slave 用來分擔大部份的資料讀取動作
另外也可以當作資料庫的即時備份
主資料庫 Master 設定
修改設定檔 my.cnf
$ sudo vim /etc/my.cnf
# 不同的資料庫要用不用的 id 數字,通常主資料庫就設定為 1
server-id = 1
# 將資料庫的每個寫入指令記錄在 binlog 檔,用來讓備援資料庫同步用
log-bin=mysql-bin
# 設定 binlog 一天就到期自動刪除,免得占用過多硬碟空間
expire-logs-days=1
# 要忽略某些資料庫不要記錄的話可以設定
binlog_ignore_db=test1
binlog_ignore_db=test2
# 每次的寫入指令都會記錄在 binlog,確保資料即時被同步到備援資料庫,會影響效能
sync_binlog = 1
# 有使用 innodb 的話,要設定這個確保資料會寫入硬碟,會影響效能
innodb_flush_log_at_trx_commit = 1
重啟讓設定生效
$ sudo systemctl restart mariadb
在主資料庫新增一個使用者 replication
用來給備援資料庫登入用
先登入mysql指令模式
$ mysql -u root -p
新增使用者,將 rep_password 改為自訂的密碼
MariaDB > CREATE USER 'replication'@'%' IDENTIFIED BY 'rep_password';
MariaDB > GRANT REPLICATION SLAVE ON *.* TO 'replication'@'%';
MariaDB > FLUSH PRIVILEGES;
備援資料庫 Slave 設定
修改設定檔 my.cnf
$ sudo vim /etc/my.cnf
# 備援資料庫的 id 設為 2
server-id = 2
# 設為唯讀避免非管理者帳號寫入資料
read_only
# 設定 Slave 的IP,讓 Master 可以使用 show slave hosts 顯示有在連線的 Slave
report-host=db2
# 如果要忽略某些資料庫,不同步過來的話可以設定這個
# 多個資料庫的話要分行設定,不能用逗號分隔
replicate_ignore_db=test1
replicate_ignore_db=test2
# 如果要忽略某些資料表,可以設定這個
# 多個talbe要分行設定,不能用逗號分隔
replicate_ignore_table=test1.table1
replicate_ignore_table=test1.table2
重啟讓設定生效
$ sudo systemctl restart mariadb
在 Master 使用 mysqldump 備份整個資料庫
要先將主資料庫完整的複製到備援資料庫後,才能開始同步
在使用 mysqldump 前,要停止資料庫所有的寫入動作,避免之後同步失敗
先將網頁讀取資料庫的帳號設為唯讀 (只有 SELECT 權限)
然後開一個新的ssh連線登入 mysql 指令模式執行
MariaDB > FLUSH TABLES WITH READ LOCK;
將還在記憶體的資料寫進binlog,並鎖定所有資料表
取得目前 binlog 的記錄位置
MariaDB > SHOW MASTER STATUS\G;
*************************** 1. row ***************************
File: mysql-bin.000059
Position: 3847499
Binlog_Do_DB:
Binlog_Ignore_DB:
File: mysql-bin.000059
Position: 3847499
Binlog_Do_DB:
Binlog_Ignore_DB:
記下 File 和 Position,之後在 Slave 要設定從這邊開始同步
注意只要離開 mysql 指令模式後,鎖定就會解開了
所以要用另一個連線來執行 mysqldump
在另一個ssh連線執行 mysqldump 備份整個資料庫
$ mysqldump -u root -p --all-databases --single-transaction --flush-logs > dump_master.sql
或是只備份指定的資料庫 my_db
$ mysqldump -u root -p mh_db --single-transaction --flush-logs > dump_master.sql
備份完成後就可以解除鎖定了,回到 mysql 指令模式的連線執行
MariaDB > UNLOCK TABLES;
或是用 exit; 離開也可以
將網頁讀取資料庫的帳號解除唯讀
使用 scp 將 dump_master.sql 傳給 Slave 主機
$ sudo scp dump_master.sql root@db2:/root/
以上步驟也可以寫成 Shell script
#!/bin/bash
myUser="your_username"
myPass="your_password"
myDB="your_db"
backup_dir="/root/"
dump_file=$backup_dir"dump_master.sql"
echo "使用 mysqldump 備份資料庫 存成 dump_master.sql 檔"
mysqldump -u $myUser -p$myPass $myDB --single-transaction --flush-logs > $dump_file
echo "使用 scp 將 dump_master.sql 傳送給 Slave 主機"
scp $dump_file root@db2:$backup_dir
# 執行 SHOW MASTER STATUS 並取出 File 與 Position 的值
QUERY="SHOW MASTER STATUS\G;"
MYSQL_RESULT=$(mysql -u$myUser -p$myPass -e "$QUERY")
FILE=$(echo "$MYSQL_RESULT" | awk '/File/ {print $2}')
POSITION=$(echo "$MYSQL_RESULT" | awk '/Position/ {print $2}')
# 產生 SQL 指令檔傳給 SLAVE 主機
SQL_FILE="change_master.sql"
REP_PASS="rep_password"
SQL="STOP SLAVE; CHANGE MASTER TO MASTER_HOST='db3',
MASTER_USER='replication', MASTER_PASSWORD='"$REP_PASS"',
MASTER_LOG_FILE='$FILE', MASTER_LOG_POS=$POSITION; START SLAVE;"
echo $SQL > $SQL_FILE
scp $SQL_FILE root@db2:$backup_dir
其中 awk '/File/ {print $2}' 代表取出含有 File 那行的第二欄myUser="your_username"
myPass="your_password"
myDB="your_db"
backup_dir="/root/"
dump_file=$backup_dir"dump_master.sql"
echo "使用 mysqldump 備份資料庫 存成 dump_master.sql 檔"
mysqldump -u $myUser -p$myPass $myDB --single-transaction --flush-logs > $dump_file
echo "使用 scp 將 dump_master.sql 傳送給 Slave 主機"
scp $dump_file root@db2:$backup_dir
# 執行 SHOW MASTER STATUS 並取出 File 與 Position 的值
QUERY="SHOW MASTER STATUS\G;"
MYSQL_RESULT=$(mysql -u$myUser -p$myPass -e "$QUERY")
FILE=$(echo "$MYSQL_RESULT" | awk '/File/ {print $2}')
POSITION=$(echo "$MYSQL_RESULT" | awk '/Position/ {print $2}')
# 產生 SQL 指令檔傳給 SLAVE 主機
SQL_FILE="change_master.sql"
REP_PASS="rep_password"
SQL="STOP SLAVE; CHANGE MASTER TO MASTER_HOST='db3',
MASTER_USER='replication', MASTER_PASSWORD='"$REP_PASS"',
MASTER_LOG_FILE='$FILE', MASTER_LOG_POS=$POSITION; START SLAVE;"
echo $SQL > $SQL_FILE
scp $SQL_FILE root@db2:$backup_dir
在 Slave 啟動同步功能
將 Master 傳來的資料庫備份檔 dump_master.sql 還原進資料庫
$ mysql -u root -p < dump_master.sql
在 Slave 主機登入 mysql 指令模式,執行
MariaDB > STOP SLAVE;
CHANGE MASTER TO MASTER_HOST='db1',
MASTER_USER='replication',
MASTER_PASSWORD='rep_password',
MASTER_LOG_FILE='mysql-bin.000059',
MASTER_LOG_POS=3847499;
START SLAVE;
CHANGE MASTER TO MASTER_HOST='db1',
MASTER_USER='replication',
MASTER_PASSWORD='rep_password',
MASTER_LOG_FILE='mysql-bin.000059',
MASTER_LOG_POS=3847499;
START SLAVE;
將上面的 db1 改為主資料庫的 hostname 或是 IP 位址
rep_password 改為之前新增的使用者 replication 的密碼
mysql-bin.000059 和 3847499 改為前面記下的 File 和 Position
或是使用 Master 傳來的 SQL 指令檔
$ mysql -u root -p < change_master.sql
看看 Slave 主機有沒有正確在執行同步
MariaDB > SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: db1
Master_User: replication
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000060
Read_Master_Log_Pos: 16172358
Relay_Log_File: db2-relay-bin.000004
Relay_Log_Pos: 16172642
Relay_Master_Log_File: mysql-bin.000060
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
.....
若上面的 Slave_IO_Running 和 Slave_SQL_Running 都是 Yes 的話就是成功了
有使用 phpMyAdmin 的話可以在 Master 主機的「伺服器/備援」看到
前面兩個指令就是前面提過的
MariaDB > SHOW MASTER STATUS;
MariaDB > SHOW SLAVE HOSTS;
在 Slave 主機的「伺服器/備援」看到
其中查看次要伺服器狀態就是前面提過的
MariaDB > SHOW SLAVE STATUS\G;
可以過段時間看一下Running是不是 Yes,以及 Errno 是否為0
Slave主機可以關機一下再打開,一樣可以同步到最新的資料
若確認資料庫同步都不會出現錯誤後,就可以改寫 PHP 存取資料庫的程式,
將 SELECT 的 SQL 改為使用備援資料庫
例如是使用 mysqli 來存取資料庫時,原本取得連線的程式為
$mysqli = new mysqli("db1", $user, $pswd, $db_name);
再另外新增一個$mysqli_slave = new mysqli("db2", $user, $pswd, $db_name);
if($mysqli_slave->connect_errno){ //備援DB連不上時,使用主DB
}
然後將使用 SELECT 的 SQL 改為使用 $mysqli_slave 這個連線即可if($mysqli_slave->connect_errno){ //備援DB連不上時,使用主DB
unset($mysqli_slave);
$mysqli_slave = $mysqli;
}
另外要注意讀取型態為 MEMORY 的資料表不要使用 Slave
因為 Slave 主機只要重開機後資料表被清空,就會與主資料庫的不相同了
自動檢查 Slave 主機狀態
寫一個 shell 檔 check_slave.sh 每10分鐘自動檢查 Slave 主機是否有在同步,
若沒有的話產生一個 slave_error 的檔案,讓 PHP 可以知道 Slave 掛了不要使用
#!/bin/bash
myUser="your_username"
myPswd="your_password"
myStatus=(`mysql -u$myUser -p$myPswd -e "SHOW SLAVE STATUS\G;" |egrep "Slave_IO_Running|Slave_SQL_Running" |awk '{print $NF}'`)
if [ "${myStatus[0]}" == "No" -o "${myStatus[1]}" == "No" ]; then
touch slave_error
scp slave_error your_admin_id@your_web_host:/var/www/your_path/
fi
其中使用 |egrep 將 SLAVE STAUS 中的 Slave_IO_Running 與 Slave_SQL_Running 這兩行抓出來myUser="your_username"
myPswd="your_password"
myStatus=(`mysql -u$myUser -p$myPswd -e "SHOW SLAVE STATUS\G;" |egrep "Slave_IO_Running|Slave_SQL_Running" |awk '{print $NF}'`)
if [ "${myStatus[0]}" == "No" -o "${myStatus[1]}" == "No" ]; then
touch slave_error
scp slave_error your_admin_id@your_web_host:/var/www/your_path/
fi
使用 |awk '{print $NF}' 可以將那兩行的最後一個字串抓出來
若 Slave 主機正常的話,抓出來的結果為兩行都是「Yes」,有錯的話其中一行會是「No」
存到 myStatus 後可使用 ${myStatus[0]} 與 ${myStatus[1]} 來判斷是否有「No」
有「No」的話用 touch 產生一個 slave_error 檔
用 scp 將 slave_error 檔傳到 web 主機的網頁目錄
在 /etc/crontab 每十分鐘執行一次檢查
*/10 * * * * your_admin_id /home/your_admin_id/check_slave.sh
在 PHP 連線資料庫時多加個判斷
$slave_error = false;
if(file_exists(slave_error) && time()-filemtime(slave_error)<600){
}
if(file_exists(slave_error) && time()-filemtime(slave_error)<600){
$slave_error = true;
}
參考:
MySQL Replication 主從式架構設定教學
--
※ 作者: Knuckles 時間: 2018-12-25 02:27:37
※ 編輯: Knuckles 時間: 2023-11-19 19:05:50 (台灣)
※ 看板: KnucklesNote 文章推薦值: 1 目前人氣: 0 累積人氣: 3189
回列表(←)
分享