目的
因为业务架构上需要实现读写分离,刚好前段时间美团点评开源了在360Atlas基础上开发的读写分离中间件DBProxy,关于其介绍在官方文档已经有很详细的说明了,其特性主要有:读写分离、负载均衡、支持分表、IP过滤、sql语句黑名单、DBA平滑下线DB、从库流量配置、动态加载配置项。本文大致简单的介绍自己在使用中如何快速安装和使用DBProxy。
环境
Ubuntu 16.04.1 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 install libmysqlclient-dev libgcrypt11-dev pkg-config lua5.1-0 liblua5.1-0-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-2.42.0.tar.xz tar xvf glib-2.42.0.tar编译: cd glib-2.42.0 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 "$(dirname "$0")"; 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 startedroot@dbtest:/usr/local/mysql-proxy# ps -ef | grep mysql root 23000 1 0 02:44 ? 00:00:00 /usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/usr/local/mysql-proxy/conf/source.cnf root 23001 23000 0 02:44 ? 00:00:00 /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=0.0.0.0:3309 #用户名与其对应的加密过的MySQL密码,密码使用PREFIX/bin目录下的加密程序encrypt加密,将其替换为你的MySQL的用户名和加密密码! pwds=dxy_proxy:uK6+XM9x2YRDabHitUtqIK5KOLfO #dbproxy监听的工作接口IP和端口 proxy-address=0.0.0.0:3308 #dbproxy后端连接的MySQL主库的IP和端口 proxy-backend-addresses=192.168.200.202:3306 #dbproxy后端连接的MySQL从库的IP和端口,@后面的数字代表权重,用来作负载均衡,若省略则默认为1,可设置多项,用逗号分隔 proxy-read-only-backend-addresses=192.168.200.132:3306@3 #在原有配置从库的基础上,可以对从库指定tag名 #例如:proxy-read-only-backend-addresses=xx.xx.xx.xx:3306$tag_mt@10,xx.xx.xx.xx:3306 #实例名称,用于同一台机器上多个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=16 #设置dbproxy的运行方式,设为true时为守护进程方式,设为false时为前台方式,一般开发调试时设为false,线上运行时设为true daemon=true #设置dbproxy的运行方式,设为true时dbproxy会启动两个进程,一个为monitor,一个为worker,monitor在worker意外退出后会自动将其重启,设为false时只有worker,没有monitor,一般开发调试时设为false,线上运行时设为true keepalive=1 #分表设置,此例中person为库名,mt为表名,id为分表字段,3为子表数量,可设置多项,以逗号分隔,若不分表则不需要设置该项 #tables = person.mt.id.3 #是否限制WHERE语句。ON: SELECT 后必须有WHERE 语句 select-where-limit=OFF #dbproxy前面挂接的LVS的物理网卡的IP(注意不是虚IP),若有LVS且设置了client-ips则此项必须设置,否则可以不设置 #lvs-ips = 192.168.1.1 #DBProxy最大连接数 max-connections=1000 #长等待的阈值,超过该值则认定为同步等待时间过长,打印warning(单位ms) long-wait-time=500 #慢查询阈值,查询执行时间超过该阈值则认为是慢查询(单位ms) long-query-time=3000 #0:不统计,1:仅统计总体的响应时间,其中包括慢查询,2:进行直方图统计;默认为1。 query-response-time-stats=2 #SQL日志文件最大大小,单位为字节,默认为1G sql-log-file-size=1073741824 #保留的最大SQL日志文件个数,默认为0,不保留历史文件,仅保留当前文件 sql-log-file-num=50 #后台MySQL版本号,默认为5.5 mysql-version=5.6 #DBProxy 客户端连接的 timeout,如果连接到 DBProxy 的连接空闲超过此值,DBProxy 会关闭此连接(单位ms) wait-timeout=86400 #DBProxy 连接池中连接的空闲时间 db-connection-idle-timeout=3600 #连接池中连接的生存周期 db-connection-max-age=7200 #后台MySQL最大连接数,默认为0,表示不限制 backend-max-thread-running=0 #指定当 backend 的 thread running 数超过 backend-max-thread-running时,新来连接等待的时间(单位ms) thread-running-sleep-delay=10 #SQL过滤统计缓存的SQL模板数,默认为0 #lastest-query-num=10000 #设置某类语句记入黑名单时,要满足的查询的执行时间,超过此值的查询会被做为放到黑名单的候选(单位ms) #query-filter-time-threshold=10000 #设置某类语句记入黑名单时,要满足的查询的执行频率,超过此值的查询会被做为放到黑名单的候选,当同时满足时间和频率都超过指定值时,此类查询会被放入黑名单 #query-filter-frequent-threshold=10.000000 #SQL过滤频率统计时间窗口内的最小执行次数,根据频率和该参数计算时间窗口 #access-num-per-time-window=10 #手动过滤SQL是否生效,默认为OFF #manual-filter-flag=OFF #自动过滤SQL是否生效,默认为OFF #auto-filter-flag=OFF #Shutdown DBProxy时,空闲事务的等待时间, 单位是s shutdown-timeout=600 #设置SQL的类型: client: 客户端的SQL日志、连接、关闭信息; backend: 后台MySQL执行的语句及其状态;all: client + backend sql-log-mode=backend #设置trace的DBProxy模块,设置后模块的运行情况会打印到admin日志中 log-trace-modules=0 #设置proxy用户白名单,只能从本参数指定的主机进行访问DBProxy,为空时不对用户限制 user-hosts= #移除backend时最长等待时间。在删除过程中,如果当前待删除的库正在事务中,则可以设置等待时间,没有设置等待时间,则参考该值,单位是s。 remove-backend-timeout=1024 #percentile统计功能的开关 ON:打开 OFF:关闭,默认OFF #percentile-switch=OFF #设置百分占比,默认95 #percentile=95 #配置监控backends的用户信息 #backend-monitor-pwds= #日志缓冲大小(日志条数),默认500 #sql-log-buffer-size= #设置check state线程查询backend的信息的时间间隔,默认为4s check-state-interval=4 #设置check state线程查询backend的信息时的连接超时,默认为1s check-state-conn-timeout=1
具体的参数说明可以见官网。按照配置文件进行相关的设置,如日志目录、非root用户开启DBProxy等。相关操作如下:
1:创建启动用户 useradd dbproxy2:创建日志目录 mkdir /var/log/dbproxy_log/3:修改相应目录文件权限 cd /usr/local/ chown -R dbproxy.dbproxy mysql-proxy/cd /var/log/ chown -R dbproxy.dbproxy dbproxy_log/4:启动 root@dbtest:/usr/local/mysql-proxy# ./bin/mysql-proxyd proxy_test start 2017-04-12 11:35:46.762: (message)mysql-proxy-cli.c:795(main_cmdline)running as user: dbproxy (1001/1001) 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.0.0.1
和登陆mysql一样的命令,但是在命令行只能执行特定的命令,通过下面的命令来查看可以执行的操作。
select * from help;
通过下面的命令查看用户信息,包括admin和proxy用户:
guest@127.0.0.1 : (none) 12:53:17>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@127.0.0.1 : (none) 12:55:32>alter admin user zjy:zjy; Empty set (0.00 sec)guest@127.0.0.1 : (none) 01:01:15>^DBye root@dbtest:~# mysql -uguest -pguest -P3309 -h127.0.0.1 Warning: Using a password on the command line interface can be insecure. ERROR 1045 (28000): unknown user root@dbtest:~# mysql -uzjy -pzjy -P3309 -h127.0.0.1 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 id is 1 Server version: 5.0.99-agent-adminCopyright (c) 2000, 2016, 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@127.0.0.1 : (none) 01:03:31>select * from pwds; +-----------+------------------------------+-------+----------+-------+ | username | password | hosts | backends | type | +-----------+------------------------------+-------+----------+-------+ | % | | | | proxy | | dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO | | | proxy | | zjy | p7aS | | | admin | +-----------+------------------------------+-------+----------+-------+ 3 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 | +-----------+------------------------------+-------------------------------+----------+-------+ 4 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 1045 (28000): unpermitted host从200.64上连接: #在白名单里,可以连接 $ mysql -uguest -pguest -P3309 -h192.168.200.24 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 id is 1 Server version: 5.0.99-agent-admin ...
删除白名单:remove admin user hosts $ip,$ip
guest@192.168.200.24 : (none) 12:28:54>remove admin user hosts 192.168.200.64; Empty set (0.00 sec)guest@192.168.200.24 : (none) 12:31:13>select * from pwds; +-----------+------------------------------+----------------+----------+-------+ | username | password | hosts | backends | type | +-----------+------------------------------+----------------+----------+-------+ | % | | | | proxy | | sbtest | rr6fdddI | | | proxy | | guest | uqmOY9A= | 192.168.200.25 | | admin | | dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO | | | proxy | +-----------+------------------------------+----------------+----------+-------+ 4 rows in set (0.00 sec)guest@192.168.200.24 : (none) 12:31:15>^DBye $ mysql -uguest -pguest -P3309 -h192.168.200.24 #不在白名单 Warning: Using a password on the command line interface can be insecure. ERROR 1045 (28000): 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 %@192.168.200.%|192.168.201.%; Empty set (0.00 sec)mysql> select * from pwds; +-----------+------------------------------+-----------------------------+----------+-------+ | username | password | hosts | backends | type | +-----------+------------------------------+-----------------------------+----------+-------+ | % | | 192.168.200.%|192.168.201.% | | 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 %@192.168.201.%; Empty set (0.00 sec)mysql> select * from pwds; +-----------+------------------------------+---------------+----------+-------+ | username | password | hosts | backends | type | +-----------+------------------------------+---------------+----------+-------+ | % | | 192.168.200.% | | proxy | | sbtest | rr6fdddI | | | proxy | | guest | uqmOY9A= | | | admin | | dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO | | | proxy | +-----------+------------------------------+---------------+----------+-------+ 4 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 | +-----------+------------------------------+-------+----------+-------+ 4 rows in set (0.00 sec)
当添加完系统白名单之后,若用户白名单没设置,只要是200和201网段的都可以用账号访问。好像还是不太安全,后面继续用户白名单设置。
mysql> select * from pwds; +-----------+------------------------------+-----------------------------+----------+-------+ | username | password | hosts | backends | type | +-----------+------------------------------+-----------------------------+----------+-------+ | % | | 192.168.200.%|192.168.201.% | | proxy | | sbtest | rr6fdddI | | | proxy | | guest | uqmOY9A= | 192.168.200.% | | admin | | dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO | | | proxy | +-----------+------------------------------+-----------------------------+----------+-------+通过非200和201网段访问: $ mysql -usbtest -psbtest -P3308 -h192.168.200.24 Warning: Using a password on the command line interface can be insecure. ERROR 1045 (28000): 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 | +-----------+------------------------------+-------------------------------+----------+-------+ | % | | 192.168.200.%|192.168.201.% | | proxy | | sbtest | rr6fdddI | 192.168.200.25|192.168.200.64 | | proxy | | guest | uqmOY9A= | 192.168.200.% | | admin | | dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO | | | proxy | +-----------+------------------------------+-------------------------------+----------+-------+
若在非上面指定的白名单(IP)中执行则报错:
$mysql -usbtest -psbtest -P3308 -h192.168.200.24 Warning: Using a password on the command line interface can be insecure. ERROR 1045 (28000): 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 | +-----------+------------------------------+-----------------------------+----------+-------+ | % | | 192.168.200.%|192.168.201.% | | proxy | | sbtest | rr6fdddI | | | proxy | | guest | uqmOY9A= | 192.168.200.% | | admin | | dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO | | | proxy | +-----------+------------------------------+-----------------------------+----------+-------+ 4 rows in set (0.00 sec)
若系统白名单设置不允许200网段登陆,则直接不会走到验证用户白名单这不,直接退出。
mysql> remove user hosts %@192.168.200.%; Empty set (0.00 sec)mysql> select * from pwds; +-----------+------------------------------+-------------------------------+----------+-------+ | username | password | hosts | backends | type | +-----------+------------------------------+-------------------------------+----------+-------+ | % | | 192.168.201.% | | proxy | | sbtest | rr6fdddI | 192.168.200.25|192.168.200.64 | | proxy | | guest | uqmOY9A= | 192.168.200.% | | admin | | dxy_proxy | uK6+XM9x2YRDabHitUtqIK5KOLfO | | | proxy | +-----------+------------------------------+-------------------------------+----------+-------+ 4 rows in set (0.00 sec)
#系统白名单只允许201网段访问。即使用户白名单允许200网段访问也不行: $mysql -usbtest -psbtest -P3308 -h192.168.200.24 Warning: Using a password on the command line interface can be insecure. ERROR 1045 (28000): 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@192.168.200.24 : (none) 02:45:29>show databases; #不能执行命令,因为后端数据库没有账号或则密码错误。 ERROR 2006 (HY000): MySQL server has gone away No connection. Trying to reconnect... Connection id: 431 Current database: *** NONE ***ERROR 1105 (07000): Internal Error: I have no server backend, closing connection
把上面的配置持久化到配置文件(save config)
user-hosts=%@192.168.200.%|192.168.201.%,sbtest@192.168.200.25|192.168.200.64 admin-user-hosts=192.168.200.%
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 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | up | ro | 3 | NULL | 0 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+
要是配置文件里没有配置,也可以到admin接口里进行设置:
添加一个主库:add master $ip:$port
添加一个从库:add slave $ip:$port
guest@127.0.0.1 : (none) 04:31:55>select * from backends; Empty set (0.00 sec)guest@127.0.0.1 : (none) 04:31:57>add master 192.168.200.202:3306; Empty set (0.00 sec)guest@127.0.0.1 : (none) 04:32:00>add slave 192.168.200.132:3306; Empty set (0.00 sec)guest@127.0.0.1 : (none) 04:32:13>select * from backends; +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | backend_ndx | address | hostname | state | type | weight | tag | threads_running | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | up | ro | 1 | NULL | 0 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ 2 rows in set (0.00 sec)guest@127.0.0.1 : (none) 04:32:18>
当已经存在一个主库,再添加一个主库会报错:
guest@127.0.0.1 : (none) 04:32:18>add master 192.168.200.202:3306; ERROR 1105 (07000): there is already one RW backend
DBProxy只允许一主一从或一主多从,不能多主。
删除backends:remove backend $backend_ndx [timeout $int]
timeout的意义是:在删除过程中,如果当前待删除的库正在事务中,则可以设置等待时间,没有设置等待时间,则参考系统全局的remove-backend-timeout的值。若当前backend中有连接在事务中,则等待该事务完成,若超过等待时间,该事务仍旧没有执行完,则强制对backend进行删除。
guest@127.0.0.1 : (none) 04:34:24>select * from backends; +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | backend_ndx | address | hostname | state | type | weight | tag | threads_running | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | up | ro | 1 | NULL | 0 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ 2 rows in set (0.00 sec)guest@127.0.0.1 : (none) 04:36:02>remove backend 2; Empty set (0.00 sec)guest@127.0.0.1 : (none) 04:36:42>remove backend 1 timeout 100; Empty set (0.00 sec)guest@127.0.0.1 : (none) 04:36:48>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 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | up | ro | 1 | NULL | 0 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ 2 rows in set (0.00 sec)mysql> set offline 2; Empty set (0.00 sec)通过查看日志(/var/log/dbproxy_log/sql),看到这时的读都去了主库。mysql> select * from backends; +-------------+----------------------+----------+---------+------+--------+------+-----------------+ | backend_ndx | address | hostname | state | type | weight | tag | threads_running | +-------------+----------------------+----------+---------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | offline | ro | 1 | NULL | 0 | +-------------+----------------------+----------+---------+------+--------+------+-----------------+ 2 rows in set (0.00 sec)mysql> set online 2; Empty set (0.00 sec)mysql> select * from backends; +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | backend_ndx | address | hostname | state | type | weight | tag | threads_running | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | up | ro | 1 | NULL | 0 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ 2 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 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | up | ro | 1 | NULL | 0 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ 2 rows in set (0.00 sec)mysql> alter slave weight 2 100; Empty set (0.00 sec)mysql> select * from backends; +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | backend_ndx | address | hostname | state | type | weight | tag | threads_running | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ | 1 | 192.168.200.202:3306 | | up | rw | 0 | NULL | 0 | | 2 | 192.168.200.132:3306 | | up | ro | 100 | NULL | 0 | +-------------+----------------------+----------+-------+------+--------+------+-----------------+ 2 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,其他版也没问题。
1:安装依赖包 apt-get install make automake libtool pkg-config libaio-dev vim-common libmysqlclient-dev2:下载 git clone https://github.com/akopytov/sysbench.git3:安装 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 1.1.0-6bb15d7 dxy@redis2:/usr/local/sysbench-1.1$ ./bin/sysbench cpu help sysbench 1.1.0-6bb15d7 (using bundled LuaJIT 2.1.0-beta2)cpu options:--cpu-max-prime=N upper limit for primes generator [10000]dxy@redis2:/usr/local/sysbench-1.1$ ./bin/sysbench ./share/sysbench/oltp_read_write.lua help sysbench 1.1.0-6bb15d7 (using bundled LuaJIT 2.1.0-beta2)oltp_read_write.lua options:--distinct_ranges=N Number of SELECT DISTINCT queries per transaction [1]--sum_ranges=N Number of SELECT SUM() queries per transaction [1]--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 [1]--range_size=N Range size for range SELECT queries [100]--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 [1]--tables=N Number of tables [1]--mysql_storage_engine=STRING Storage engine, if MySQL is used [innodb]--non_index_updates=N Number of UPDATE non-index queries per transaction [1]--table_size=N Number of rows per table [10000]--pgsql_variant=STRING Use this PostgreSQL variant when running with the PostgreSQL driver. The only currently supported variant is 'redshift'. When enabled, create_secondary is automatically disabled, and delete_inserts is set to 0--simple_ranges=N Number of simple range SELECT queries per transaction [1]--order_ranges=N Number of SELECT ORDER BY queries per transaction [1]--range_selects[=on|off] Enable/disable all range SELECT queries [on]--point_selects=N Number of point SELECT queries per transaction [10]
3)测试,这里测试选择通过select、insert、update和混合模式进行测试说明,自带的lua脚本有:
$ ls -lh /usr/local/sysbench-1.1/share/sysbench/ total 64K -rwxr-xr-x 1 root root 1.5K 4月 12 17:35 bulk_insert.lua -rw-r--r-- 1 root root 14K 4月 12 17:35 oltp_common.lua -rwxr-xr-x 1 root root 1.1K 4月 12 17:35 oltp_delete.lua #删 -rwxr-xr-x 1 root root 2.0K 4月 12 17:35 oltp_insert.lua #写 -rwxr-xr-x 1 root root 1.3K 4月 12 17:35 oltp_point_select.lua -rwxr-xr-x 1 root root 1.7K 4月 12 17:35 oltp_read_only.lua #只读 -rwxr-xr-x 1 root root 1.8K 4月 12 17:35 oltp_read_write.lua #读写 -rwxr-xr-x 1 root root 1.1K 4月 12 17:35 oltp_update_index.lua #更新索引字段 -rwxr-xr-x 1 root root 1.2K 4月 12 17:35 oltp_update_non_index.lua #更新非索引字段 -rwxr-xr-x 1 root root 1.5K 4月 12 17:35 oltp_write_only.lua #只写 -rwxr-xr-x 1 root root 1.9K 4月 12 17:35 select_random_points.lua -rwxr-xr-x 1 root root 2.1K 4月 12 17:35 select_random_ranges.lua drwxr-xr-x 4 root root 4.0K 4月 12 17:35 tests
进入执行文件目录 cd /usr/local/sysbench-1.1
①:读写混合(oltp_read_write.lua)
直连数据库:
./bin/sysbench --test=./share/sysbench/oltp_read_write.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=4 --tables=3 --table-size=500000 prepare/run/cleanup
代理连数据库
./bin/sysbench --test=./share/sysbench/oltp_read_write.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=4 --tables=3 --table-size=500000 --skip-trx=on --db-ps-mode=disable --mysql-ignore-errors=1062 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=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_read_only.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
把上面数据以折线图的形式表现:
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属于CPU密集型任务,相对于磁盘IO和内存占用率而言,其对CPU消耗显得最为明显,所以建议在部署的时候需要优先考虑服务器的CPU性能。
总结
通过上面的一些基本介绍,大致了解了DBProxy读写分离功能的使用,和直连DB数据库对比的性能情况。关于DBproxy的其他功能内容在手册里有了详尽的介绍,需要的话可以去查看。
这里还有2个问题:一个问题是在读写分离上面,要是从库延迟超过阈值,导致读写分离不可用,则需要自己编写脚本去连接admin接口控制问题从库的下线。另一个问题是即使数据库有MHA保证,但是DBProxy是一个单点,所以需要保证DBProxy的高可用,这2个问题后面会继续研究,以及会再看另一个中间件proxysql来和DBProxy进行对比选型。
补充:
补上检测MySQL从库延迟进行下线和不延迟进行上线操作的判断,账号密码通过读配置文件,通过读取admin接口进行操作,配置文件(proxy_db.txt)的格式是:
DBProxy实例名,DBProxy IP,DBProxy Admin User,DBProxy Admin Password,从实例的用户,从实例的用户,从实例的密码
如:
masterdata_proxy,192.168.200.24,3309,guest,guest,test,test
DBProxy Admin接口进行下线和上线的python脚本(check_set_repl.py):具体的参数可以自行按照需要修改。
#!/bin/env python # -*- encoding: utf-8 -*-from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage from email.Utils import COMMASPACE, formatdateimport MySQLdb import fileinput import smtplib import mimetypes import email.MIMEMultipart import email.MIMEText import email.MIMEBasedef send_mail(to, subject, text, from_mail, server="localhost"):message = MIMEMultipart()message['From'] = from_mailmessage['To'] = COMMASPACE.join(to)message['Date'] = formatdate(localtime=True)message['Subject'] = subjectmessage.attach(MIMEText(text,_charset='utf-8'))smtp = smtplib.SMTP(server)smtp.sendmail(from_mail, to, message.as_string())smtp.close()class DBProxyInfo():def __init__(self,host,port,user,password,slave_user,slave_password):self.host = hostself.port = portself.user = userself.password = passwordself.slave_user = slave_userself.slave_password = slave_passworddef backends_info(self):try:m = MySQLdb.connect(host=self.host,user=self.user,passwd=self.password,port=self.port)query = "SELECT * FROM backends"cursor = m.cursor()cursor.execute(query)Str_string = cursor.fetchall()cursor.close()m.close()return Str_stringexcept Exception, e:print esys.exit()def repl_info(self,slave_host,slave_port):try:m = MySQLdb.connect(host=slave_host,user=self.slave_user,passwd=self.slave_password,port=slave_port)query = "SHOW SLAVE STATUS"cursor = m.cursor()cursor.execute(query)Str_string = cursor.fetchone()cursor.close()m.close()return Str_stringexcept Exception, e:print esys.exit()def set_offline(self,backend_ndx):try:m = MySQLdb.connect(host=self.host,user=self.user,passwd=self.password,port=self.port)query = "SET offline %s" %backend_ndxcursor = m.cursor()cursor.execute(query)Str_string = cursor.fetchone()cursor.close()m.close()return Str_stringexcept Exception, e:print esys.exit()def set_online(self,backend_ndx):try:m = MySQLdb.connect(host=self.host,user=self.user,passwd=self.password,port=self.port)query = "SET online %s" %backend_ndxcursor = m.cursor()cursor.execute(query)Str_string = cursor.fetchone()cursor.close()m.close()return Str_stringexcept Exception, e:print esys.exit()if __name__ == '__main__':db_list= []for line in fileinput.input():db_list.append(line.strip())for db_info in db_list:instance_name,host,port,user,password,salve_user,slave_password = db_info.split(',')conn = DBProxyInfo(host,int(port),user,password,salve_user,slave_password)backends_info = conn.backends_info()for backend_ndx,address,hostname,state,type,weight,tag,threads_running in backends_info:if type == 'ro':salve_host,slave_port = address.split(':')repl_info = conn.repl_info(salve_host,int(slave_port))Slave_IO_Running = repl_info[10]Slave_SQL_Running = repl_info[11]Seconds_Behind_Master = repl_info[32]if Slave_IO_Running != 'Yes' or Slave_SQL_Running != 'Yes' or Seconds_Behind_Master >= 60:if state == 'up':conn.set_offline(self,backend_ndx)subject = "%s's slave is offline " %instance_namemessage = " %s's slave Warning:\n Slave_IO_Running:%s \n Slave_SQL_Running:%s \n Seconds_Behind_Master:%s" %(address,Slave_IO_Running,Slave_SQL_Running,Seconds_Behind_Master)mail_list = ['zjy@xxx.com']send_mail(mail_list, subject.encode("utf8"), message, "check_set_rep@ls.xxx.net", server="192.168.200.xxx")else:passelif Slave_IO_Running == 'Yes' and Slave_SQL_Running == 'Yes' and Seconds_Behind_Master < 60 and state != 'up':conn.set_online(backend_ndx)subject = "%s's slave is online " %instance_namemessage = " %s's slave OK " %(address)mail_list = ['zjy@dxyer.com']send_mail(mail_list, subject.encode("utf8"), message, "check_set_rep@ls.xxx.net", server="192.168.200.xxx")else:pass
运行方式:可以放到系统的crontab里定时检测。
python check_set_repl.py proxy_db.txt
参考文档
DBProxy手册
Atlas手册