目的

因为业务架构上需要实现读写分离,刚好前段时间美团点评开源了在360Atlas基础上开发的读写分离中间件DBProxy,关于其介绍在官方文档已经有很详细的说明了,其特性主要有:读写分离、负载均衡、支持分表、IP过滤、sql语句黑名单、DBA平滑下线DB、从库流量配置、动态加载配置项。本文大致简单的介绍自己在使用中如何快速安装和使用DBProxy。

环境

Ubuntu  LTS

注意:由于DBProxy在16.04上面会有报错,因为在Ubuntu16.04上面libmysqlclient-dev源是MySQL5.7,详细的问题可以看这篇文章。所以需要重新设置其源,若是16.04以下版本可以跳过此步。

add-apt-repository 'deb http://archive.ubuntu.com/ubuntu trusty universe'

源替换了之后,还需要把16.04的源先注视掉,不然还是会装MySQL5.7。最后在update:

sudo apt-get update

安装

① 安装依赖包:

apt-get - liblua5.--dev libtool flex bison openssl  libssl-dev libjemalloc1 libjemalloc-dev  libevent-dev autoconf gettext libffi-dev

② 安装 glib-2.42.0

wget http://ftp.gnome.org/pub/GNOME/sources/glib/2.42/glib-2.42.0.tar.xz

由于目前确认有效的glib2版本是2.42.0-1.el6,CentOS、Ubuntu和Debian默认源中的版本都不是2.42.0-1.el6,会导致make报错,因此在上面增加对glib的下载编译安装。

解压:
xz -d glib-.tar.xz
.tar

编译:
cd glib-
autoreconf -ivf
./configure --prefix=/usr/local/glib-2.42
make
make install

③ 安装DBProxy

下载:
git clone https://github.com/Meituan-Dianping/DBProxy.git

安装:
cd DBProxy/
sh autogen.sh

注意:
不使用默认的glib,使用上面新编译的版本,变更glib编译选项,替换原始的bootstrap.sh的内容:

vi bootstrap.sh
+++++++++++++++++++++++++++

#!/bin/sh
base=$(cd ")"; pwd)
cd $base
./configure --prefix=/usr/local/mysql-proxy CFLAGS="-g -O0" \
GLIB_CFLAGS="-I/usr/local/glib-2.42/include/glib-2.0 -I/usr/local/glib-2.42/lib/glib-2.0/include" \
GLIB_LIBS="-L/usr/local/glib-2.42/lib -lglib-2.0" \
GMODULE_CFLAGS="-pthread -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include" \
GMODULE_LIBS="-L/usr/local/glib-2.42/lib -Wl,--export-dynamic -lgmodule-2.0 -pthread -lrt" \
GTHREAD_CFLAGS="-pthread -I/usr/local/glib-2.42/include/glib-2.0 -I/usr/local/glib-2.42/lib/glib-2.0/include" \
GTHREAD_LIBS="-L/usr/local/glib-2.42/lib -lgthread-2.0 -pthread -lrt"
+++++++++++++++++++++++++++

sh bootstrap.sh
make
make install

成功编译安装完成之后,默认安装路径是:

/usr/local/mysql-proxy

④ 启动DBProxy

创建配置文件目录:

mkdir /usr/local/mysql-proxy/conf

初始化配置文件:建议配置文件和DBProxy实例名保持一致。

cp script/source.cnf.samples /usr/local/mysql-proxy/conf/source.cnf

脚本启动:$install_path/mysql-proxyd $instance_name(实例名) start/restart/stop 

root@dbtest:/usr/local/mysql-proxy# ./bin/mysql-proxyd source start
OK: MySQL-Proxy of source is started

root@dbtest:/usr/local/mysql-proxy# ps -ef | grep mysql
root             : ?        :: /usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/usr/local/mysql-proxy/conf/source.cnf
root         : ?        :: /usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/usr/local/mysql-proxy/conf/source.cnf

到此安装结束,接着介绍下配置文件。

配置文件

根据需要,把配置文件改名成proxy_test.cnf(/usr/local/mysql-proxy/conf),对应的实例名为proxy_test,具体的配置信息如下:

[mysql-proxy]
#设置DBProxy进程的所属用户以及日志的所属用户等 该命令在非root用户启动时不生效,只有在root用户下,配置存在的user时才会生效
#user=mysql
#管理接口的用户名
admin-username=guest
#管理接口的密码
admin-password=guest
#dbproxy监听的管理接口IP和端口
admin-address=
#用户名与其对应的加密过的MySQL密码,密码使用PREFIX/bin目录下的加密程序encrypt加密,将其替换为你的MySQL的用户名和加密密码!
pwds=dxy_proxy:uK6+XM9x2YRDabHitUtqIK5KOLfO
#dbproxy监听的工作接口IP和端口
proxy-address=
#dbproxy后端连接的MySQL主库的IP和端口
proxy-backend-addresses=
#dbproxy后端连接的MySQL从库的IP和端口,@后面的数字代表权重,用来作负载均衡,若省略则默认为1,可设置多项,用逗号分隔
proxy-read-only-backend-addresses=@
#在原有配置从库的基础上,可以对从库指定tag名
#例如:proxy-read-only-backend-addresses=xx.xx.xx.xx:$tag_mt@,xx.xx.xx.xx:
#实例名称,用于同一台机器上多个dbproxy实例间的区分
instance=proxy_test
#日志存放的路径
log-path=/var/log/dbproxy_log/
#SQL日志的开关,可设置为OFF、ON、REALTIME,OFF代表不记录SQL日志,ON代表记录SQL日志,REALTIME代表记录SQL日志且实时写入磁盘,默认为OFF
sql-log=REALTIME
#日志级别,分为message、warning、critical、error、debug五个级别
log-level=message
#工作线程数,对dbproxy的性能有很大影响,可根据情况适当设置,默认是1。DBProxy的线程分这几类:日志等辅助功能的线程、主线程、工作线程。这个参数指的是工作线程数
event-threads=
#设置dbproxy的运行方式,设为true时为守护进程方式,设为false时为前台方式,一般开发调试时设为false,线上运行时设为true
daemon=true
#设置dbproxy的运行方式,设为true时dbproxy会启动两个进程,一个为monitor,一个为worker,monitor在worker意外退出后会自动将其重启,设为false时只有worker,没有monitor,一般开发调试时设为false,线上运行时设为true
keepalive=
#分表设置,此例中person为库名,mt为表名,id为分表字段,3为子表数量,可设置多项,以逗号分隔,若不分表则不需要设置该项
#tables = person.mt.
#是否限制WHERE语句。ON: SELECT 后必须有WHERE 语句
select-where-limit=OFF
#dbproxy前面挂接的LVS的物理网卡的IP(注意不是虚IP),若有LVS且设置了client-ips则此项必须设置,否则可以不设置
#lvs-ips = 192.168.1.1
#DBProxy最大连接数
max-connections=
#长等待的阈值,超过该值则认定为同步等待时间过长,打印warning(单位ms)

#慢查询阈值,查询执行时间超过该阈值则认为是慢查询(单位ms)

#:不统计,:仅统计总体的响应时间,其中包括慢查询,:进行直方图统计;默认为1。
query-response-
#SQL日志文件最大大小,单位为字节,默认为1G
sql-log-
#保留的最大SQL日志文件个数,默认为0,不保留历史文件,仅保留当前文件
sql-log-
#后台MySQL版本号,默认为5.
mysql-version=5.6
#DBProxy 客户端连接的 timeout,如果连接到 DBProxy 的连接空闲超过此值,DBProxy 会关闭此连接(单位ms)

#DBProxy 连接池中连接的空闲时间
db-connection-idle-timeout=
#连接池中连接的生存周期
db-connection-max-age=
#后台MySQL最大连接数,默认为0,表示不限制
backend-max-thread-running=
#指定当 backend 的 thread running 数超过 backend-max-thread-running时,新来连接等待的时间(单位ms)
thread-running-
#SQL过滤统计缓存的SQL模板数,默认为0
#lastest-query-num=
#设置某类语句记入黑名单时,要满足的查询的执行时间,超过此值的查询会被做为放到黑名单的候选(单位ms)
#query-filter-
#设置某类语句记入黑名单时,要满足的查询的执行频率,超过此值的查询会被做为放到黑名单的候选,当同时满足时间和频率都超过指定值时,此类查询会被放入黑名单
#query-filter-frequent-threshold=10.000000
#SQL过滤频率统计时间窗口内的最小执行次数,根据频率和该参数计算时间窗口
#access-num-per-
#手动过滤SQL是否生效,默认为OFF
#manual-filter-flag=OFF
#自动过滤SQL是否生效,默认为OFF
#auto-filter-flag=OFF
#Shutdown DBProxy时,空闲事务的等待时间, 单位是s
shutdown-timeout=
#设置SQL的类型: client: 客户端的SQL日志、连接、关闭信息; backend: 后台MySQL执行的语句及其状态;all: client + backend
sql-log-mode=backend
#设置trace的DBProxy模块,设置后模块的运行情况会打印到admin日志中
log-trace-modules=
#设置proxy用户白名单,只能从本参数指定的主机进行访问DBProxy,为空时不对用户限制
user-hosts=
#移除backend时最长等待时间。在删除过程中,如果当前待删除的库正在事务中,则可以设置等待时间,没有设置等待时间,则参考该值,单位是s。
remove-backend-timeout=
#percentile统计功能的开关 ON:打开 OFF:关闭,默认OFF
#percentile-switch=OFF
#设置百分占比,默认95
#percentile=
#配置监控backends的用户信息
#backend-monitor-pwds=
#日志缓冲大小(日志条数),默认500
#sql-log-buffer-size=
#设置check state线程查询backend的信息的时间间隔,默认为4s
check-state-interval=
#设置check state线程查询backend的信息时的连接超时,默认为1s
check-state-conn-timeout=

具体的参数说明可以见官网。按照配置文件进行相关的设置,如日志目录、非root用户开启DBProxy等。相关操作如下:

:创建启动用户
useradd dbproxy

:创建日志目录
mkdir /var/log/dbproxy_log/

:修改相应目录文件权限
cd /usr/local/
chown -R dbproxy.dbproxy mysql-proxy/

cd /var/log/
chown -R dbproxy.dbproxy dbproxy_log/

:启动
root@dbtest:/usr/local/mysql-proxy# ./bin/mysql-proxyd proxy_test start
-- ::(main_cmdline)running as user: dbproxy (/)
OK: MySQL-Proxy of proxy_test is started

root@dbtest:/usr/local/mysql-proxy# ps -ef | grep mysql
dbproxy 25254 1 0 12:35 ? 00:00:00 /usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/usr/local/mysql-proxy/conf/proxy_test.cnf
dbproxy 25255 25254 0 12:35 ? 00:00:00 /usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/usr/local/mysql-proxy/conf/proxy_test.cnf

