欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用MySQL Router实现高可用、负载均衡、读写分离的详细教程

程序员文章站 2022-03-23 19:37:02
一、mysql router简介 mysql router是mysql官方提供的一个轻量级中间件,可以在应用程序与mysql服务器之间提供透明的路由方式。主要用以解决mysql主从库集群的高可用、负...

一、mysql router简介

mysql router是mysql官方提供的一个轻量级中间件,可以在应用程序与mysql服务器之间提供透明的路由方式。主要用以解决mysql主从库集群的高可用、负载均衡、易扩展等问题。router可以与mysql fabric无缝连接,允许fabric存储和管理用于路由的高可用服务器组,使管理mysql服务器组更加简单。

mysql router是一个可执行文件,可以与应用程序在同一平台上运行,也可以单独部署。虽然mysql router是innodb cluster(mysql 7.x)的一部分,mysql 5.6 等版本数据库仍然可以使用router作为其中间代理层。mysql router的配置文件中包含有关如何执行路由的信息。它与mysql服务器的配置文件类似,也是由多个段组成,每个段中包含相关配置选项。

mysql router是mysql proxy的替代方案,mysql官方不建议将mysql proxy用于生产环境,并且已经不提供mysql proxy的下载。

1. 功能

router作为一个流量转发层,位于应用与mysql服务器之间,其功能类似于lvs。mysql servers作为router的“downstream”(nat模式),应用不再直连mysql servers,而是与router相连。根据router的配置,将会把应用程序的读写请求转发给下游的mysql servers。

当下游有多个mysql servers,无论主、从,router可以对读写请求进行负载均衡。当下游某个server失效时,router可以将其从active列表中移除,当其online后再次加入active列表,即提供了failover特性。

当mysql servers集群拓扑变更时,比如增减slaves节点,只需要修改router的配置即可,无需修改应用中数据库连接配置,因为应用配置的为router地址而非mysql servers的原始地址,即数据库集群对应用来说是透明的。如果mysql servers为5.7+版本,且构建为innodb cluster模式,那么router还能基于metacache(metaservers)机制,感知mysql servers的主从切换、从库增减等集群拓扑变更,而且基于变更能够实现master自动切换、slaves列表自动装配等。比如master失效后,cluster将会自动选举一个新的master,此时router不需要任何调整、可以自动发现此新master进而继续为应用服务。

考虑到router独立部署可能引入“额外的部署成本”、“性能降级”、“连接数上限”等问题,通常建议基于“agent”方式部署,即将router与应用部署在机器上。router通常是解决“mysql集群规模性迁移”,比如跨机房部署、流量迁移、异构兼容,或者解决mysql集群规模性宕机时快速切换等。

router中间件本身不会对请求“拆包”(unpackage),所以无法在router中间件上实现比如“sql审计”、“隔离”、“限流”、“分库分表”等功能。但是router提供了plugin(c语言)机制,用户可以开发自己的plugin来扩展router的额外特性。

2. 架构

mysql router是一个单独的应用程序,其体系结构如图1所示。

使用MySQL Router实现高可用、负载均衡、读写分离的详细教程图1

在图1的最左边是连接到router的应用程序,最右侧是两个连接目的地,即mysql fabric和mysql cluster。中间的router架构分为三层,从上至下依次为插件层、核心层和harness。插件层是对应用开放的接口,mysqlrouter程序是router的核心,而该程序时基于mysql harness实现的。router采用模块化设计,并在实现功能时利用此架构。mysql harness是一个基础模块,提供日志、配置重载、插件管理等功能。当前router提供connection routing和fabric cache两个插件,未来功能将与这两个插件一起出现。

如图1中的箭头所示,应用程序连接router,如果连接路由插件,router从配置文件中读取目标并重定向到列表中的一个服务器。如果使用fabric cache,需在router的路由策略中指定fabric安装的url。这种情况下,应用程序连接到router,然后router将从fabric获取目标列表,然后将连接重定向到列表中的一个服务器。

3. 使用

