读写分离时,须要注意,对于实时性要求比较高的数据,不适合在从库上查询(由于主从复制存在必定延迟(毫秒级)),好比库存就应该在主库上查询,若是放在从库上查询,可能会存在超卖的状况前端
优势:
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会自动识别后端服务器的角色,若是使用双主架构,则没法分清当前的主是哪个