到此DBProxy已经正式启动了,后面就需要对其进行管理,在配置文件中设置了管理用户和proxy用户的账号、密码已经主从的相关信息,当然里面大部分信息都可以在管理接口进行set的动态修改设置。

DBProxy管理操作

admin接口(3309),proxy接口(3308)管理

1)admin 用户,通过admin接口访问

在配置文件里配置了管理接口的地址、端口和用户、密码。登入:

mysql -uguest -pguest -P3309 -h127.

和登陆mysql一样的命令,但是在命令行只能执行特定的命令,通过下面的命令来查看可以执行的操作。

select * from help;

通过下面的命令查看用户信息,包括admin和proxy用户:

guest@::>select * from pwds;
+-----------+------------------------------+-------+----------+-------+
| username  | password                     | hosts | backends | type  |
+-----------+------------------------------+-------+----------+-------+
| %         |                              |       |          | proxy |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |       |          | proxy |
| guest     | uqmOY9A=                     |       |          | admin |
+-----------+------------------------------+-------+----------+-------+
字段名称 含义
username 用户名
password 密码(encrypted_pwd)
hosts 用户对应的白名单ip
backends proxy用户名下绑定的bakcend(以tag名的形式标记)
type 类型信息。proxy:proxy端口的用户 admin:admin 端口的用户

不能添加、删除admin用户,只能修改admin用户的用户名、密码和访问IP。

① 修改用户名和密码:alter admin user $user:$pwd

guest@::>alter admin user zjy:zjy;
Empty set (0.00 sec)

guest@::>^DBye
root@dbtest:~# mysql -uguest -pguest -P3309 -h127.
Warning: Using a password on the command line interface can be insecure.
ERROR  (): unknown user
root@dbtest:~# mysql -uzjy -pzjy -P3309 -h127.
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection
Server version: -agent-admin

Copyright (c) , , 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.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

zjy@::>select * from pwds;
+-----------+------------------------------+-------+----------+-------+
| username  | password                     | hosts | backends | type  |
+-----------+------------------------------+-------+----------+-------+
| %         |                              |       |          | proxy |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |       |          | proxy |
| zjy       | p7aS                         |       |          | admin |
+-----------+------------------------------+-------+----------+-------+
 rows in set (0.00 sec)

② 限制访问IP。设置连接admin端口的ip白名单,从而加强对admin账号的安全管理,限制部分ip对admin端口的登录请求,即只有在白名单内的ip才能够允许连接admin端口(不设置时,默认不限制),

添加白名单:alter admin user hosts $ip,$ip ,多个用逗号分隔。

mysql> add admin user hosts 192.168.200.64,192.168.200.25;
Empty set (0.03 sec)

mysql> select * from pwds;
+-----------+------------------------------+-------------------------------+----------+-------+
| username  | password                     | hosts                         | backends | type  |
+-----------+------------------------------+-------------------------------+----------+-------+
| %         |                              |                               |          | proxy |
| sbtest    | rr6fdddI                     |                               |          | proxy |
| guest     | uqmOY9A=                     | 192.168.200.25,192.168.200.64 |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |                               |          | proxy |
+-----------+------------------------------+-------------------------------+----------+-------+
 rows in set (0.00 sec)

mysql> ^DBye
# mysql -uguest -pguest -P3309 -h127.0.0.1  #限制IP,连不上了
Warning: Using a password on the command line interface can be insecure.
ERROR  (): unpermitted host

从200.64上连接: #在白名单里,可以连接
$ mysql -uguest -pguest -P3309 -h192.
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection
Server version: -agent-admin
...

删除白名单:remove admin user hosts $ip,$ip

guest@::>remove admin user hosts 192.168.200.64;
Empty set (0.00 sec)

guest@::>select * from pwds;
+-----------+------------------------------+----------------+----------+-------+
| username  | password                     | hosts          | backends | type  |
+-----------+------------------------------+----------------+----------+-------+
| %         |                              |                |          | proxy |
| sbtest    | rr6fdddI                     |                |          | proxy |
| guest     | uqmOY9A=                     | 192.168.200.25 |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |                |          | proxy |
+-----------+------------------------------+----------------+----------+-------+
 rows in set (0.00 sec)

guest@::>^DBye
$ mysql -uguest -pguest -P3309 -h192.168.200.24    #不在白名单
Warning: Using a password on the command line interface can be insecure.
ERROR  (): unpermitted host

③ 保存修改到配置文件。要是通过管理接口进行的一些修改,仅在当时是有效,若DBProxy重启之后则会丢失配置,通过 save config来持久到配置文件。

mysql> save config;
Empty set (0.01 sec)

2)Proxy 用户,通过proxy接口访问

能添加、删除proxy用户和访问IP,不能修改proxy用户的用户名、密码。

这里结合程序一起说明:比如有2套程序需要访问数据库sbtest,程序所在IP:192.168.200.64和192.168.200.25;DBproxy所在IP:192.168.200.24,后端数据库所在IP(配置文件里已经设置):192.168.200.202(M),192.168.200.132(S)。

① 添加和删除proxy用户:add pwd $user:$pwd

#明文密码
mysql> add pwd sbtest:sbtest;
Empty set (0.00 sec)

#加密密码
先获取加密密码:
:/usr/local/mysql-proxy/bin# ./encrypt sbtest
rr6fdddI
再添加账号:
mysql> add enpwd sbtest:rr6fdddI;
Empty set (0.00 sec)

mysql> select * from pwds;
+-----------+------------------------------+-------+----------+-------+
| username  | password                     | hosts | backends | type  |
+-----------+------------------------------+-------+----------+-------+
| %         |                              |       |          | proxy |
| sbtest    | rr6fdddI                     |       |          | proxy |
| guest     | uqmOY9A=                     |       |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |       |          | proxy |
+-----------+------------------------------+-------+----------+-------+

