目的

      在美团点评DBProxy读写分离使用说明文章中已经说明了使用目的本文介绍ProxySQL的使用方法以及和DBProxy的性能差异。具体的介绍可以看官网的相关说明并且这个中间件也是percona推的一款中间件。其特性和其他读写分离的中间件差距不大具体的会在文中介绍。本文大致简单的介绍在使用过程中的一些说明也可以看官方的wiki获得使用帮助。

环境 

Distributor ID    Ubuntu
Description    Ubuntu 14.04.5 LTS
Release    14.04Codename    trusty

下载

percona站点

https//www.percona.com/downloads/proxysql/

github/官网

https//github.com/sysown/proxysql/releases

安装

1编译安装

1下载wget https//github.com/sysown/proxysql/archive/v1.3.6.tar.gz2安装依赖包
apt-get install automake bzip2 cmake make g++ gcc git openssl debconf-utils3解压、编译makemake install...
 Adding system startup for /etc/init.d/proxysql ...   /etc/rc0.d/K20proxysql -> ../init.d/proxysql   /etc/rc1.d/K20proxysql -> ../init.d/proxysql   /etc/rc6.d/K20proxysql -> ../init.d/proxysql   /etc/rc2.d/S20proxysql -> ../init.d/proxysql   /etc/rc3.d/S20proxysql -> ../init.d/proxysql   /etc/rc4.d/S20proxysql -> ../init.d/proxysql   /etc/rc5.d/S20proxysql -> ../init.d/proxysql4修改非root启动
添加用户
useradd proxysql
修改相关文件权限
cd /var/lib/chown -R proxysql.proxysql proxysql/cd /etc/chown -R proxysql.proxysql proxysql.cnf 
修改启动命令vi /etc/init.d/proxysql ->75行
proxysql $OPTS
改成sudo -u proxysql /usr/bin/proxysql $OPTS5启动&关闭/etc/init.d/proxysql start/etc/init.d/proxysql stop

2deb包安装

1下载wget https//github.com/sysown/proxysql/releases/download/v1.3.6/proxysql_1.3.6-dbg-ubuntu14_amd64.deb2安装
dpkg -i proxysql_1.3.5-1.1.trusty_amd64.deb3非root用户启动参考编译安装部分。

文件说明

通过启动文件 /etc/init.d/proxysql里可以知道ProxySQL安装好的数据目录在 /var/lib/proxysql/配置文件目录在 /etc/proxysql.cnf。

root@proxysql/var/lib/proxysql# pwd/var/lib/proxysql
root@proxysql/var/lib/proxysql# ls -lh
total 48K-rw------- 1 proxysql proxysql  33K  5月  9 1152 proxysql.db-rw------- 1 proxysql proxysql 7.4K  5月  9 1156 proxysql.log

root@proxysql~# ls -lh /etc/proxysql.cnf 
-rw------- 1 proxysql proxysql 4.2K  5月  9 1145 /etc/proxysql.cnf

启动之后可以看到这些文件proxysql.db是SQLITE的数据文件proxysql配置如后端数据库的账号、密码、路由等存储在这个数据库里面proxysql.log是日志文件。proxysql.pid这个pid文件。proxysql.cnf是ProxySQL的一些静态配置项比如一些启动选项sqlite的数据目录等等。配置文件只在第一次启动的时候读取进行初始化后面只读取db文件。

进程说明

root@proxysql~# ps -ef | grep proxysql
proxysql  35740      1  0 1220 ?        000000 /usr/bin/proxysql -c /etc/proxysql.cnf -D /var/lib/proxysql
proxysql  35741  35740  0 1220 ?        000000 /usr/bin/proxysql -c /etc/proxysql.cnf -D /var/lib/proxysql

和MySQL的很相似我们启动一个进程然后fork出一个子进程父进程负责监控子进程运行状况如果挂了则拉起来子进程负责执行真正的任务。

接口说明

和DBProxy一样ProxySQL也是有管理接口和客户端接口通过配置文件/etc/proxysql.cnf可以看到管理和客户端接口的信息

admin_variables========

看到管理接口的端口是6032账号密码是admin(可以动态修改)只能通过本地连接客户端接口的端口是6033账号密码通过管理接口去设置。

库、表说明

通过管理接口登录需要先装好mysql客户端并且设置prompt

[client]
prompt      = \\u@\\h  \\d \\r\\m\\s>

通过管理接口进入ProxySQL

root@proxysql~# mysql -uadmin -padmin -h127.0.0.1 -P6032
...
...
[email protected]  (none) 124308>show databases;+-----+---------+-------------------------------+
| seq | name    | file                          |
+-----+---------+-------------------------------+
| 0   | main    |                               |
| 2   | disk    | /var/lib/proxysql/proxysql.db |
| 3   | stats   |                               |
| 4   | monitor |                               |
+-----+---------+-------------------------------+4 rows in set (0.00 sec)

  • main 内存配置数据库表里存放后端db实例、用户验证、路由规则等信息。表名以 runtime_开头的表示proxysql当前运行的配置内容不能通过dml语句修改只能修改对应的不以 runtime_ 开头的在内存里的表然后 LOAD 使其生效 SAVE 使其存到硬盘以供下次重启加载。

  • disk 是持久化到硬盘的配置sqlite数据文件。

  • stats 是proxysql运行抓取的统计信息包括到后端各命令的执行次数、流量、processlist、查询种类汇总/执行时间等等。

  • monitor 库存储 monitor 模块收集的信息主要是对后端db的健康/延迟检查。

