结构图
| ---> master(rw) (172.18.14.70:3306)
client ---> mysqlrouter ----> |---> slave1(rw) (172.18.14.71:3306)
| ---> salve2(ro) (172.18.14.72:3306)
| ---> slave3(ro) (172.18.14.73:3306)
我们通过 mysqlrouter 定义一个监听端口,把 master 和 slave1 定义到一组,做成高可用(rw),平时将客户端对该端口的写请求发给 master,当 master 故障时,把请求转给 slave1;再通过 mysqlrouter 定义一个监听端口,把 salve2 和 slave3 定义到一组,做成负载均衡(ro),装客户端对端口的读请求轮流发送给后端。
注意:MySQL Router 不管 MySQL 服务器的主从关系以及数据是否一致,他只起到数据转发作用。
安装
下载二进制包后解压即可。
mkdir -p /var/log/mysqlrouter/ # 创建日志目录
mkdir -p /etc/mysqlrouter # 创建配置文件目录
MySQL Router的配置
配置文件为 /etc/mysqlrouter/mysqlrouter.ini
[DEFAULT]
logging_folder = /var/log/mysqlrouter
[logger]
level = INFO
# 一个高可用的标签
[routing:failover]
bind_address = 0.0.0.0
bind_port = 7001
max_connections = 1024
mode = read-write
# mode 目前就支持两种: read-write 和 read-only,前者用于高可用,后者用于负载均衡
destinations = 172.18.14.70:3306, 172.18.14.71:3306
# 实际转发的地址,第一个socket无法连接了,才会连接到第二个socket
# 一个负载均衡的标签
[routing:balancing]
bind_address = 0.0.0.0
bind_port = 7002
max_connections = 1024
mode = read-only
destinations = 172.18.14.72:3306, 172.18.14.73:3306
# 这里的两个socket是轮询用的
MySQL Router的启动
官方二进制安装包中的启动脚本示例是 debian 系的,可在 MySQL 官网下载 RedHat 对应的 mysql-router 的 RPM 包,解压后,将其中的 /etc/init.d/mysqlrouter 抽取出来,修改相应文件路径即可。
也可以直接使用如下示例:
# cat /etc/init.d/mysqlrouter
#! /bin/bash
#
# mysqlrouter This shell script takes care of starting and stopping
# the MySQL Router
#
# chkconfig: 2345 66 34
# description: MySQL Router
# processname: mysqlrouter
# config: /etc/mysqlrouter/mysqlrouter.ini
# pidfile: /var/run/mysqlrouter/mysqlrouter.pid
#
# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# Maintainer: MySQL Release Engineering <mysql-build@oss.oracle.com>
#
# Source function library
. /etc/rc.d/init.d/functions
# Source networking configuration
. /etc/sysconfig/network
# add general install path
base_dir=/usr/local/mysql-router
# fix exec path
exec=${base_dir}/bin/mysqlrouter
prog=mysqlrouter
piddir=${base_dir}/run/mysqlrouter
pidfile=${piddir}/mysqlrouter.pid
logdir=/var/log/mysqlrouter
logfile=$logdir/mysqlrouter.log
lockfile=/var/lock/subsys/$prog
# add conf path
conf=/etc/mysqlrouter/mysqlrouter.ini
start () {
[ -d $piddir ] || mkdir -p $piddir
chown mysql:mysql $piddir
[ -d $logdir ] || mkdir -p $logdir
chown mysql:mysql $logdir
[ -e $logfile ] || touch $logfile
chown mysql:mysql $logfile
export ROUTER_PID=$pidfile
# add opt -c to resolv mysqlrouter.ini
daemon --user mysql $exec -c $conf >/dev/null 2>&1 &
ret=$?
if [ $ret -eq "0" ]; then
action $"Starting $prog: " /bin/true
touch /var/lock/subsys/$prog
else
action $"Starting $prog: " /bin/false
fi
return $ret
}
stop () {
[ -f /var/lock/subsys/$prog ] || return 0
killproc mysqlrouter >/dev/null 2>&1
ret=$?
if [ $ret -eq "0" ]; then
rm -f $pidfile
rm -f /var/lock/subsys/$prog
action $"Stopping $prog: " /bin/true
else
ation $"Stopping $prog: " /bin/false
fi
}
restart () {
stop
start
}
condrestart () {
[ -e /var/lock/subsys/$prog ] && restart || return 0
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status -p "$pidfile" $prog
;;
restart)
restart
;;
condrestart|try-restart)
condrestart
;;
reload)
exit 3
;;
force-reload)
restart
;;
*)
echo $"Usage: $0 {start|stop|status|condrestart|try-restart|reload|force-reload}"
exit 2
esac
exit $?
启动mysqlrouter:
# /etc/init.d/mysqlrouter start
启动后,可以通过 netstat 命令和 mysqlrouter 日志文件观察 mysqlrouter 的监听 IP 和端口。
MySQL Router 的测试
现在,就可以通过连接 mysqlrouter 主机的 IP 和端口来进行 mysql 的连接测试了。
可以发现,如果连接的是读端口,则每次连接都是轮询的,如果连接的是写端口,则只连接的是 master。
- 通过这种方式,其实就实现了读写分离,但是需要依靠业务端进行代码修改才能完成。读的指向7002(RO),写的指向7001(RW)。
- 如果一个事物中既有读,又有写的时候,针对 MySQL Router 这个中间件来说,应该指向 7001(RW)。
- 针对其他中间件如 mycat,DDB(网易)、TDDL(淘宝)来说,对外只暴露一个端口,对业务层是透明的,但是实现起来较重,因为这类中间件需要对SQL语句进行解析(CPU 开销较重)。且在一个事务中有读有写的情况下,最保险的做法还是应该发送给 Master 节点,避免数据不一致。