② 限制访问IP,允许200.64/25访问DBPrxoy。这里比较特别,在用户的白名单的上一层还有一个系统白名单,即username是%,大致的验证流程如下:

大致的意思是设置了系统白名单则需要首先通过系统白名单验证,要是也设置了用户白名单则再通过用户白名单验证,若均没有设置白名单则直接访问不需要验证(默认)。所以我们这里系统白名单没有设置,只需要设置用户白名单即可。既然系统白名单有设置,为了安全还是设置一下,大致流程如下:

系统白名单设置:

添加:add user hosts %@host[|host];

mysql> add user hosts %@.%|.%;
Empty set (0.00 sec)

mysql> select * from pwds;
+-----------+------------------------------+-----------------------------+----------+-------+
| username  | password                     | hosts                       | backends | type  |
+-----------+------------------------------+-----------------------------+----------+-------+
| %         |                              | .%|.% |          | proxy |
| sbtest    | rr6fdddI                     |                             |          | proxy |
| guest     | uqmOY9A=                     |                             |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |                             |          | proxy |
+-----------+------------------------------+-----------------------------+----------+-------+

系统白名单设置了允许200和201网段的IP访问,其他IP访问不了。

删除指定的白名单:remove user hosts %@192.168.201.%;

删除所有白名单:remove user host %;

#删除指定的白名单
mysql> remove user hosts %@.%;
Empty set (0.00 sec)

mysql> select * from pwds;
+-----------+------------------------------+---------------+----------+-------+
| username  | password                     | hosts         | backends | type  |
+-----------+------------------------------+---------------+----------+-------+
| %         |                              | .% |          | proxy |
| sbtest    | rr6fdddI                     |               |          | proxy |
| guest     | uqmOY9A=                     |               |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |               |          | proxy |
+-----------+------------------------------+---------------+----------+-------+
 rows in set (0.00 sec)

#删除所有白名单
mysql> remove user hosts %;
Empty set (0.00 sec)

mysql> select * from pwds;
+-----------+------------------------------+-------+----------+-------+
| username  | password                     | hosts | backends | type  |
+-----------+------------------------------+-------+----------+-------+
| %         |                              |       |          | proxy |
| sbtest    | rr6fdddI                     |       |          | proxy |
| guest     | uqmOY9A=                     |       |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |       |          | proxy |
+-----------+------------------------------+-------+----------+-------+
 rows in set (0.00 sec)

当添加完系统白名单之后,若用户白名单没设置,只要是200和201网段的都可以用账号访问。好像还是不太安全,后面继续用户白名单设置。

mysql> select * from pwds;
+-----------+------------------------------+-----------------------------+----------+-------+
| username  | password                     | hosts                       | backends | type  |
+-----------+------------------------------+-----------------------------+----------+-------+
| %         |                              | .%|.% |          | proxy |
| sbtest    | rr6fdddI                     |                             |          | proxy |
| guest     | uqmOY9A=                     | .%               |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |                             |          | proxy |
+-----------+------------------------------+-----------------------------+----------+-------+

通过非200和201网段访问:
$ mysql -usbtest -psbtest -P3308 -h192.
Warning: Using a password on the command line interface can be insecure.
ERROR  (): Access denied(host is forbidden) for user 'sbtest'@'192.168.204.187' (using password: YES)

用户白名单设置:根据上面的条件设置具体的白名单

添加:add user hosts $user@host[|host];

mysql> add user hosts sbtest@192.168.200.64|192.168.200.25;
Empty set (0.00 sec)

mysql> select * from pwds;
+-----------+------------------------------+-------------------------------+----------+-------+
| username  | password                     | hosts                         | backends | type  |
+-----------+------------------------------+-------------------------------+----------+-------+
| %         |                              | .%|.%   |          | proxy |
| sbtest    | rr6fdddI                     | 192.168.200.25|192.168.200.64 |          | proxy |
| guest     | uqmOY9A=                     | .%                 |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |                               |          | proxy |
+-----------+------------------------------+-------------------------------+----------+-------+

若在非上面指定的白名单(IP)中执行则报错:

$mysql -usbtest -psbtest -P3308 -h192.
Warning: Using a password on the command line interface can be insecure.
ERROR  (): Access denied(host is forbidden) for user 'sbtest'@'192.168.200.55' (using password: YES)

删除用户白名单:remove user hosts $user@$host|$host

mysql> remove user hosts sbtest@192.168.200.25|192.168.200.64;
Empty set (0.00 sec)

mysql> select * from pwds;
+-----------+------------------------------+-----------------------------+----------+-------+
| username  | password                     | hosts                       | backends | type  |
+-----------+------------------------------+-----------------------------+----------+-------+
| %         |                              | .%|.% |          | proxy |
| sbtest    | rr6fdddI                     |                             |          | proxy |
| guest     | uqmOY9A=                     | .%               |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |                             |          | proxy |
+-----------+------------------------------+-----------------------------+----------+-------+
 rows in set (0.00 sec)

若系统白名单设置不允许200网段登陆,则直接不会走到验证用户白名单这不,直接退出。

mysql> remove user hosts %@.%;
Empty set (0.00 sec)