1main库

[email protected]  (none) 020154>show tables from main;+--------------------------------------+
| tables                               |
+--------------------------------------+
| global_variables                     |
| mysql_collations                     |
| mysql_query_rules                    |
| mysql_replication_hostgroups         |
| mysql_servers                        |
| mysql_users                          |
| runtime_global_variables             |
| runtime_mysql_query_rules            |
| runtime_mysql_replication_hostgroups |
| runtime_mysql_servers                |
| runtime_mysql_users                  |
| runtime_scheduler                    |
| scheduler                            |
+--------------------------------------+13 rows in set (0.00 sec)

global_variables 设置变量包括监听的端口、管理账号等详细可参考 https//github.com/sysown/proxysql/wiki/Global-variables。通过set来设置如

set mysql-monitor_ping_interval =10001;

mysql_collations相关字符集和校验规则。
mysql_query_rules定义查询路由规则。

CREATE TABLE mysql_query_rules (
    rule_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 0,
    username VARCHAR,
    schemaname VARCHAR,
    flagIN INT NOT NULL DEFAULT 0,
    client_addr VARCHAR,
    proxy_addr VARCHAR,
    proxy_port INT,
    digest VARCHAR,
    match_digest VARCHAR,
    match_pattern VARCHAR,
    negate_match_pattern INT CHECK (negate_match_pattern IN (0,1)) NOT NULL DEFAULT 0,
    flagOUT INT,
    replace_pattern VARCHAR,
    destination_hostgroup INT DEFAULT NULL,
    cache_ttl INT CHECK(cache_ttl > 0),
    reconnect INT CHECK (reconnect IN (0,1)) DEFAULT NULL,
    timeout INT UNSIGNED,
    retries INT CHECK (retries>=0 AND retries <=1000),
    delay INT UNSIGNED,
    mirror_flagOUT INT UNSIGNED,
    mirror_hostgroup INT UNSIGNED,
    error_msg VARCHAR,    log INT CHECK (log IN (0,1)),
    apply INT CHECK(apply IN (0,1)) NOT NULL DEFAULT 0,
    comment VARCHAR)

  • rule_id 表主键自增。规则处理是以 rule_id 的顺序进行。

  • active 只有 active=1 的规则才会参与匹配。

  • username 如果非 NULL只有连接用户是 username 的值才会匹配

  • schemaname 如果非 NULL只有查询连接使用的db是 schemaname 的值才会匹配。注意如果是 NULL不代表连接没有使用schema而是不伦任何schema都进一步匹配。

  • client_addr 匹配客户端来源IP

  • proxy_addr, proxy_port 匹配本地proxysql的IP、端口。

  • digest 精确的匹配一类查询。

  • match_digest 正则匹配一类查询。query digest 是指对查询去掉具体值后进行“模糊化”后的查询。

  • match_pattern 正则匹配查询。

  • negate_match_pattern 反向匹配相当于对 match_digest/match_pattern 的匹配取反。

  • re_modifiers 修改正则匹配的参数比如默认的忽略大小写CASELESS、禁用GLOBAL。
    上面都是匹配规则下面是匹配后的行为

  • replace_pattern 查询重写默认为空不rewrite。

  • destination_hostgroup 路由查询到这个 hostgroup。当然如果用户显式 start transaction 且 transaction_persistent=1那么即使匹配到了也依然按照事务里第一条sql的路由规则去走。

  • cache_ttl 查询结果缓存的毫秒数。proxysql这个 Query Cache 与 MySQL 自带的query cache不是同一个。proxysql query cache也不会关心后端数据是否被修改它所做的就是针对某些特定种类的查询结果进行缓存比如一些历史数据的count结果。一般不设。

  • timeout 这一类查询执行的最大时间毫秒超时则自动kill。这是对后端DB的保护机制默认mysql-default_query_timeout给的是10h。

  • retries 语句在执行时失败时重试次数。默认由 mysql-query_retries_on_failure变量指定默认为1 。

  • delay 查询延迟执行这是ProxySQL提供的限流机制会让其它的查询优先执行。

  • error_msg 默认为NULL如果指定了则这个查询直接被 block 掉马上返回这个错误信息。这个功能也很实用比如线上突然冒出一个 “坏查询”应用端不方便马上发版解决我们就可以在这配置一个规则把查询屏蔽掉想正常的mysql报错那样抛异常。

  • multiplex 连接是否复用。

  • log 是否记录查询日志。

  • flagINflagOUTapply

    • flagIN, flagOUT, apply 用来定义路由链 chains of rules

    • 首先会检查 flagIN=0 的规则以rule_id的顺序如果都没匹配上则走这个用户的 default_hostgroup

    • 当匹配一条规则后会检查 flagOUT

    • 如果不为NULL并且 flagIN != flagOUT 则进入以flagIN为上一个flagOUT值的新规则链

    • 如果不为NULL并且 flagIN = flagOUT则应用这条规则

    • 如果为NULL或者 apply=1则结束应用这条规则

    • 如果最终没有匹配到则找到这个用户的 default_hostgroup

