如何進行讀寫分離 由開發人員根據所執行的SQL類型連接不同的伺服器 由資料庫中間層實現讀寫分離 讀寫分離時,需要註意,對於實時性要求比較高的數據,不適合在從庫上查詢(因為主從複製存在一定延遲(毫秒級)),比如庫存就應該在主庫上查詢,如果放在從庫上查詢,可能會存在超賣的情況 由開發人員根據所執行的SQ ...
如何進行讀寫分離
- 由開發人員根據所執行的SQL類型連接不同的伺服器
- 由資料庫中間層實現讀寫分離
讀寫分離時,需要註意,對於實時性要求比較高的數據,不適合在從庫上查詢(因為主從複製存在一定延遲(毫秒級)),比如庫存就應該在主庫上查詢,如果放在從庫上查詢,可能會存在超賣的情況
由開發人員根據所執行的SQL類型進行讀寫分離的方式
優點:
1. 完全由開發人員控制,實現更加靈活
2. 由程式直接連接資料庫,所以性能損耗比較少
缺點:
1. 增加了開發的工作量,使程式代碼更加複雜
2. 人為控制,容易出現錯誤
- 可採用DNS輪詢的方式
DNS輪詢:在同一個功能變數名稱伺服器上為同一個功能變數名稱配置多個不同IP地址的A記錄
應用端使用功能變數名稱來連接資料庫伺服器,這樣在進行功能變數名稱解析時,功能變數名稱伺服器會迴圈的將不同的IP返回給應用端,應用端就可以按地址連接不同的只讀伺服器來進行讀取操作
這種操作比較簡單,只需要修改功能變數名稱伺服器的配置即可,但是如果某一後端伺服器出現故障,則必須通過修改DNS的方式把故障伺服器剔除到只讀伺服器列表之外,性能較差,負載也不均衡,大多數情況下不推薦此方式
- 使用LVS/Haproxy 等代理層軟體
由於是通用的代理層軟體,所以不能自動對SQL語句進行分析,實現讀寫分離,但是可以完成只讀伺服器的負載均衡操作
LVS 四層代理,Haproxy 七層代理,所以從性能來看LVS高於Haproxy
- F5硬體:成本較高
keepalived+lvs的架構方式
此處使用keepalived+lvs的架構方式,演示如下
優點:
抗負載能力較強,屬於四層代理,只進行流量分發,不會對數據內容進行解析,對記憶體和CPU的消耗也比較低,處理效率更高
工作穩定,自身有完整的雙機熱備方案,可進行高可用配置
無流量,只分發請求,流量不從它本身出去,不會對主機的網路IO造成影響
伺服器信息
# 主DB IP:192.168.3.100
# 主備DB IP:192.168.3.101
# SlaveDB IP:192.168.3.102
# keepalived vip:192.168.3.99
# lvs manage : 192.168.3.100/101
# lvs vip :192.168.3.98
1. 安裝lvs管理工具
在192.168.3.100 和192.168.3.101上安裝lvs管理工具
[root@Node1 keepalived]# yum install -y ipvsadmin.x86_64
2. 載入ipvs模塊
在 192.168.3.100 和192.168.3.101以及192.168.3.102 執行以下命令,載入ipvs模塊
[root@Node1 keepalived]# modprobe ip_vs
3. 在slave伺服器上編寫並運行要使用lvs腳本
在 192.168.3.101和 192.168.3.102上編寫腳本
/etc/init.d/lvsrs 腳本文件內容如下
#!/bin/bash
VIP=192.168.3.98
. /etc/rc.d/init.d/functions
case "$1" in
start)
/sbin/ifconfig lo down
/sbin/ifconfig lo up
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
/sbin/sysctl -p >/dev/null 2>&1
/sbin/ifconfig lo:0 $VIP netmask 255.255.255.255 up
/sbin/route add -host $VIP dev lo:0
echo "LVS-DR real server starts successfully.\n"
;;
stop)
/sbin/ifconfig lo:0 down
/sbin/route del $VIP >/dev/null 2>&1
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "LVS-DR real server stopped."
;;
status)
isLoOn=`/sbin/ifconfig lo:0 | grep "$VIP"`
isRoOn=`/bin/netstat -rn | grep "$VIP"`
if [ "$isLoOn" == "" -a "$isRoOn" == "" ]; then
echo "LVS-DR real server has to run yet."
else
echo "LVS-DR real server is running."
fi
exit 3
;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
esac
exit 0
/etc/init.d/lvsrs 需要具有可執行許可權
運行腳本
[root@Node1 keepalived]# /etc/init.d/lvsrs start
運行成功後 通過ip addr 命令 可以看到lo中除了127.0.0.1外還有192.168.3.98
4. 修改主伺服器上的keepalived.conf文件,通過keepalived,保證lvs的高可用
! Configuration File for keepalived
global_defs {
router_id mysql_ha
}
vrrp_script check_run {
script "/etc/keepalived/check_mysql.sh"
interval 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 200
priority 99
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1200
}
track_script {
check_run
}
virtual_ipaddress {
192.168.3.99/24
}
}
vrrp_instance VI_2 {
state BACKUP
interface eth0
virtual_router_id 201
priority 99
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1200
}
virtual_ipaddress {
192.168.3.98/24
}
}
virtual_server 192.168.3.99/24 3306 {
delay_loop 5
lb_algo rr
lb_kind DR
persistence_timeout 120
protocol TCP
sorry_server 192.168.3.99 3306
real_server 192.168.3.101 3306 {
weight 1
MISC_CHECK {
misc_path "/etc/keepalived/check_slave.sh -udba_monitor -p123456 -h10.103.9.204 -P3306"
misc_dynamic
}
}
real_server 192.168.3.102 3306 {
weight 1
MISC_CHECK {
misc_path "/etc/keepalived/check_slave.sh -udba_monitor -p123456 -h10.103.9.205 -P3306"
misc_dynamic
}
}
delay_loop :健康檢查時間,單位秒
lb_algo :lvs負載均衡調度演算法,rr:輪詢演算法
lb_kind :lvs實現負載均衡的機制,有NAT,TUN,DR三種模式
persistence_timeout:會話保存時間,單位秒,如果要做session保持,可以將值設大點,可以保證同一個連接在指定時間內都會讀取到同一臺客戶端伺服器
sorry_server :後端所有伺服器失效後,就會訪問此伺服器
check_slave.sh 用來監測slave伺服器是否可用,當slave伺服器宕機或者slave伺服器延遲比較大時,腳本會把此slave伺服器從lvs的讀列表中去掉
腳本內容如下
#/bin/bash
# check_slave.sh
MYSQL=`which mysql`
VIP=192.168.3.98
VPORT=3306
function usage()
{
echo "usage:"
echo "example:# mysql -umonitor -pmonitor -P3306 -h192.168.3.100"
echo "-p, --password[=name]"
echo "-P, --port"
echo "-h, --host=name"
echo "-u, --user=name"
}
while getopts "u:p:h:P:" option
do
case "$option" in
u)
dbuser="$OPTARG";;
p)
dbpwd="$OPTARG";;
h)
dbhost="$OPTARG";;
P)
dbport="$OPTARG";;
\?)
usage
exit 1;;
esac
done
if [ "-$dbuser" = "-" ]; then
usage
exit 1
fi
if [ "-$dbpwd" = "-" ]; then
usage
exit 1
fi
if [ "-$dbhost" = "-" ]; then
usage
exit 1
fi
if [ "-$dbport" = "-" ]; then
usage
exit 1
fi
$MYSQL -u$dbuser -p$dbpwd -P$dbport -h$dbhost -e "select @@version;" >/dev/null 2>&1
if [ $? = 0 ] ;then
MySQL_ok=1
else
/sbin/ipvsadm -d -t $VIP:$VPORT -r $dbhost:$VPORT
exit 1
fi
slave_status=$(${MYSQL} -u$dbuser -p$dbpwd -P$dbport -h$dbhost -e 'show slave status \G' | awk ' \
/Slave_IO_Running/{io=$2} \
/Slave_SQL_Running/{sql=$2} \
/Seconds_Behind_Master/{printf "%s %s %d\n",io,sql,$2}') >/dev/null 2>&1
arr=($slave_status)
io=${arr[0]}
sql=${arr[1]}
behind=${arr[2]}
if [ "$io" == "No" ]||[ "$sql" == "No" ]; then
/sbin/ipvsadm -d -t $VIP:$VPORT -r $dbhost:$VPORT
exit 1
elif [ $behind -gt 60 ]; then
/sbin/ipvsadm -d -t $VIP:$VPORT -r $dbhost:$VPORT
exit 1
else
/sbin/ipvsadm -a -t $VIP:$VPORT -r $dbhost:$VPORT -g
exit 0
fi
5. 創建lvs用於監控後端資料庫所使用的資料庫賬號
[root@Node1 keepalived]# mysql -uroot -p
mysql> grant all privileges on *.* to dba_monitor@'192.168.3.%' identified by '123456';
6. 在使用lvs的manage伺服器上編寫並運行lvs所需要的腳本
在 192.168.3.100 上編寫腳本lvsdr
/etc/init.d/lvsdr 腳本文件內容如下
#!/bin/bash
VIP=192.168.3.98
DEV=eth0
. /etc/rc.d/init.d/functions
case "$1" in
start)
echo "1">/proc/sys/net/ipv4/ip_forward
/sbin/ipvsadm -A -t $VIP:3306 -s rr -p 60
/sbin/ipvsadm -a -t $VIP:3306 -r 10.103.9.204:3306 -g
/sbin/ipvsadm -a -t $VIP:3306 -r 10.103.9.205:3306 -g
/sbin/ipvsadm --start-daemon
echo "LVS-DR server starts successfully.\n"
;;
stop)
/sbin/route del $VIP >/dev/null 2>&1
echo "0" >/proc/sys/net/ipv4/ip_forward
/sbin/ipvsadm -C
echo "LVS-DR real server stopped."
;;
status)
isLoOn=`/sbin/ifconfig lo:0 | grep "$VIP"`
isRoOn=`/bin/netstat -rn | grep "$VIP"`
if [ "$isLoOn" == "" -a "$isRoOn" == "" ]; then
echo "LVS-DR real server has to run yet."
else
echo "LVS-DR real server is running."
fi
exit 3
;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
esac
exit 0
/etc/init.d/lvsdr 需要具有可執行許可權
運行腳本
[root@Node1 keepalived]# /etc/init.d/lvsdr start
7. 在從伺服器上訪問虛擬IP,進行測試
[root@Node3 ~]# mysql -udba_monitor -p123456 -h192.168.3.98 -e"show variables like ''server_id";
可以通過以上命令查看虛擬IP當前所在伺服器的server_id
由於我們persistence_timeout設置的是120秒,所以接下來的120秒如果一直運行以上命令可以發現,一直訪問的是同一個server_id
下麵我們在192.168.3.102上查看一下ipvs的狀態,命令如下
[root@Node2 init.d]# ipvsadm -L -n
可以看到 192.168.3.98:3306 對應了兩個伺服器ip 192.168.3.101 和192.168.3.102
接下來我們模擬其中一個伺服器宕機的情況
[root@Node3 ~]# /etc/init.d/mysqld stop
然後我們再來查詢ipvs狀態
[root@Node1 keepalived]# ipvsadm -L -n
發現 192.168.3.98:3306 現在只對應了1個伺服器ip 192.168.3.101,而192.168.3.102已被剔除
二. 由資料庫中間層完成讀寫分離
常用中間層軟體有:MysqlProxy、MaxScale、OneProxy 、 ProxySQL等
優點:
1. 由中間件根據查詢語法分析,自動完成讀寫分離
通過判斷SQL語句如果是select語句則使用slave,如果是update、insert、delete、create語句則使用master伺服器,無法判斷的則使用master
2. 對程式透明,對於已有程式不用做任何調整
3. 前面所說到的一些中間層軟體除了能做到讀寫分離外,還具有能對多個只讀資料庫進行負載均衡的功能
缺點:
1. 由於增加了中間層,所以對查詢效率有損耗
2. 對於延遲敏感的業務無法自動在主庫執行
使用MaxScale解決讀壓力大的問題
MaxScale介紹
支持高可用,負載均衡,良好擴展的插件式資料庫中間層軟體
MaxScale允許用戶開發和定製適合自己的插件,目前MaxScale提供的插件功能主要分為5個種類
1. 認證插件
提供資料庫登錄認證的功能
2. 協議插件
負責 MaxScale和外部系統間介面的協議,包括客戶端到MaxScale的介面,以及MaxScale 到後端資料庫的介面
3. 路由插件
ReadConnRoute 用來解決多台讀伺服器的負載均衡
ReadWriteSplit 用來實現讀寫分離
4. 監控插件
用於對後端資料庫進行實時監控,以便將前端請求發送到正確的(即正常的可以對外提供服務的)資料庫中
5. 過濾和日誌插件
提供了簡單的資料庫防火牆功能,可以對某些SQL進行過濾和改寫,可以進行一些簡單的SQL容錯和語句的自動轉換
使用MaxScale
安裝方法自行百度
伺服器信息
MaxScale 節點 192.168.3.102
Master DB:192.168.3.100
Slave DB:192.168.3.101
Slave DB:192.168.3.102
1. 為監控模塊創建mysql賬號
mysql> create user scalemon@'192.168.3.%' identified by '123456';
mysql> grant replication slave,replication client on *.* to scalemon@'192.168.3.%';
2. 為路由模塊創建mysql賬號
用來讀取mysql系統庫下的表,獲取後端資料庫的許可權
mysql> create user scaleroute@'192.168.3.%' identified by '123456';
mysql> grant select on mysql.* to scaleroute@'192.168.3.%';
3. 對資料庫密碼進行加密
因為maxScale的配置文件是一個文本格式的明文文件,在文件中直接書寫mysql密碼是不安全的
maxScale提供了加密mysql密碼的命令,這個命令是在maxScale節點中運行
[root@Node3 tools]# maxpassword /var/lib/maxscale/ 123456
E3AEE4B7125B9C76BF742AE6246ECC5C
生成了密碼123456對應的加密字元串
4. 對maxscale進行配置
[root@Node3 tools]# vim /etc/maxscale.cnf
參數說明
[maxscale]
thread=1 # 不要超過cpu的數量
[server1]
type=server
address=192.168.3.100
port=3306
protocol=MySQLBackend
[server2]
type=server
address=192.168.3.101
port=3306
protocol=MySQLBackend
[server3]
type=server
address=192.168.3.102
port=3306
protocol=MySQLBackend
[MySQL Monitor]
type=monitor
module =mysqlmon
servers=server1,server2,server3
user=scalemon
passwd=E3AEE4B7125B9C76BF742AE6246ECC5C # 使用剛剛的加密字元串
monitor_interval=1000 # 毫秒
[Read-Write Service]
type=service
router=readwritesplit
servers=server1,server2,server3
user=scalerouter
passwd=E3AEE4B7125B9C76BF742AE6246ECC5C # 使用剛剛的加密字元串
max_slave_connections=100%
max_slave_replication_lag=60
[MaxAdmin Service]
type=service
router=cli
[Read-Write Listener]
type=listener
service=Read-Write Service
protocol=MySQLClient
port=4006
[MaxAdmin Listener]
type=listener
service=MaxAdmin Service
protocol=maxscaled
port=6603
5. 啟動maxscale服務
[root@Node3 tools]# maxscale -f /etc/maxscale.cnf
6. 查看maxscale服務狀態
maxscale是使用maxadmin進行管理的,預設賬號是admin,密碼是mariadb
[root@Node3 tools]# maxadmin --user=admin --password=mariadb
# 查看後端伺服器列表
MaxScale> list servers
# 查看是否讀取到了後端資料庫伺服器的賬號
MaxScale> show dbusers "Read-Write Service"
引入MaxScale後的架構
將雙主架構改為了單主架構,因為MaxScale會自動識別後端伺服器的角色,如果使用雙主架構,則無法分清當前的主是哪一個