mysql> select * from pwds;
+-----------+------------------------------+-------------------------------+----------+-------+
| username  | password                     | hosts                         | backends | type  |
+-----------+------------------------------+-------------------------------+----------+-------+
| %         |                              | .%                 |          | proxy |
| sbtest    | rr6fdddI                     | 192.168.200.25|192.168.200.64 |          | proxy |
| guest     | uqmOY9A=                     | .%                 |          | admin |
| dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO |                               |          | proxy |
+-----------+------------------------------+-------------------------------+----------+-------+
 rows in set (0.00 sec)
#系统白名单只允许201网段访问。即使用户白名单允许200网段访问也不行:
$mysql -usbtest -psbtest -P3308 -h192.
Warning: Using a password on the command line interface can be insecure.
ERROR  (): Access denied(host is forbidden) for user 'sbtest'@'192.168.200.64' (using password: YES)

③ 权限相关,proxy用户必须在后端数据库里存在并且保持一致的密码。

通过上面设置了白名单,加强了数据库的安全,这里还有另一个需要注意的:访问的账号(sbtest)需要在后端数据库(MS)里面存在并且密码和DBproxy设置的一样,而且拥有相应的权限,否则报错:

$mysql -usbtest -psbtest -P3308 -h192.168.200.24  #可以访问DBproxy...
sbtest@::>show databases;   #不能执行命令,因为后端数据库没有账号或则密码错误。
ERROR  (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection
Current database: *** NONE ***

ERROR  (): Internal Error: I have no server backend, closing connection

把上面的配置持久化到配置文件(save config)

user-hosts=%@.%|.%,sbtest@192.168.200.25|192.168.200.64
admin-user-hosts=.%

backends管理

DBProxy启动之前需要在配置文件中配置backend信息。backend按照读写分类,可分为读写backend库(可以读写操作)和只读backend库(仅可以读操作),其中在配置文件中读写backend库是必须配置的。连接DBProxy的客户端所发送的sql语句,根据DBProxy内部的策略,会将sql语句发送至特定的backend库执行。具体的说明可以看文档

配置文件参数:

proxy-backend-addresses:配置主库IP:Port
proxy-read-only-backend-addresses:配置从库IP:Port@权重,多个,号分隔。
select * from backends;
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state | type | weight | tag  | threads_running |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
|            |  |          | up    | rw   |       | NULL |                |
|            |  |          | up    | ro   |       | NULL |                |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+

要是配置文件里没有配置,也可以到admin接口里进行设置:

添加一个主库:add master $ip:$port

添加一个从库:add slave $ip:$port

guest@::>select * from backends;
Empty set (0.00 sec)

guest@::>add master ;
Empty set (0.00 sec)

guest@::>add slave ;
Empty set (0.00 sec)

guest@::>select * from backends;
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state | type | weight | tag  | threads_running |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
|            |  |          | up    | rw   |       | NULL |                |
|            |  |          | up    | ro   |       | NULL |                |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
 rows in set (0.00 sec)

guest@::>

当已经存在一个主库,再添加一个主库会报错:

guest@::>add master ;
ERROR  (): there is already one RW backend

DBProxy只允许一主一从或一主多从,不能多主。

删除backends:remove backend $backend_ndx [timeout $int]

timeout的意义是:在删除过程中,如果当前待删除的库正在事务中,则可以设置等待时间,没有设置等待时间,则参考系统全局的remove-backend-timeout的值。若当前backend中有连接在事务中,则等待该事务完成,若超过等待时间,该事务仍旧没有执行完,则强制对backend进行删除。

guest@::>select * from backends;
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state | type | weight | tag  | threads_running |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
|            |  |          | up    | rw   |       | NULL |                |
|            |  |          | up    | ro   |       | NULL |                |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
 rows in set (0.00 sec)

guest@::>remove backend ;
Empty set (0.00 sec)

guest@::>remove backend  timeout ;
Empty set (0.00 sec)

guest@::>select * from backends;
Empty set (0.00 sec)
字段名称 含义
backend_ndx 每个backend的索引值,该值是动态变化的,从1开始依次排序,主库序号靠前
address backend的主机ip和port
hostname 显示ip地址对应的主机名称
state 显示backend的状态。状态有:UP/DOWN/REMOVING/OFFLINE/OFFLING
type 代表backend的类型。类型有:rw:主库,可进行读写操作 ro:从库,只可进行读取操作
weight 代表从库的权重。主库权重为0,代表其没有权重属性;从库的权重最小值为1
tag backend的标签名
threads_running 当前backend上正在运行的并发线程数

这里说明一下state和weight的意义,tag的说明可以看文档,这里不做说明。

state:proxy根据其状态进行读写策略,当状态是down或则是offline的时候,表示已经不可用,读写都不会分配过来,会分流到其他的从库或则主库。即:当一主一从,从down了,之后的读写都会分到主上去。直到state正常之后就会自动分流回来。这个可以通过sql日志(日志目录下的sql目录中)看到。如:

这里有个不足是DBProxy不关心主从复制的可靠性和稳定性,若从延迟大,读的操作还是会到延迟的从上去,不会自动的下线从库。这里需要DBA自己介入,通过判断延迟的阈值去admin接口执行set offline来手动更新state字段,使延迟大的从先下线。

set offline $backend_ndx [timeout $int];set online $backend_ndx;timeout的意义和上面的一致。

mysql> select * from backends;
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state | type | weight | tag  | threads_running |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
|            |  |          | up    | rw   |       | NULL |                |
|            |  |          | up    | ro   |       | NULL |                |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
 rows in set (0.00 sec)

mysql> set offline ;
Empty set (0.00 sec)

通过查看日志(/var/log/dbproxy_log/sql),看到这时的读都去了主库。

mysql> select * from backends;
+-------------+----------------------+----------+---------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state   | type | weight | tag  | threads_running |
+-------------+----------------------+----------+---------+------+--------+------+-----------------+
|            |  |          | up      | rw   |       | NULL |                |
|            |  |          | offline | ro   |       | NULL |                |
+-------------+----------------------+----------+---------+------+--------+------+-----------------+
 rows in set (0.00 sec)

mysql> set online ;
Empty set (0.00 sec)

mysql> select * from backends;
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state | type | weight | tag  | threads_running |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
|            |  |          | up    | rw   |       | NULL |                |
|            |  |          | up    | ro   |       | NULL |                |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
 rows in set (0.00 sec)

通过查看日志(/var/log/dbproxy_log/sql),看到这时的读都去了从库。

若把主库set offline,读不受影响;写不能执行。要是有多个从,可以通过weight实现负载均衡:alter slave weight $backend_index $weight

mysql> select * from backends;
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state | type | weight | tag  | threads_running |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
|            |  |          | up    | rw   |       | NULL |                |
|            |  |          | up    | ro   |       | NULL |                |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
 rows in set (0.00 sec)

mysql> alter slave weight  ;
Empty set (0.00 sec)

mysql> select * from backends;
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
| backend_ndx | address              | hostname | state | type | weight | tag  | threads_running |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
|            |  |          | up    | rw   |       | NULL |                |
|            |  |          | up    | ro   |     | NULL |                |
+-------------+----------------------+----------+-------+------+--------+------+-----------------+
 rows in set (0.00 sec)

权重越大,其被读的几率就越大,要是某一个从库性能比较好,权重可以设置的大一点。这里补充一下,通过上面的sql日志的截图,为什么写一次之后马上读取却还是在主上呢?这个是由于DBProxy的限制造成的:update、insert、delete、replace之后,下一条 sql有可能 去读 affected row 的行数。有的select 语句带有特殊函数的,为了随后的sql 查上次语句的某些状态,这种可能需要上下文信息的,DBProxy暂时未把后台连接放回连接池。所以第一个读会使用上次操作的后台连接,以后就走从库了。

平滑关闭DBProxy

支持DBProxy立即关闭和平滑关闭。立即关闭是指立即结束DBProxy进程;平滑关闭是指若有正在执行的事务,等待shutdown-timeout时间,如果仍有执行的事务,则直接结束DBProxy进程。

shutdown [NORMAL|IMMEDIATE]; #NORMAL:平滑下线 IMMEDIATE:立即下线 

mysql> shutdown normal;
Empty set (0.01 sec)

介绍到这里,大致已经能让基本的读写分离功能用起来了,DBProxy里还有很多其他功能,包括为SLAVE打TAG、指定从指定从库上读取,指定到主上读等等。这里暂时没用到就不介绍了,所有的内容在手册里有了详尽的介绍,有兴趣的可以去查看。再看文档都时候遇到过一些问题,这里感谢DBProxy开发同学<紫气东来>的解惑。

DBProxy性能测试

在上DBProxy中间件的时候必须还要做个测试,因为用DBProxy进行数据库操作毕竟多了一层访问,不知道性能上有多大的折损。这里通过sysbench进行相关的测试来大致了解一下大概能有多大的差别。关于sysbench的安装和使用可以看sysbench 安装、使用和测试,也可以参考使用sysbench对mysql压力测试

1)安装,用最新版的sysbench,其他版也没问题。