mysql_replication_hostgroups监视指定主机组中所有服务器的read_only值并且根据read_only的值将服务器分配给写入器或读取器主机组

CREATE TABLE mysql_replication_hostgroups (
    writer_hostgroup INT CHECK (writer_hostgroup>=0) NOT NULL PRIMARY KEY,
    reader_hostgroup INT NOT NULL CHECK (reader_hostgroup<>writer_hostgroup AND reader_hostgroup>0),
    comment VARCHAR,    UNIQUE (reader_hostgroup))

定义 hostgroup 的主从关系。ProxySQL monitor 模块会监控 HG 后端所有servers 的 read_only 变量如果发现从库的 read_only 变为0、主库变为1则认为角色互换了自动改写 mysql_servers 表里面 hostgroup 关系达到自动 Failover 效果。

mysql_servers设置后端MySQL的表

CREATE TABLE mysql_servers (
    hostgroup_id INT NOT NULL DEFAULT 0,
    hostname VARCHAR NOT NULL,
    port INT NOT NULL DEFAULT 3306,
    status VARCHAR CHECK (UPPER(status) IN ('ONLINE','SHUNNED','OFFLINE_SOFT', 'OFFLINE_HARD')) NOT NULL DEFAULT 'ONLINE',
    weight INT CHECK (weight >= 0) NOT NULL DEFAULT 1,
    compression INT CHECK (compression >=0 AND compression <= 102400) NOT NULL DEFAULT 0,
    max_connections INT CHECK (max_connections >=0) NOT NULL DEFAULT 1000,
    max_replication_lag INT CHECK (max_replication_lag >= 0 AND max_replication_lag <= 126144000) NOT NULL DEFAULT 0,
    use_ssl INT CHECK (use_ssl IN(0,1)) NOT NULL DEFAULT 0,
    max_latency_ms INT UNSIGNED CHECK (max_latency_ms>=0) NOT NULL DEFAULT 0,
    comment VARCHAR NOT NULL DEFAULT '',    PRIMARY KEY (hostgroup_id, hostname, port) )

  • hostgroup_id ProxySQL通过 hostgroup (下称HG) 的形式组织后端db实例。一个 HG 代表同属于一个角色
    该表的主键是 (hostgroup_id, hostname, port)可以看到一个 hostnameport 可以在多个hostgroup里面这样可以避免从库全都不可用时依然可以把读请求发到主库上。一个 HG可以有多个实例即多个从库可以通过 weight 分配权重。hostgroup_id 0 是一个特殊的HG路由查询的时候没有匹配到规则则默认选择 HG 0

  • status
    ONLINE 当前后端实例状态正常
    SHUNNED 临时被剔除可能因为后端 too many connections error或者超过了可容忍延迟阀值 max_replication_lag
    OFFLINE_SOFT “软离线”状态不再接受新的连接但已建立的连接会等待活跃事务完成。
    OFFLINE_HARD “硬离线”状态不再接受新的连接已建立的连接或被强制中断。当后端实例宕机或网络不可达会出现。

  • max_connections 允许连接到该后端实例的最大连接数。不要大于MySQL设置的 max_connections如果后端实例 hostnameport 在多个 hostgroup 里以较大者为准而不是各自独立允许的最大连接数。

  • max_replication_lag 允许的最大延迟主库不受这个影响默认0。如果 > 0 monitor 模块监控主从延迟大于阀值时会临时把它变为 SHUNNED

  • max_latency_ms mysql_ping 响应时间大于这个阀值会把它从连接池剔除即使是ONLINE默认0。

  • comment 备注。

mysql_users配置后端数据库的账号和监控的账号

     (active  (,))      (use_ssl  (,))          (schema_locked  (,))      (transaction_persistent  (,))      (fast_forward  (,))      (backend  (,))      (frontend  (,))      (max_connections )

  • username, password 连接后端db的用户密码。
    这个密码你可以插入明文也可以插入hash加密后的密文proxysql会检查你插入的时候密码是否以 * 开头来判断而且密文要在其它地方使用 PASSWORD()生成。但到 runtime_mysql_users 里都统一变成了密文所以可以明文插入再 SAVE MYSQL USERS TO MEM此时看到的也是HASH密文。

  • active 是否生效该用户。

  • default_hostgroup 这个用户的请求没有匹配到规则时默认发到这个 hostgroup默认0

  • default_schema 这个用户连接时没有指定 database name 时默认使用的schema注意表面上看默认为NULL但实际上受到变量 mysql-default_schema 的影响默认为 information_schema。关于这个参考我所提的 issue #988

  • transaction_persistent 如果设置为1连接上ProxySQL的会话后如果在一个hostgroup上开启了事务那么后续的sql都继续维持在这个hostgroup上不伦是否会匹配上其它路由规则直到事务结束。虽然默认是0。

  • frontend, backend 目前版本这两个都需要使用默认的1将来有可能会把 Client -> ProxySQL (frontend) 与 ProxySQL -> BackendDB (backend)的认证分开。从 runtime_mysql_users 表内容看到记录数比 mysql_users 多了一倍就是把前端认证与后端认证独立出来的结果。

  • fast_forward 忽略查询重写/缓存层直接把这个用户的请求透传到后端DB。相当于只用它的连接池功能一般不用路由规则 .* 就行了。