正如前面提到的,在目前形式中,mysql router最好与应用程序一起使用。也就是说,应该在运行应用程序的相同机器上安装router。虽然这不是强制要求,但建议采用这种做法。可以编写程序来监控mysqlrouter的执行,并在需要时重新启动它。例如,如果目标选项中的服务器列表已用尽,则可以使用新目标列表重新启动router,或重新启动router以重试列表中的服务器。图2说明了如何在应用程序中使用router。

使用MySQL Router实现高可用、负载均衡、读写分离的详细教程图2

可以在整个网络中运行多个router实例。但是mysql官方并没有提供router集群的ha,即每个router节点均为独立,它们之间互不通信,无leader角色,无选举机制。那么当某个router节点失效,应用层面需要借助mysql connector的高级特性,比如:failover、loadbalance等协议来实现failover功能。简单而言,router中间件与connector的高级协议互相协作,才能够实现请求在router集群之间的负载均衡、failover等。

mysql router非常轻量级,与直连servers相比,其性能损耗低于1%。摆在router面前的问题,是其对链接数的支撑能力,原则上我们一个router节点限定在500个tcp链接。router本身cpu、内存、磁盘消耗都极低,但是要求router节点对网络io的支撑能力应该较强。考虑到router底层为“异步io”,如果条件允许,应该构建在较高版本的linux平台下,且给予合理的cpu资源。mysql router在2.1.4版本以下,内核基于select() io模型,存在连接数500上限、较大sql请求导致cpu过高,以及并发连接过高时router假死等问题,建议升级到2.1.6+。

router对连接的管理是基于“粘性”方式,即应用与router的一个tcp连接,将对应一个router与mysql server的连接,当应用与router的连接失效时,router也将断开其与mysql server的连接。只要router上下游网络联通性正常,那么router将不会主动断开与应用的连接,也不会切换其与server的连接。即当应用与router创建一个新连接时,router将根据负载均衡算法,选择一个server并与其建立连接,此后将唯一绑定,直到此server失效时触发重新选择其他server。这就引入一个问题,如果某个连接上发生了“繁重”的sql操作,那么将会导致下游server伴随高负载而无法“负载均衡”。

router对应用是透明的,开发与router一起使用的应用程序不需要任何特殊的库或接口,所增加的工作只是维护mysql router实例。

二、安装配置

环境

172.16.1.125:mysql router

172.16.1.126:mysql replication master

172.16.1.127:mysql replication slave

我们在172.16.1.125上安装配置mysql router,172.16.1.126、172.16.1.127为本例中要通过router访问的两个已经mysql数据库服务器地址。在本例中这两个mysql服务器已经配置好主从复制,拓扑如图3所示。

使用MySQL Router实现高可用、负载均衡、读写分离的详细教程图3

1. 下载二进制安装包

从https://dev.mysql.com/downloads/router/2.1.html页面选择下载的安装包,本例为mysql-router-2.1.6-linux-glibc2.12-x86-64bit.tar.gz。

2. 解压缩

tar xzf mysql-router-2.1.6-linux-glibc2.12-x86-64bit.tar.gz
mv mysql-router-2.1.6-linux-glibc2.12-x86-64bit mysql-router-2.1.6

3. 在资源文件中(本例为.bashrc)添加执行文件路径

.bashrc文件的内容为:

[mysql@hdp2~]$more ~/.bashrc
# .bashrc

# source global definitions
if [ -f /etc/bashrc ]; then
 . /etc/bashrc
fi

# uncomment the following line if you don't like systemctl's auto-paging feature:
# export systemd_pager=

# user specific aliases and functions
export path=.:/sbin:/bin:/usr/sbin:/usr/bin:/usr/x11r6/bin:/home/mysql/mysql-5.6.14/bin:/home/mysql/mysql-router-2.1.6/bin;
[mysql@hdp2~]$

使资源配置生效:

source ~/.bashrc

4. 验证安装