:安装依赖包
apt-get install make automake libtool pkg-config libaio-dev vim-common libmysqlclient-dev

:下载
git clone https://github.com/akopytov/sysbench.git

:安装
cd sysbench
sh autogen.sh
./configure --prefix=/usr/local/sysbench-1.1
make
make install

2)使用,最新版1.1的使用方法和0.4.12,甚至是0.5都有了差别,可以通过help来调整

dxy@redis2:/usr/local/sysbench-1.1$ ./bin/sysbench --version
sysbench -6bb15d7
dxy@redis2:/usr/local/sysbench-1.1$ ./bin/sysbench cpu help
sysbench -6bb15d7 (using bundled LuaJIT -beta2)

cpu options:
  --cpu-max-prime=N upper limit ]

dxy@redis2:/usr/local/sysbench-1.1$ ./bin/sysbench ./share/sysbench/oltp_read_write.lua help
sysbench -6bb15d7 (using bundled LuaJIT -beta2)

oltp_read_write.lua options:
  --distinct_ranges=N           Number of SELECT DISTINCT queries per transaction []
  --sum_ranges=N                Number of SELECT SUM() queries per transaction []
  --skip_trx[=on|off]           Don't start explicit transactions and execute all queries in the AUTOCOMMIT mode [off]
  --secondary[=on|off]          Use a secondary index in place of the PRIMARY KEY [off]
  --create_secondary[=on|off]   Create a secondary index in addition to the PRIMARY KEY [on]
  --index_updates=N             Number of UPDATE index queries per transaction []
  --range_size=N                Range size ]
  --auto_inc[=on|off]           Use AUTO_INCREMENT column as Primary Key (for MySQL), or its alternatives in other DBMS. When disabled, use client-generated IDs [on]
  --delete_inserts=N            Number of DELETE/INSERT combination per transaction []
  --tables=N                    Number of tables []
  --mysql_storage_engine=STRING Storage engine, if MySQL is used [innodb]
  --non_index_updates=N         Number of UPDATE non-index queries per transaction []
  --table_size=N                Number of rows per table []
  --pgsql_variant=STRING        Use this PostgreSQL variant when running with the PostgreSQL driver. The only currently supported variant is
  --simple_ranges=N             Number of simple range SELECT queries per transaction []
  --order_ranges=N              Number of SELECT ORDER BY queries per transaction []
  --range_selects[=on|off]      Enable/disable all range SELECT queries [on]
  --point_selects=N             Number of point SELECT queries per transaction []