scheduler调度器是一个类似于cron的实现集成在ProxySQL中具有毫秒的粒度。通过脚本检测来设置ProxySQL。

 CREATE TABLE scheduler (
    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1,
    interval_ms INTEGER CHECK (interval_ms>=100 AND interval_ms<=100000000) NOT NULL,
    filename VARCHAR NOT NULL,
    arg1 VARCHAR,
    arg2 VARCHAR,
    arg3 VARCHAR,
    arg4 VARCHAR,
    arg5 VARCHAR,
    comment VARCHAR NOT NULL DEFAULT '')

  • id 调度器唯一标识自增主键。

  • active 是否开启默认开启。

  • interval_ms  工作的开始频率以毫秒为单位。 最小interval_ms为100毫秒

  • filename  执行的可执行文件的绝对路径

  • arg1 to arg5 传递给执行文件的参数最大允许5个参数

  • comment 备注

关于main库下的表的说明可以看这篇文章的说明http//seanlook.com/2017/04/10/mysql-proxysql-install-config/ 

2disk库

show tables from disk;+------------------------------+| tables                       |+------------------------------+| global_variables             || mysql_collations             || mysql_query_rules            || mysql_replication_hostgroups || mysql_servers                || mysql_users                  || scheduler                    |+------------------------------+7 rows in set (0.00 sec)

和上面介绍的main库一样。

3stats库

stats_mysql_commands_counters统计各种SQL类型的执行次数和时间通过参数mysql-commands_stats控制开关默认是ture。

CREATE TABLE stats_mysql_commands_counters (
    Command VARCHAR NOT NULL PRIMARY KEY,
    Total_Time_us INT NOT NULL,
    Total_cnt INT NOT NULL,
    cnt_100us INT NOT NULL,
    cnt_500us INT NOT NULL,
    cnt_1ms INT NOT NULL,
    cnt_5ms INT NOT NULL,
    cnt_10ms INT NOT NULL,
    cnt_50ms INT NOT NULL,
    cnt_100ms INT NOT NULL,
    cnt_500ms INT NOT NULL,
    cnt_1s INT NOT NULL,
    cnt_5s INT NOT NULL,
    cnt_10s INT NOT NULL,
    cnt_INFs)

  • command已执行的SQL命令的类型。 如FLUSHINSERTKILLSELECT FOR UPDATE等。

  • Total_Time_us执行该类型命令的总时间以微秒为单位。

  • total_cnt执行该类型的命令的总数。

  • cnt_100us, cnt_500us, ..., cnt_10s, cnt_INFs在指定的时间限制内执行的给定类型的命令总数和前一个命令的总数。

stats_mysql_connection_pool连接后端MySQL的连接信息。

CREATE TABLE stats_mysql_connection_pool (
    hostgroup VARCHAR,
    srv_host VARCHAR,
    srv_port VARCHAR,
    status VARCHAR,
    ConnUsed INT,
    ConnFree INT,
    ConnOK INT,
    ConnERR INT,
    Queries INT,
    Bytes_data_sent INT,
    Bytes_data_recv INT,
    Latency_us INT)

  • hostgroup后端服务器所属的主机组。请注意单个后端服务器可以属于多个主机组。

  • srv_hostsrv_portmysqld后端服务器正在侦听连接的TCP端点的IP和Port。

  • status后端服务器的状态。可以有ONLINESHUNNEDOFFLINE_SOFTOFFLINE_HARD。有关每个状态意味着什么的详细信息请参阅上面的mysql_servers表的描述。

  • ConnUsedProxySQL当前使用多少个连接来向后端服务器发送查询。

  • ConnFree目前有多少个连接是空闲。

  • ConnOK成功建立了多少个连接。

  • ConnERR没有成功建立多少个连接。

  • Queries路由到此特定后端服务器的查询数。

  • Bytes_data_sent发送到后端的数据量。

  • Bytes_data_recv从后端接收的数据量。

  • Latency_ms从Monitor报告的当前ping以毫秒为单位的延迟时间。

stats_mysql_global代表与MySQL相关的代理级别的全局统计如

  • Client_Connections_aborted由于无效凭据或max_connections而导致的前端连接数已达到。

  • Client_Connections_connected - 当前连接的前端连接数。

  • Client_Connections_created - 到目前为止创建的前端连接数。

  • Questions 从前端发送的查询总数。

  • Slow_queries在全局变量中定义的运行时间超过阈值的毫秒数的查询数mysql-long_query_time

  • 等等...

stats_mysql_processlist类似MySQL的show processlist的命令查看各线程的状态。