[mysql@hdp2~]$mysqlrouter --help
mysql router v2.1.6 on linux (64-bit) (gpl community edition)
copyright (c) 2015, 2018, oracle and/or its affiliates. all rights reserved.

oracle is a registered trademark of oracle corporation and/or its
affiliates. other names may be trademarks of their respective
owners.

start mysql router.

configuration read from the following files in the given order (enclosed
in parentheses means not available for reading):
 (/home/mysql/mysql-router-2.1.6/bin/.././mysqlrouter.conf)
 (/home/mysql/.mysqlrouter.conf)
plugins path:
 /home/mysql/mysql-router-2.1.6/lib/mysqlrouter
default log directory:
 /home/mysql/mysql-router-2.1.6
default persistent data directory:
 /home/mysql/mysql-router-2.1.6/data
default runtime state directory:
 /home/mysql/mysql-router-2.1.6/run

......

从mysqlrouter联机帮助的输出中,可以看到默认配置文件寻找路径及其顺序,插件路径、日志目录、持久化数据目录、运行时状态目录的缺省位置等重要信息。在后面的配置文件和服务启停文件中需要定义这些目录。注意,如果在mysqlrouter命令行使用--config或-c选项传入用户定义的配置文件,则不会加载默认配置文件。

5. 配置router

# 复制配置文件
cp /home/mysql/mysql-router-2.1.6/share/doc/mysqlrouter/sample_mysqlrouter.conf /etc/mysqlrouter.conf
cp /home/mysql/mysql-router-2.1.6/share/doc/mysqlrouter/sample_mysqlrouter.init /etc/init.d/mysqlrouter

# 修改属主为mysql
chown mysql:mysql /etc/mysqlrouter.conf
chown mysql:mysql /etc/init.d/mysqlrouter

# 变为可执行
chmod +x /etc/init.d/mysqlrouter

# 系统启动时自动执行
echo "/etc/init.d/mysqlrouter" >> /etc/rc.d/rc.local

# 建立日志目录
mkdir /home/mysql/mysql-router-2.1.6/log

配置文件内容如下:

[mysql@hdp2~]$more /etc/mysqlrouter.conf
[default]
# 日志路径
logging_folder = /home/mysql/mysql-router-2.1.6/log

# 插件路径
plugin_folder = /home/mysql/mysql-router-2.1.6/lib/mysqlrouter

# 配置路径
config_folder = /home/mysql/mysql-router-2.1.6/config

# 运行时状态路径
runtime_folder = /home/mysql/mysql-router-2.1.6/run

# 数据文件路径
data_folder = /home/mysql/mysql-router-2.1.6/data

[logger]
# 日志级别
level = info

# 以下选项可用于路由标识的策略部分
[routing:basic_failover]
# router地址
bind_address = 172.16.1.125
# router端口
bind_port = 7001
# 读写模式
mode = read-write
# 目标服务器
destinations = 172.16.1.126:3306,172.16.1.127:3306

[routing:load_balance]
bind_address = 172.16.1.125
bind_port = 7002
mode = read-only
destinations = 172.16.1.126:3306,172.16.1.127:3306

[mysql@hdp2~]$

mysql router的配置文件比较简单,大部分配置项的含义一路了然。上面的文件中配置了两条路由策略,一个用于失败切换,一个用于负载均衡,绑定端口分别是7001和7002。值得一提的是mode参数,该参数的可选值为read-write或read-only,但其实际作用并不是字面含义所示。

对于read-write模式,将采用“首个可用”算法,优先使用第一个server,当第一个server(即172.16.1.126:3306)不可达时,将会failover到第二个server(172.16.1.127:3306),依次进行。如果都不可达,那么此端口上的请求将会被中断,此端口将不能提供服务,且此时所属的路由策略将不可用。需要注意,此算法只遍历一次列表,即逐个验证destinations中的server,不会循环。一旦所有的servers依次验证且不可用后,本条路由策略将不能继续服务,内置状态设定为aborted,即使此后servers恢复上线,也不能继续对client提供服务,因为它不会与servers保持心跳检测。对于router而言,直接拒绝client连接请求,只有重启router节点才能解决。