3)测试,这里测试选择通过select、insert、update和混合模式进行测试说明,自带的lua脚本有:

$ ls -lh /usr/local/sysbench-1.1/share/sysbench/
total 64K
-rwxr-xr-x  root root .5K  4月  : bulk_insert.lua
-rw-r--r--  root root  14K  4月  : oltp_common.lua
-rwxr-xr-x  root root .1K  4月  : oltp_delete.lua #删
-rwxr-xr-x  root root .0K  4月  : oltp_insert.lua #写
-rwxr-xr-x  root root .3K  4月  : oltp_point_select.lua
-rwxr-xr-x  root root .7K  4月  : oltp_read_only.lua #只读
-rwxr-xr-x  root root .8K  4月  : oltp_read_write.lua #读写
-rwxr-xr-x  root root .1K  4月  : oltp_update_index.lua  #更新索引字段
-rwxr-xr-x  root root .2K  4月  : oltp_update_non_index.lua #更新非索引字段
-rwxr-xr-x  root root .5K  4月  : oltp_write_only.lua #只写
-rwxr-xr-x  root root .9K  4月  : select_random_points.lua
-rwxr-xr-x  root root .1K  4月  : select_random_ranges.lua
drwxr-xr-x  root root .0K  4月  : tests

进入执行文件目录 cd /usr/local/sysbench-1.1

①:读写混合(oltp_read_write.lua)

直连数据库:

 --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=  --max-requests= -- --threads=4 --tables=  --table-size= prepare/run/cleanup  

代理连数据库

./bin/sysbench --test=./share/sysbench/oltp_read_write.lua --mysql-host= --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=  --max-requests= -- --threads= --tables=3  --table-size= --skip-trx=on --db- prepare/run/cleanup 

这里需要注意:因为DBProxy的限制,如不支持prepare,需要添加--skip-trx=on --db-ps-mode=disable--skip-trx=on --db-ps-mode=disable;由于不加事务出现的重复key的几率比较大,所以需要跳过错误:--mysql-ignore-errors=1062。

测试的线程:1、4、8、16、32、64、128

把上面数据以折线图的形式表现:

TPS:

QPS:

②:只读(oltp_read_only.lua)

直连数据库:

./bin/sysbench --test=./share/sysbench/oltp_read_only.lua --mysql-host= --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=  --max-requests= -- --threads= --tables=  --table-size= run

代理连数据库

./bin/sysbench --test=./share/sysbench/oltp_read_only.lua --mysql-host= --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=  --max-requests= -- --threads= --tables=  --table-size= --skip-trx=on --db- run

测试的线程:1、4、8、16、32、64、128

把上面数据以折线图的形式表现:

TPS:

QPS:

③:只写入(oltp_insert.lua)

直连数据库:

./bin/sysbench --test=./share/sysbench/oltp_insert.lua --mysql-host=192.168.200.202 --mysql-port=3306 --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=10  --max-requests=0 --time=300 --threads=1 --tables=3  --table-size=500000 run

代理连数据库

./bin/sysbench --test=./share/sysbench/oltp_insert.lua --mysql-host=192.168.200.24 --mysql-port=3308 --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=10  --max-requests=0 --time=300 --threads=1 --tables=3  --table-size=500000 --skip-trx=on --db-ps-mode=disable --mysql-ignore-errors=1062 run

测试的线程:1、4、8、16、32、64、128

把上面数据以折线图的形式表现:

QPS:

④:只更新(oltp_update_index.lua)

直连数据库:

./bin/sysbench --test=./share/sysbench/oltp_update_index.lua --mysql-host=192.168.200.202 --mysql-port=3306 --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=10  --max-requests=0 --time=300 --threads=1 --tables=3  --table-size=500000 run

代理连数据库

./bin/sysbench --test=./share/sysbench/oltp_update_index.lua --mysql-host=192.168.200.24 --mysql-port=3308 --mysql-user=sbtest --mysql-password=sbtest --mysql-db=sbtest  --report-interval=10  --max-requests=0 --time=300 --threads=1 --tables=3  --table-size=500000 --skip-trx=on --db-ps-mode=disable --mysql-ignore-errors=1062 run

测试的线程:1、4、8、16、32、64、128

把上面数据以折线图的形式表现:

QPS:

测试小结:

在读写混合的模式下:代理的TPS是直连的TPS的65%~80%,线程越少差距越大;代理的QPS是直连的QPS的60%~75%,线程越少差距越大。

在纯读模式下:和读写混合模式表现相近。

在写和更新模式下:因为代理也只访问主,它们的差距就是网络开销上,代理需要二次访问,不需要轮训。代理的QPS是直连QPS的65%~90%。

当sysbench的并发测试线程较少时,代理和直连DB的QPS差距较大。这主要是因为当sysbench并发线程少时,DBProxy的性能没有得到充分的发挥,sysbench只有很少的线程向DBProxy发送请求,此时网络延迟对QPS和TPS的影响是最主要的。 当sysbench的并发测试线程较大时,此时DBProxy的性能就得到了充分的发挥, 此时QPS和TPS的对比是代理与直连DB性能对比的真实的反应,网络延迟对QPS的影响作用就显得很小了。

由此看来利用DBProxy转发SQL请求带来的性能下降虽有下降,通过Open-Falcon对数据库指标的监控,这个性能下降是完全可以接受的。