CREATE TABLE stats_mysql_processlist (
    ThreadID INT NOT NULL,
    SessionID INTEGER PRIMARY KEY,    user VARCHAR,
    db VARCHAR,
    cli_host VARCHAR,
    cli_port VARCHAR,
    hostgroup VARCHAR,
    l_srv_host VARCHAR,
    l_srv_port VARCHAR,
    srv_host VARCHAR,
    srv_port VARCHAR,
    command VARCHAR,
    time_ms INT NOT NULL,
    info VARCHAR)

  • ThreadIDProxySQL内线程的内部ID。

  • SessionIDProxySQL会话ID。通过这个ID可以进行kill操作。

  • user与MySQL客户端连接到ProxySQL的用户。

  • db当前选择的数据库。

  • cli_hostcli_port连接ProxySQL的IP和TCP端口。

  • hostgroup当前主机组。如果正在处理查询则是查询已被路由或将要路由的主机组或默认主机组。可以通过这个查看该SQL到底是到哪个HG里。

  • l_srv_hostl_srv_portProxySQL的IP和TCP端口。

  • srv_hostsrv_port - 后端MySQL服务器的IP和端口。

  • command正在执行的MySQL查询的类型。

  • time_ms命令执行的时间以毫秒为单位。

  • info正在执行的SQL。

stats_mysql_query_digest表示SQL的执行次数、时间消耗等。通过变量mysql-query_digests控制开关默认是开。 

stats_mysql_query_digestCreate Table CREATE TABLE stats_mysql_query_digest (
    hostgroup INT,
    schemaname VARCHAR NOT NULL,
    username VARCHAR NOT NULL,
    digest VARCHAR NOT NULL,
    digest_text VARCHAR NOT NULL,
    count_star INTEGER NOT NULL,
    first_seen INTEGER NOT NULL,
    last_seen INTEGER NOT NULL,
    sum_time INTEGER NOT NULL,
    min_time INTEGER NOT NULL,
    max_time INTEGER NOT NULL,    PRIMARY KEY(hostgroup, schemaname, username, digest))

  • hostgroup发送查询的主机组。值-1表示查询查询缓存。

  • schemaname查询的数据库。

  • user连接ProxySQL的用户名。

  • digest一个十六进制散列表示其参数剥离的SQL。

  • digest_text参数剥离的实际SQL文本。

  • count_star执行查询的总次数参数的值不同。

  • first_seenunix时间戳是通过代理路由查询的第一时刻。

  • last_seenunix时间戳当查询通过代理路由时的最后一刻到目前为止。

  • sum_time执行此类查询的总时间以微秒为单位。这对于确定应用程序工作负载中花费的最多时间在哪里是非常有用的并为改进的地方提供了一个良好的起点。

  • min_timemax_time - 执行此类查询时期望的持续时间范围。 min_time是到目前为止所看到的最小执行时间而max_time表示最大执行时间以微秒为单位。

stats_mysql_query_digest_reset和stats_mysql_query_digest结构一样stats_mysql_query_digest_reset可以清空stats_mysql_query_digest的数据如执行

select * from stats_mysql_query_digest_reset;
show create table stats.stats_mysql_query_digest_reset;

执行上面的任意一条就可以清空stats_mysql_query_digest表。

stats_mysql_query_rules路由命中次数统计。

 CREATE TABLE stats_mysql_query_rules (
    rule_id INTEGER PRIMARY KEY,
    hits INT NOT NULL)
  • rule_id路由规则的ID与main.mysql_query_rules的id对应。

  • hits此路由规则的匹配总数。 如果当前传入的查询符合规则则会记录一次命中。

4monitor库 对后端MySQL的健康检查。由变量mysql-monitor_enabled来确定是否开启Monitor模块。

  • mysql_server_connect/mysql_server_connect_log连接到所有MySQL服务器以检查它们是否可用该表用来存放检测连接的日志。由变量mysql-monitor_connect_interval来控制其检测的时间间隔由参数mysql-monitor_connect_timeout控制连接是否超时默认200毫秒。

  • mysql_server_ping/mysql_server_ping_log使用mysql_ping API ping后端MySQL服务器检查它们是否可用该表用来存放ping的日志。由变量mysql-monitor_ping_interval控制ping的时间间隔默认值10000毫秒相当于10秒。

  • mysql_server_replication_lag_log后端MySQL服务主从延迟的检测。由参数mysql-monitor_replication_lag_interval控制检测间隔时间 如果复制滞后太大可以暂时关闭从。由mysql_servers.max_replication_lag列控制。默认值10000毫秒相当于10秒。

配置

ProxySQL有一个完备的配置系统持配置修改之后的在线保存、应用不需要重启之后生效。整个配置系统分三层设计。配置系统分为三层的目的有三个:
(1).自动更新
(2).尽可能的不重启proxysql就可以修改配置
(3).方便回滚错误配置
整个配置系统分为三层如下所示:

+-------------------------+|         RUNTIME         |+-------------------------+
       /|\          |
        |           |
    [1] |       [2] |
        |          \|/+-------------------------+|         MEMORY          |+-------------------------+ _
       /|\          |      |\        |           |        \    [3] |       [4] |         \ [5]
        |          \|/         \+-------------------------+  +-------------------------+|          DISK           |  |       CONFIG FILE       |+-------------------------+  +-------------------------+

RUNTIME 代表的是ProxySQL当前生效的正在使用的配置包括 global_variables, mysql_servers, mysql_users, mysql_query_rules。无法直接修改这里的配置必须要从下一层load进来。也就是说RUNTIME这个顶级层就是proxysql运行过程中实际采用的那一份配置这一份配置就是要影响实际生产的所以将你的配置加进RUNTIME层时需要三思而行。