对于read-only模式:将采用“轮询”算法,依次选择server新建连接,如果某个server不可达,将会重试下一个server,如果所有的server都不可达,那么此端口上的请求将中断,即读写操作将不可用。同时router将会持续与每个server保持心跳探测,当恢复后重新加入active列表,此后那些新建连接请求将可以分发给此server。

但是比较遗憾,router不会将已有的连接重新分配给“新加入”列表的server,比如router有2个server地址(s1,s2),某时刻s1不可达,那么在s1上粘性的客户端连接也将被断开,新建连接将会全部在s2上,此后s1恢复正常,那么在s2上的旧的连接将不会迁移到s1上,此时s1只会接收新的连接,如果没有新连接请求,那么s1将会在一段时间看起来是“不提供服务”的。为了解决此问题,我们要求connection pool有管理“连接生命周期”的相关控制,比如一个connection被创建x秒以后在返回连接池时应该被主动关闭,这个参数在tomcat-jdbc-pool中为“maxage”。 如果应用程序中,部署方式是单master、多slaves,我们完全可以在承接“master”请求的router节点上,也配置为“read-only”模式,那么此单master节点失效重启后,可以不需要重启router节点即可继续服务。因为router不会对tpc拆包,所有“read-write”、“read-only”并不会干扰实际的sql执行。严格来说,这两种mode映射两种“路由算法”:“首个可用”、“轮询”;除此之外,再无特殊含义。

对于读写两种操作,因为router不对请求拆包,所以它无法判断请求的读写类型。我们只能在配置文件中,分别为读、写设定不同的配置:使用不同的绑定端口。比如本例“7001”端口接收到的请求都会转发给172.16.1.126:3306,当它不可用时,都会转发给172.16.1.127:3306。“7002”端口接收的请求则会轮询转发给172.16.1.126:3306和172.16.1.127:3306。

mysql router服务启停文件内容如下:

[mysql@hdp2~]$more /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 
#

# source function library
. /etc/rc.d/init.d/functions

# source networking configuration
. /etc/sysconfig/network

# add general install path
base_dir=/home/mysql/mysql-router-2.1.6
# fix exec path
exec=${base_dir}/bin/mysqlrouter
prog=mysqlrouter
piddir=${base_dir}/run
pidfile=${piddir}/mysqlrouter.pid
logdir=${base_dir}/log
logfile=$logdir/mysqlrouter.log
lockfile=/var/lock/subsys/$prog

# add conf path
conf=/etc/mysqlrouter.conf

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 $
[mysql@hdp2~]$

当程序意外被kill后,有相关程序运行标识,需要先:

rm -f $pidfile
rm -f /var/lock/subsys/$prog

再启动,否则程序会提示有一个实例运行而不能运行该服务。

很多程序需要判断是否当前已经有一个实例在运行,这个目录就是让程序判断是否有实例运行的标志。比如说xinetd,如果存在这个文件,表示已经有xinetd在运行了,否则就是没有。当然程序里面还要有相应的判断措施来真正确定是否有实例在运行。通常与该目录配套的还有/var/run目录,用来存放对应实例的pid,如果写脚本的话,会发现这2个目录结合起来可以很方便的判断出许多服务是否在运行,运行的相关信息等等。实际上,判断是否上锁就是判断这个文件,所以文件存在与否也就隐含了是否上锁。而这个目录的内容并不能表示一定上锁了,因为很多服务在启动脚本里用touch来创建这个加锁文件,在系统结束时该脚本负责清除锁,这本身就不可靠,比如意外失败导致锁文件仍然存在。所以脚本里一般结合pid文件,如果有pid文件的话,从pid文件里得到该实例的pid,然后用ps测试是否存在该pid,从而判断是否真正有这个实例在运行,更加稳妥的方法是用进程通讯,不过这样的话单单靠脚本就做不到了。6. 启动router服务

用root用户执行服务启动命令:

[root@hdp2~]#service mysqlrouter start
starting mysqlrouter (via systemctl):           [ ok ]
[root@hdp2~]#

查看日志文件,显示两个路由策略的监听器已经启动。

[mysql@hdp2~]$more /home/mysql/mysql-router-2.1.6/log/mysqlrouter.log
2018-07-18 17:04:11 info  [7fa3437fb700] [routing:load_balance] started: listening on 172.16.1.125:7002; read-only
2018-07-18 17:04:11 info  [7fa343ffc700] [routing:basic_failover] started: listening on 172.16.1.125:7001; read-write
[mysql@hdp2~]$

三、自动失败切换

当172.16.1.126可用时,对于7001端口的请求,会全部发送到172.16.1.126。对7002端口的请求,会轮询发送给172.16.1.126和172.16.1.127。

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

当172.16.1.126不可用时,对于7001端口的请求,会自动切换到172.16.1.127,而对于7002端口的请求,会全部转移到172.16.1.127。

杀掉172.16.1.126的进程:

pkill -9 mysqld

查看路由的目标服务器:

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

当172.16.1.126再次可用时(假设复制已经重新搭建,主从角色已经互换,172.16.1.127为master,172.16.1.126为slave),对于7001端口的请求,还是会路由到172.16.1.127,而不会自动转到172.16.1.126。而对于7002端口的请求,会自动继续轮询发送给172.16.1.126、172.16.1.127两个服务器。

启动172.16.1.126的mysql服务:

service mysql start

查看路由的目标服务器:

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

此时重启mysqlrouter服务,回到初始状态,对于7001端口的请求,只会路由到172.16.1.126。对7002端口的请求路由策略不变,会轮询发送给172.16.1.126和172.16.1.127。

在172.16.1.125上重启mysqlrouter服务:

service mysqlrouter restart

查看路由的目标服务器:

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id'"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+

注意,此时172.16.1.126在复制中的角色依然是slave,但只有它接受读写请求,实际上是以172.16.1.126作为复制的master,这次router的重启已经破坏了复制的数据一致性,因此这种情况下需要重新手工搭建复制互换角色。

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7001 -e "use test; create table t1 (a int); insert into t1 values (1);"
mysql: [warning] using a password on the command line interface can be insecure.

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id';select * from test.t1;"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+
+------+
| a  |
+------+
|  1 |
+------+

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id';select * from test.t1;"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 127  |
+---------------+-------+
error 1146 (42s02) at line 1: table 'test.t1' doesn't exist

验证read-only模式下的写请求:
c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "insert into test.t1 values (2);"
mysql: [warning] using a password on the command line interface can be insecure.

c:\windows\system32>mysql -utest -p123456 -h172.16.1.125 -p7002 -e "show variables like 'server_id';select * from test.t1;"
mysql: [warning] using a password on the command line interface can be insecure.
+---------------+-------+
| variable_name | value |
+---------------+-------+
| server_id   | 126  |
+---------------+-------+
+------+
| a  |
+------+
|  1 |
|  2 |
+------+

四、负载均衡

从上面的实验可以看出,在一主一从的配置中,只要将读请求发送到7002端口,请求会被轮询发送到两个mysql服务器,从而达到读负载均衡的目的。对于读写负载均衡,则需要配置双主复制,然后将两个mysql服务器都放到read-only下,例如两台mysql服务器互为主从的拓扑结构,只需要配置如下一条路由策略即可。

[routing:load_balance]
bind_address = 172.16.1.125
bind_port = 7001
mode = read-only
destinations = 172.16.1.126:3306,172.16.1.127:3306

虽然叫read-only模式,但这只是指出路由方式为“轮询”。正如上面测试看到的,两个服务器会以轮询方式进行读写,也就实现了最简单读写负载均衡。

五、读写分离

从上面的实验可以看出,在一主一从的配置中,只要将写请求发送到7001端口,读请求7002端口,就可实现读写分离。正常情况下,master接收写请求,master和slave接收读请求。如果master宕机,所有读写请求都切换到slave一台服务器上。