总结

通过上面的一些基本介绍,大致了解了DBProxy读写分离功能的使用,和直连DB性能的下降也可以接受。关于DBproxy的其他功能内容在手册里有了详尽的介绍,需要的话可以去查看。

这里还有2个问题:一个问题是在读写分离上面,要是从库延迟超过阈值,导致读写分离不可用,则需要自己编写脚本去连接admin接口控制问题从库的下线。另一个问题是即使数据库有MHA保证,但是DBProxy是一个单点,所以需要保证DBProxy的高可用,这2个问题后面会继续研究,以及会再看另一个中间件proxysql来和DBProxy进行对比选型。

参考文档

DBProxy手册

Atlas手册

美团点评DBProxy读写分离使用说明的更多相关文章

  1. DBProxy 读写分离使用说明

    美团点评DBProxy读写分离使用说明   目的 因为业务架构上需要实现读写分离,刚好前段时间美团点评开源了在360Atlas基础上开发的读写分离中间件DBProxy,关于其介绍在官方文档已经有很详细 ...

  2. MySQL ProxySQL读写分离使用初探

    目的 在美团点评DBProxy读写分离使用说明文章中已经说明了使用目的,本文介绍ProxySQL的使用方法以及和DBProxy的性能差异.具体的介绍可以看官网的相关说明,并且这个中间件也是percon ...

  3. MySQL ProxySQL读写分离实践

    目的 在上一篇文章MySQL ProxySQL读写分离使用初探里初步介绍了ProxySQL的使用,本文继续介绍它的一些特点和DBProxy的性能差异.深入一些去了解ProxySQL,通过测试来说明Pr ...

  4. 美团点评基于MGR的CMDB高可用架构搭建之路【转】

    王志朋 美团点评DBA 曾在京东金融担任DBA,目前就职于美团点评,主要负责金融业务线数据库及基础组件数据库的运维. MySQL Group Replication(以下简称MGR),于5.7.17版 ...

  5. 美团点评MySQL数据库高可用架构从MMM到MHA+Zebra以及MHA+Proxy的演进

    本文介绍最近几年美团点评MySQL数据库高可用架构的演进过程,以及我们在开源技术基础上做的一些创新.同时,也和业界其它方案进行综合对比,了解业界在高可用方面的进展,和未来我们的一些规划和展望. MMM ...

  6. Leaf——美团点评分布式ID生成系统

    背景 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店.猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数 ...

  7. 基于Spring和Mybatis拦截器实现数据库操作读写分离

    首先需要配置好数据库的主从同步: 上一篇文章中有写到:https://www.cnblogs.com/xuyiqing/p/10647133.html 为什么要进行读写分离呢? 通常的Web应用大多数 ...

  8. ProxySQL 配置详解及读写分离(+GTID)等功能说明 (完整篇)

    ProxySQL是灵活强大的MySQL代理层, 是一个能实实在在用在生产环境的MySQL中间件,可以实现读写分离,支持 Query 路由功能,支持动态指定某个 SQL 进行 cache,支持动态加载配 ...

  9. ProxySQL实现Mysql读写分离 - 部署手册

    ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎.ProxySQL是用C++语言开发的,也是percona推的一款中间件,虽然也是一个轻量级产品,但性能很好(据测试,能处理千亿级的数 ...

随机推荐

  1. XlFileFormat Enumeration

    https://msdn.microsoft.com/zh-cn/library/office/ff198017.aspx

  2. 深入理解 NodeList

    在web前端编程中,我们通常会通过document.getElementsByTagName的方法取出一组相同标签的dom元素,比如: var list = document.getElementsB ...

  3. Mysql常用命令详解

    Mysql安装目录 数据库目录 /var/lib/mysql/ 配置文件 /usr/share/mysql(mysql.server命令及配置文件) 相关命令 /usr/bin(mysqladmin ...

  4. 【软件使用】TortoiseSVN版本管理软件使用简单说明

    TortoiseSVN版本管理软件使用简单说明 很多时候在写一个小的项目不想使用github等工具,只想简单在本地搭建一个版本管理器.那么TortoiseSVN就非常适合. 第一步:下载Tortois ...

  5. zabbix的邮件报警

    邮件报警插件 #!/usr/bin/python #coding:utf-8 import smtplib from email.MIMEText import MIMEText import os ...

  6. [Mime] MimeEntity--MimeEntity Mime实体帮助类 (转载)

    点击下载 MimeEntity.rar 这个类是关于Mime实体的类看下面代码吧 /// <summary> /// 类说明:Assistant /// 编 码 人:苏飞 /// 联系方式 ...

  7. CSS3 Pie工具可以让IE6至IE8版本实现大多数的CSS3修饰特性,如圆角、阴影、渐变等

    css3 pie使用方法: <!doctype html> <html lang="en"> <head> <meta charset=& ...

  8. C++STL中map容器的说明和使用技巧(杂谈)

    1.map简介 map是一类关联式容器.它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响.对于迭代器来说,可以修改实值,而不能修改key. 2.map的功能 自 ...

  9. bzoj 3028: 食物 -- 母函数

    3028: 食物 Time Limit: 3 Sec  Memory Limit: 128 MB Description 明明这次又要出去旅游了,和上次不同的是,他这次要去宇宙探险! 我们暂且不讨论他 ...

  10. 机器学习技法笔记:07 Blending and Bagging

    Roadmap Motivation of Aggregation Uniform Blending Linear and Any Blending Bagging (Bootstrap Aggreg ...