MEMORY 用户可以将MySQL客户端连接到此接口admin接口并查询不同的表和数据库是在mysql命令行修改的 main 里头配置可以认为是SQLite数据库在内存的镜像。也就是说MEMORY这个中间层上面接着生产配置项RUNTIME下面接着持久化层DISK,CONFIG FILE。MEMORY也是我们修改proxysql的唯一正常入口。一般的我们修改一个配置先改Memory确认无误后再接入生产(RUNTIME)和持久化到磁盘(DISK)。也就是说memeory里面的配置随便改不影响生产也不影响磁盘中保存的数据。通过此接口可以修改mysql_servers、mysql_users、mysql_query_rules、global_variables等。

DISK / CONFIG FILE 持久存储的那份配置一般在$(DATADIR)/proxysql.db在重启的时候会从硬盘里加载。 /etc/proxysql.cnf文件只在第一次初始化的时候用到完了后如果要修改监听端口还是需要在管理命令行里修改再 save 到硬盘。 也就是说DISK和CONFIG FILE这一层是持久化层我们做的任何配置更改如果不持久化下来重启后配置都将丢失。

说明proxysql的每一个配置项在三层中都存在但是这三层是独立的也就是说proxysql可以同时拥有三份配置每层都是独立的可能三份都不一样可能三份都一样。

当proxysql启动时首先读取配置文件CONFIG FILE(/etc/proxysql.cnf)然后从该配置文件中获取datadirdatadir中存储的是sqlite的数据目录。如果该目录存在且sqlite数据文件存在那么正常启动将sqlite中的配置项读进内存并且加载进RUNTIME用于初始化proxysql的运行。如果datadir目录下没有sqlite的数据文件proxysql就会使用config file中的配置来初始化proxysql并且将这些配置保存至数据库。

修改配置需要修改配置时直接操作的是 MEMORAYmain

以下命令可用于加载或保存 usersmysql_users 序号对应上图

[1]: LOAD MYSQL USERS TO RUNTIME / LOAD MYSQL USERS FROM MEMORY --将修改后的配置(在memory层)用到实际生产[2]: SAVE MYSQL USERS TO MEMORY / SAVE MYSQL USERS FROM RUNTIME --将生产配置拉一份到memory中[3]: LOAD MYSQL USERS TO MEMORY / LOAD MYSQL USERS FROM DISK    --将磁盘中持久化的配置拉一份到memory中来[4]: SAVE MYSQL USERS TO DISK / SAVE MYSQL USERS FROM MEMORY    --将memoery中的配置保存到磁盘中去[5]: LOAD MYSQL USERS FROM CONFIG                               --将配置文件中的配置加载到memeory中

以下命令加载或保存serversmysql_servers:

[1]: LOAD MYSQL SERVERS TO RUNTIME --让修改的配置生效[2]: SAVE MYSQL SERVERS TO MEMORY[3]: LOAD MYSQL SERVERS TO MEMORY[4]: SAVE MYSQL SERVERS TO DISK --将修改的配置持久化[5]: LOAD MYSQL SERVERS FROM CONFIG

以下命令加载或保存query rulesmysql_query_rules:

[1]: load mysql query rules to run[2]: save mysql query rules to mem[3]: load mysql query rules to mem[4]: save mysql query rules to disk[5]: load mysql query rules from config

以下命令加载或保存 mysql variablesglobal_variables:

[1]: load mysql variables to runtime[2]: save mysql variables to memory[3]: load mysql variables to memory[4]: save mysql variables to disk[5]: load mysql variables from config

以下命令加载或保存admin variablesselect * from global_variables where variable_name like 'admin-%':

[1]: load admin variables to runtime[2]: save admin variables to memory[3]: load admin variables to memory[4]: save admin variables to disk[5]: load admin variables from config

到此PrxoySQL的说明已经大致介绍完更多的信息可以看官方文档介绍和官方wiki说明后面接着开始测试ProxySQL的相关功能。

测试说明

1读写分离

环境

APP192.168.200.25、192.168.200.64DB
   M192.168.200.202
   S192.168.200.132ProxySQL192.168.200.24

① 配置后端MySQL。登入ProxySQL把MySQL主从的信息添加进去。将主库master也就是做写入的节点放到HG 100中salve节点做读放到HG 1000。

[email protected] : (none) 12:37:05>insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) values(100,'192.168.200.202',3306,1,1000,10,'test proxysql');
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:37:13>insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) values(1000,'192.168.200.132',3306,1,1000,10,'test proxysql');
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:37:19>select * from mysql_servers;     
+--------------+-----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------------+| hostgroup_id | hostname        | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment       |+--------------+-----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------------+| 100          | 192.168.200.202 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 0              | test proxysql || 1000         | 192.168.200.132 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 0              | test proxysql |+--------------+-----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------------+2 rows in set (0.00 sec)

② 配置后端MySQL用户。这个用户需要先在后端MySQL(202,132)里真实存在一个是监控账号、一个是程序账号

