讀寫分離時,須要注意,對於實時性要求比較高的數據,不適合在從庫上查詢(由於主從複製存在必定延遲(毫秒級)),好比庫存就應該在主庫上查詢,若是放在從庫上查詢,可能會存在超賣的狀況前端
優勢:
1. 徹底由開發人員控制,實現更加靈活
2. 由程序直接鏈接數據庫,因此性能損耗比較少mysql
缺點:
1. 增長了開發的工做量,使程序代碼更加複雜
2. 人爲控制,容易出現錯誤算法
DNS輪詢:在同一個域名服務器上爲同一個域名配置多個不一樣IP地址的A記錄sql
應用端使用域名來鏈接數據庫服務器,這樣在進行域名解析時,域名服務器會循環的將不一樣的IP返回給應用端,應用端就能夠按地址鏈接不一樣的只讀服務器來進行讀取操做
這種操做比較簡單,只須要修改域名服務器的配置便可,可是若是某一後端服務器出現故障,則必須經過修改DNS的方式把故障服務器剔除到只讀服務器列表以外,性能較差,負載也不均衡,大多數狀況下不推薦此方式數據庫
因爲是通用的代理層軟件,因此不能自動對SQL語句進行分析,實現讀寫分離,可是能夠完成只讀服務器的負載均衡操做
LVS 四層代理,Haproxy 七層代理,因此從性能來看LVS高於Haproxyvim
此處使用keepalived+lvs的架構方式,演示以下後端
優勢:
抗負載能力較強,屬於四層代理,只進行流量分發,不會對數據內容進行解析,對內存和CPU的消耗也比較低,處理效率更高
工做穩定,自身有完整的雙機熱備方案,可進行高可用配置
無流量,只分發請求,流量不從它自己出去,不會對主機的網絡IO形成影響安全
服務器信息bash
# 主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
在192.168.3.100 和192.168.3.101上安裝lvs管理工具服務器
[root@Node1 keepalived]# yum install -y ipvsadmin.x86_64
在 192.168.3.100 和192.168.3.101以及192.168.3.102 執行如下命令,加載ipvs模塊
[root@Node1 keepalived]# modprobe ip_vs
在 192.168.3.101和 192.168.3.102上編寫腳本
#!/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
! 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
[root@Node1 keepalived]# mysql -uroot -p mysql> grant all privileges on *.* to dba_monitor@'192.168.3.%' identified by '123456';
在 192.168.3.100 上編寫腳本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
[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提供的插件功能主要分爲5個種類
提供數據庫登陸認證的功能
負責 MaxScale和外部系統間接口的協議,包括客戶端到MaxScale的接口,以及MaxScale 到後端數據庫的接口
ReadConnRoute 用來解決多臺讀服務器的負載均衡
ReadWriteSplit 用來實現讀寫分離
用於對後端數據庫進行實時監控,以便將前端請求發送到正確的(即正常的能夠對外提供服務的)數據庫中
提供了簡單的數據庫防火牆功能,能夠對某些SQL進行過濾和改寫,能夠進行一些簡單的SQL容錯和語句的自動轉換
安裝方法自行百度
服務器信息
MaxScale 節點 192.168.3.102 Master DB:192.168.3.100 Slave DB:192.168.3.101 Slave DB:192.168.3.102
mysql> create user scalemon@'192.168.3.%' identified by '123456'; mysql> grant replication slave,replication client on *.* to scalemon@'192.168.3.%';
用來讀取mysql系統庫下的表,獲取後端數據庫的權限
mysql> create user scaleroute@'192.168.3.%' identified by '123456'; mysql> grant select on mysql.* to scaleroute@'192.168.3.%';
由於maxScale的配置文件是一個文本格式的明文文件,在文件中直接書寫mysql密碼是不安全的
maxScale提供了加密mysql密碼的命令,這個命令是在maxScale節點中運行
[root@Node3 tools]# maxpassword /var/lib/maxscale/ 123456 E3AEE4B7125B9C76BF742AE6246ECC5C
生成了密碼123456對應的加密字符串
[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
[root@Node3 tools]# maxscale -f /etc/maxscale.cnf
maxscale是使用maxadmin進行管理的,默認帳號是admin,密碼是mariadb
[root@Node3 tools]# maxadmin --user=admin --password=mariadb
# 查看後端服務器列表 MaxScale> list servers # 查看是否讀取到了後端數據庫服務器的帳號 MaxScale> show dbusers "Read-Write Service"
將雙主架構改成了單主架構,由於MaxScale會自動識別後端服務器的角色,若是使用雙主架構,則沒法分清當前的主是哪個