今天閑來無事,打算搭建一個MySQL的高可用架構,采用的是MySQL的主主結構,再外加Keepalived,對外統一提供虛IP。先來說說背景吧,現在的項目為了高可用性,都是避免單節點的存在的,比如,我們的應用程式,都是部署多個節點,通過Nginx做負載均衡,某個節點出現問題,并不會影響整體應用。那麼資料庫層如何搭建高可用的架構呢?今天我們就來看看。
整體架構
MySQL采用主主結構,我們使用兩台機器就夠了,然後再這兩台機器上再安裝Keepalived,使用vrrp技術,虛拟出一個IP。兩台機器如下:
- 192.168.73.141:MySQL(主1)、Keepalived(MASTER)
- 192.168.73.142:MySQL(主2)、Keepalived(BACKUP)
- 192.168.73.150:虛IP
整體架構圖如下:

MySQL主主搭建
我們分别在兩台機器上安裝MySQL,使用yum方式安裝,首先從MySQL官網下載下傳rpm包,選擇對應的系統,在這裡,我們選擇CentOS7的prm包,
mysql80-community-release-el7-3.noarch.rpm
。然後将rpm檔案分别上傳到兩台機器上,接下來我們就是用yum來安裝MySQL。
在192.168.73.141(主1)執行如下指令,
# 使用yum安裝rpm包
yum install mysql80-community-release-el7-3.noarch.rpm
# 安裝MySQL社群版 時間較長 耐心等待
yum install mysql-community-server
#啟動MySQL服務
service mysqld start
到這裡,MySQL就安裝完成,并且正常啟動了。然後,我們用root賬号登入MySQL,并建立一個可用的賬号。
# 從MySQL的日志中 找到root賬号的臨時密碼
grep 'temporary password' /var/log/mysqld.log
# 使用root賬号登入 輸入臨時密碼 登入成功
mysql -uroot -p
# 修改root賬号的密碼 使用MYSQL_NATIVE_PASSWORD的加密方式 這種方式大多數用戶端都可以連接配接
ALTER USER 'root'@'localhost' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'MyNewPass4!';
# 建立MySQL賬号
CREATE USER 'USER'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'USER_PWD';
# 對USER賬号授權
GRANT ALL ON *.* TO 'USER'@'%';
# 重新整理權限
FLUSH PRIVILEGES;
好了,到這裡,在192.168.73.141上安裝MySQL成功,并且建立了USER賬戶,我們可以使用NAVICAT等用戶端連接配接。
在192.168.73.142(主2)上也執行上面的指令,這樣我們在兩台機器上都安裝了MySQL。接下來,我們就要配置MySQL的主主結構了。
首先,我們修改192.168.73.141(主1)上的my.cnf檔案。
vim /etc/my.cnf
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
# 配置server-id 每個MySQL執行個體的server-id都不能相同
server-id=1
# MySQL的日志檔案的名字
log-bin=mysql_master
# 作為從庫時 更新操作是否寫入日志 on:寫入 其他資料庫以此資料庫做主庫時才能進行同步
log-slave-updates=on
# MySQL系統庫的資料不需要同步 我們這裡寫了3個 更加保險
# 同步資料時忽略一下資料庫 但是必須在使用use db的情況下才會忽略;如果沒有使用use db 比如create user 資料還是會同步的
replicate-ignore-db=information_schema
replicate-ignore-db=mysql
replicate-ignore-db=performance_schema
replicate-ignore-db=sys
# 使用通配符忽略MySQL系統庫的表 這樣在create user時也不會進行同步了
replicate_wild_ignore_table=information_schema.%
replicate_wild_ignore_table=mysql.%
replicate_wild_ignore_table=performance_schema.%
replicate_wild_ignore_table=sys.%
# MySQL系統庫的日志不計入binlog 這樣更加保險了
binlog-ignore-db=information_schema
binlog-ignore-db=mysql
binlog-ignore-db=performance_schema
binlog-ignore-db=sys
在192.168.73.142(主2)上也修改my.cnf檔案,我們直接複制過去,隻需要修改其中的兩個地方,如下:
# 配置server-id=2
server-id=2
# MySQL的日志檔案的名字 不改名字也可以 這裡主要為了區分
log-bin=mysql_slave
配置檔案都已經修改好了,我們分别在192.168.73.141(主1)和192.168.73.142(主2)上重新開機MySQL服務,
service mysqld restart
下面我們就要配置主從了,其實主主模式就是配置兩個主從,先配置192.168.73.141(主1)->192.168.73.142(主2)的主從,然後再反過來配置192.168.73.142(主2)->192.168.73.141(主1)的主從,這樣主主的模式就配置好了。
我們先來配置192.168.73.141(主1)->192.168.73.142(主2)的主從
先登入192.168.73.141(主1)的資料庫,并執行如下指令:
# 建立備份的賬号 使用MYSQL_NATIVE_PASSWORD的方式加密
mysql> CREATE USER 'repl_master'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'password';
# 對repl_master授予備份的權限
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl_master'@'%';
# 重新整理權限
mysql> FLUSH PRIVILEGES;
# 檢視MySQL主節點的狀态
mysql> SHOW MASTER STATUS;
+-------------------+---------+--------------+---------------------------------------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+---------+--------------+---------------------------------------------+------------------+
| mysql_master.000001 | 516 | | information_schema,mysql,performance_schema,sys | |
+-------------------+---------+--------------+---------------------------------------------+------------------+
1 row in set
我們要記住binlog檔案的名字,也就是mysql_master.000001,和位置,也就是516。
然後,我們再登入到192.168.73.142(主2)的資料庫,執行如下指令:
mysql> CHANGE MASTER TO
# MySQL主的IP
-> MASTER_HOST='192.168.73.141',
# MySQL主的端口
-> MASTER_PORT=3306
# MySQL主的備份賬号
-> MASTER_USER='repl_master',
# MySQL主的備份賬号密碼
-> MASTER_PASSWORD='password',
# 日志檔案 通過show master status得到的
-> MASTER_LOG_FILE='mysql_master.000001',
# 日志檔案位置 通過show master status得到的
-> MASTER_LOG_POS=516;
# 開啟從庫
mysql> START SLAVE;
# 檢視從庫的狀态
mysql> SHOW SLAVE STATUS;
這樣,192.168.73.141(主1)->192.168.73.142(主2)的主從就搭建好了。然後,我們再反過來,搭建192.168.73.142(主2)->192.168.73.141(主1)的主從。
先登入192.168.73.142(主2)的資料庫,執行如下指令:
# 建立備份的賬号 使用MYSQL_NATIVE_PASSWORD的方式加密
mysql> CREATE USER 'repl_slave'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'password';
# 對repl_slave授予備份的權限
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl_slave'@'%';
# 重新整理權限
mysql> FLUSH PRIVILEGES;
# 檢視MySQL主節點的狀态
mysql> SHOW MASTER STATUS;
+-------------------+---------+--------------+---------------------------------------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+---------+--------------+---------------------------------------------+------------------+
| mysql_slave.000001 | 379 | | information_schema,mysql,performance_schema,sys | |
+-------------------+---------+--------------+---------------------------------------------+------------------+
1 row in set
再登入到192.168.73.141(主1)的資料庫,執行如下指令:
mysql> CHANGE MASTER TO
# MySQL主的IP
-> MASTER_HOST='192.168.73.142',
# MySQL主的端口
-> MASTER_PORT=3306
# MySQL主的備份賬号
-> MASTER_USER='repl_slave',
# MySQL主的備份賬号密碼
-> MASTER_PASSWORD='password',
# 日志檔案 通過show master status得到的
-> MASTER_LOG_FILE='mysql_slave.000001',
# 日志檔案位置 通過show master status得到的
-> MASTER_LOG_POS=379;
# 開啟從庫
mysql> START SLAVE;
# 檢視從庫的狀态
mysql> SHOW SLAVE STATUS;
這樣,192.168.73.142(主2)->192.168.73.141(主1)的主從也搭建好了。我們可以使用navicat分别連接配接192.168.73.141(主1)和192.168.73.142(主2),并執行建表、插入語句,驗證一下主主同步是否成功,這裡就不給大家示範了。
Keepalived高可用
MySQL主主結構已經搭建好了,無論從哪個MySQL插入資料,都會同步到另外一個MySQL。雖然有了MySQL主主結構,但是不能保證高可用,比如,我們的應用程式連接配接的是192.168.73.141(主1),倘若192.168.73.141(主1)的MySQL挂掉了,我們的應用程式并不能自動的切換到192.168.73.142(主2),我們的應用程式也是不可用的狀态。要做到這一點,就要借助于Keepalived。
Keepalived有兩個主要的功能:
- 提供虛IP,實作雙機熱備
- 通過LVS,實作負載均衡
我們這裡使用Keepalived,隻需要使用其中的一個功能,提供虛IP,實作雙機熱備。我們需要在192.168.73.141(主1)和192.168.73.142(主2)上都安裝Keepalived,執行指令如下:
yum install keepalived
我們直接使用yum進行安裝。安裝完之後,編輯keepalived的配置檔案,首先編輯192.168.73.141(主1)上的配置檔案,如下:
vim /etc/keepalived/keepalived.conf
# 全局配置 不用動 隻需注釋掉vrrp_strict
global_defs {
notification_email {
[email protected]
[email protected]
[email protected]
}
notification_email_from [email protected]
smtp_server 192.168.200.1
smtp_connect_timeout 30
router_id LVS_DEVEL
vrrp_skip_check_adv_addr
#必須注釋掉 否則報錯
#vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
# 檢查mysql服務是否存活的腳本
vrrp_script chk_mysql {
script "/usr/bin/killall -0 mysqld"
}
# vrrp配置虛IP
vrrp_instance VI_1 {
# 狀态:MASTER 另外一台機器為BACKUP
state MASTER
# 綁定的網卡
interface ens33
# 虛拟路由id 兩台機器需保持一緻
virtual_router_id 51
# 優先級 MASTER的值要大于BACKUP
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
# 虛拟IP位址 兩台keepalived需要一緻
virtual_ipaddress {
192.168.73.150
}
# 檢查腳本 vrrp_script的名字
track_script {
chk_mysql
}
}
###後邊的virtual_server全部注釋掉 它是和LVS做負載均衡用的 這裡用不到
###
再編輯192.168.73.142(主2)上的配置檔案,隻需要将state MASTER改為state BACKUP,如下:
state BACKUP
通過keepalived的配置,我們對外提供192.168.73.150的IP,這個IP實際指向是192.168.73.141(主1),因為它的state是MASTER。當keepalived檢測到192.168.73.141(主1)上的MySQL不可用時,會自動切換到192.168.73.142(主2)。對于外部使用者是無感覺的,因為外部統一使用的是192.168.73.150。
我們再來看看檢測的腳本
/usr/bin/killall -0 mysqld
,killall指令不是系統自帶的,需要安裝,我們還是使用yum來安裝,如下:
# 先查詢一下killall
yum search killall
#找到了psmisc.x86_64
Loading mirror speeds from cached hostfile
===============Matched: killall ================================
psmisc.x86_64 : Utilities for managing processes on your system
# 安裝psmisc
yum install psmisc
這樣我們就可以使用killall指令了。
killall -0
并不是殺掉程序,而是檢查程序是否存在,如果存在則傳回0,如果不存在則傳回1。當傳回1時,keepalived就會切換主備狀态。
好了,killall也介紹完了,我們在兩台機器上啟動keepalived,如下:
# 啟動keepalived
service keepalived start
然後,我們在192.168.73.141(主1)上檢視一下IP是否有192.168.73.150,如下:
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:57:8c:cd brd ff:ff:ff:ff:ff:ff
inet 192.168.73.141/24 brd 192.168.73.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet 192.168.73.150/32 scope global ens33 # 我們看到了192.168.73.150
valid_lft forever preferred_lft forever
inet6 fe80::720b:92b0:7f78:57ed/64 scope link noprefixroute
valid_lft forever preferred_lft forever
到這裡,keepalived的配置就完成了,我們通過navicat連接配接192.168.73.150,可以正常的連接配接資料庫,實際上它連接配接的是192.168.73.141的資料庫,我們操作資料庫也是正常的。
然後,我們停掉192.168.73.141(主1)上的MySQL服務,
service mysqld stop
# 再用 ip addr檢視一下
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:57:8c:cd brd ff:ff:ff:ff:ff:ff
inet 192.168.73.141/24 brd 192.168.73.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet6 fe80::720b:92b0:7f78:57ed/64 scope link noprefixroute
valid_lft forever preferred_lft forever
192.168.73.150的IP找不到了,我們再去192.168.73.142(主2)上去檢視,可以發現192.168.73.150的IP。我們在navicat上操作資料庫,是可以正常使用的。但這時實際連接配接的是192.168.73.142(主2)的資料庫。我們是沒有感覺的。如果我們把192.168.73.141(主1)上的mysql服務再啟動起來,192.168.73.150還會切換到192.168.73.141(主1)。
總結
我們通過MySQL主主結構+keepalived雙機熱備實作了MySQL的高可用,我們應用程式可以連接配接虛IP,具體連接配接的實際MySQL,不需要我們關心。如果我們再做讀寫分離的話,可以将MySQL(主2)作為主,配置資料庫的主從關系。這時,虛IP連接配接的是MySQL(主1),MySQL(主1)将資料同步到MySQL(主2),然後MySQL(主2)再将資料同步到其他從庫。如果MySQL(主1)挂掉,虛IP指向MySQL(主2),MySQL(主2)再将資料同步到其他從庫。