-- 监控[email protected] : 12:38:21>GRANT USAGE ON *.* TO 'proxysql'@'192.168.200.24' IDENTIFIED BY 'proxysql';
Query OK, 0 rows affected (0.00 sec)-- 程序[email protected] : 12:38:22>GRANT SELECT, INSERT, UPDATE, DELETE ON `sbtest`.* TO 'sbuser'@'192.168.200.24' identified by 'sbuser';
Query OK, 0 rows affected (0.00 sec)

在后端MySQL里添加完之后再配置ProxySQL这里需要注意default_hostgroup需要和上面的对应。

[email protected] : (none) 12:39:52>insert into mysql_users(username,password,active,default_hostgroup,transaction_persistent) values('sbuser','sbuser',1,100,1);
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:41:07>select * from mysql_users;+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+| username | password | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections |+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+| sbuser   | sbuser   | 1      | 0       | 100               | NULL           | 0             | 1                      | 0            | 1       | 1        | 10000           |+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+1 row in set (0.00 sec)

上面看到是一个明文密码也可以用password函数加密后的密码进行代替。

③ 设置健康监测账号

[email protected] : (none) 12:42:38>UPDATE global_variables SET variable_value='proxysql' WHERE variable_name='mysql-monitor_username';
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:42:44>UPDATE global_variables SET variable_value='proxysql' WHERE variable_name='mysql-monitor_password';
Query OK, 1 row affected (0.00 sec)

或则还行下面的

[email protected] : (none) 12:44:24>set mysql-monitor_username='proxysql';
Query OK, 1 row affected (0.01 sec)

[email protected] : (none) 12:45:14>set mysql-monitor_password='proxysql';
Query OK, 1 row affected (0.01 sec)

其他变量的修改按照需要可以自行调整。

[email protected] : (none) 12:45:22>set mysql-default_charset = 'utf8mb4';
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:47:38>set mysql-query_retries_on_failure=0;
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:48:00>set mysql-eventslog_filename='queries.log';
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:48:20>set mysql-ping_timeout_server=1500;
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:48:55>set mysql-monitor_connect_timeout=1000;
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:48:55>set mysql-default_max_latency_ms=2000;
Query OK, 1 row affected (0.00 sec)

[email protected] : (none) 12:48:56>set mysql-server_version='5.6.33';
Query OK, 1 row affected (0.00 sec)

④ 加载配置和变量因为修改了servers、users和variables所以加载的时候要执行 

-- 应用到线上[email protected] : (none) 12:51:28>load mysql servers to runtime;
Query OK, 0 rows affected (0.00 sec)

[email protected] : (none) 12:56:27>load mysql users to runtime;
Query OK, 0 rows affected (0.00 sec)

[email protected] : (none) 12:56:31>load mysql variables to runtime;
Query OK, 0 rows affected (0.01 sec)-- 持久化[email protected] : (none) 12:56:37>save mysql servers to disk;
Query OK, 0 rows affected (0.01 sec)

[email protected] : (none) 12:56:45>save mysql users to disk;
Query OK, 0 rows affected (0.00 sec)

[email protected] : (none) 12:56:50>save mysql variables to disk;
Query OK, 72 rows affected (0.00 sec)

要是是用明文密码设置mysql_users在这里可以用save命令来转换成了hash值的密码

[email protected] : (none) 04:25:23>save mysql users to mem;
Query OK, 0 rows affected (0.00 sec)

[email protected] : (none) 04:25:33>select username,password from mysql_users;+----------+-------------------------------------------+| username | password                                  |+----------+-------------------------------------------+| sbuser   | *CA96E56547F43610DDE9EB7B12B4EF4C51CDDFFC |+----------+-------------------------------------------+1 row in set (0.00 sec)

⑤ 连接数据库通过proxysql的客户端接口访问(6033)

~$ mysql -usbuser -psbuser -h192.168.200.24 -P6033
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3Server version: 5.6.33 (ProxySQL)
...
[email protected] : (none) 04:41:34>show databases;+--------------------+| Database           |+--------------------+| information_schema || sbtest             |+--------------------+2 rows in set (0.00 sec)

写入测试数据

[email protected] : sbtest 04:42:35>insert into sb values(1),(2),(3);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: [email protected] : sbtest 04:44:12>select * from sb;+------+| id   |+------+|    1 ||    2 ||    3 |+------+3 rows in set (0.00 sec)

[email protected] : sbtest 04:44:14>begin;
Query OK, 0 rows affected (0.00 sec)

[email protected] : sbtest 04:44:17>insert into sb values(11),(22),(33);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: [email protected] : sbtest 04:44:26>commit;
Query OK, 0 rows affected (0.00 sec)

[email protected] : sbtest 04:44:29>select * from sb;+------+| id   |+------+|    1 ||    2 ||    3 ||   11 ||   22 ||   33 |+------+6 rows in set (0.00 sec)

通过proxysql接口正常操作数据从管理接口看看ProxySQL的统计信息

-- 查看各类命令的执行情况[email protected] : (none) 04:52:59>select Command,Total_Time_us,Total_cnt from stats_mysql_commands_counters where Total_cnt >0;+---------+---------------+-----------+| Command | Total_Time_us | Total_cnt |+---------+---------------+-----------+| BEGIN   | 314           | 1         || COMMIT  | 493           | 1         || INSERT  | 5021          | 2         || SELECT  | 2224          | 9         || SHOW    | 4577          | 5         |+---------+---------------+-----------+

-- 查看各类SQL的执行情况[email protected] : (none) 04:54:47>select * from stats_mysql_query_digest;

通过上面看到ProxySQL已经正常启动但是在stats_mysql_query_digest的hostgroup中发现读和写全部都是走100这个Master的HG没有用到从库。主要原因就是ProxySQL的核心mysql_query_rules路由表没有配置。proxysql是通过自定义sql路由规则就可以实现读写分离。

⑥ 定义路由规则如除select * from tb for update的select全部发送到slave其他的的语句发送到master。

-- 发送到[email protected] : (none) 04:58:11>INSERT INTO mysql_query_rules(active,match_pattern,destination_hostgroup,apply) VALUES(1,'^SELECT.*FOR UPDATE$',100,1);
Query OK, 1 row affected (0.00 sec)-- 发送到[email protected] : (none) 05:08:17>INSERT INTO mysql_query_rules(active,match_pattern,destination_hostgroup,apply) VALUES(1,'^SELECT',1000,1);
Query OK, 1 row affected (0.00 sec)-- 加载[email protected] : (none) 05:09:37>load mysql query rules to runtime;
Query OK, 0 rows affected (0.00 sec)
[email protected] : (none) 05:09:57>save mysql query rules to disk;
Query OK, 0 rows affected (0.00 sec)

说明active表示是否启用这个sql路由项match_pattern就是我们正则匹配项destination_hostgroup表示我们要将该类sql转发到哪些mysql上面去apply为1表示该正则匹配后将不再接受其他匹配直接转发。具体的信息看上面的表介绍说明。路由规则添加完成现在来测试下读写分离先清空stats_mysql_query_digest统计表

[email protected] : (none) 05:16:21>select * from stats_mysql_query_digest_reset;

写入测试数据 

[email protected] : sbtest 10:20:27>insert into sb values(1),(2),(3);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: [email protected] : sbtest 10:20:29>select * from sb;
...

[email protected] : sbtest 10:20:34>start transaction;
Query OK, 0 rows affected (0.01 sec)

[email protected] : sbtest 10:20:41>insert into sb values(11),(22),(33);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: [email protected] : sbtest 10:20:46>commit;
Query OK, 0 rows affected (0.00 sec)

[email protected] : sbtest 10:20:51>select * from sb;
...

[email protected] : sbtest 10:20:56>select * from sb for update;
..

[email protected] : sbtest 10:21:11>insert into sb values(111),(222),(333);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: [email protected] : sbtest 10:21:20>select * from sb;+------+| id   |+------+|    1 ||    2 ||    3 ||   11 ||   22 ||   33 ||    1 ||    2 ||    3 ||   11 ||   22 ||   33 ||  111 ||  222 ||  333 |+------+15 rows in set (0.01 sec)

查看统计信息

select * from stats_mysql_query_digest;

看到读写分离已经成功。为了更直观一点使用sysbench测试读写分离是否正常

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

为了更好的验证可以在sysbench运行期间可以到后端的从MySQL上查看是否有查询进来。执行完之后来看下统计表

[email protected] : (none) 10:31:52>select hostgroup,schemaname,username,substr(digest_text,120,-120),count_star from stats_mysql_query_digest;+-----------+------------+----------+--------------------------------------------------------------------+------------+| hostgroup | schemaname | username | substr(digest_text,120,-120)                                       | count_star |+-----------+------------+----------+--------------------------------------------------------------------+------------+| 100       | sbtest     | sbuser   | show processlist                                                   | 15         || 100       | sbtest     | sbuser   | INSERT INTO sbtest1 (id, k, c, pad) VALUES (?, ?, ?, ?)            | 123932     || 100       | sbtest     | sbuser   | DELETE FROM sbtest1 WHERE id=?                                     | 123932     || 100       | sbtest     | sbuser   | UPDATE sbtest1 SET c=? WHERE id=?                                  | 123932     || 100       | sbtest     | sbuser   | UPDATE sbtest1 SET k=k+? WHERE id=?                                | 123932     || 1000      | sbtest     | sbuser   | SELECT SUM(k) FROM sbtest1 WHERE id BETWEEN ? AND ?                | 123932     || 100       | sbtest     | sbuser   | show tables                                                        | 1          || 100       | sbtest     | sbuser   | select * from sb for update                                        | 1          || 100       | sbtest     | sbuser   | start transaction                                                  | 1          || 1000      | sbtest     | sbuser   | SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c          | 123932     || 1000      | sbtest     | sbuser   | SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ?                     | 123932     || 1000      | sbtest     | sbuser   | SELECT c FROM sbtest1 WHERE id=?                                   | 1239320    || 1000      | sbtest     | sbuser   | select * from sb                                                   | 3          || 1000      | sbtest     | sbuser   | SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c | 123932     || 100       | sbtest     | sbuser   | drop table sbtest1                                                 | 1          || 100       | sbtest     | sbuser   | commit                                                             | 1          || 100       | sbtest     | sbuser   | insert into sb values(?),(?),(?)                                   | 3          |+-----------+------------+----------+--------------------------------------------------------------------+------------+

从上面的结果可知路由规则已经生效select语句均到从库上执行了。