MySQL ProxySQL读写分离实践

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

测试

本文测试环境是在上一篇文章的基础上进行的所以已经做了一主一从的读写分离。在此基础上若从库挂了会怎么样这里先把从库132shutdown掉看看读去了哪里。

tips如何修改管理接口的用户名密码除了初始化时候修改配置文件还有一个方法是在管理端口设置

admin@127.0.0.1 : (none) 12:52:53>set admin-admin_credentials='zjy:zjy';                                                                                                            Query OK, 1 row affected (0.00 sec)

admin模块修改select * from global_variables where variable_name like 'admin%';需要用admin加载

admin@127.0.0.1 : (none) 12:53:02>load admin variables to runtime;
Query OK, 0 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 12:53:33>save admin variables to disk;
Query OK, 9 rows affected (0.00 sec)

1从库不可用

① 关闭从库

当前ProxySQL下后端MySQL的运行情况

admin@127.0.0.1 : (none) 11:25:26>select hostgroup_id,hostname,port,status from runtime_mysql_servers;+--------------+-----------------+------+--------+| hostgroup_id | hostname        | port | status |+--------------+-----------------+------+--------+| 100          | 192.168.200.202 | 3306 | ONLINE || 1000         | 192.168.200.132 | 3306 | ONLINE |+--------------+-----------------+------+--------+

shutdown从库132后后端MySQL的运行情况

admin@127.0.0.1 : (none) 11:33:24>select hostgroup_id,hostname,port,status from runtime_mysql_servers;+--------------+-----------------+------+---------+| hostgroup_id | hostname        | port | status  |+--------------+-----------------+------+---------+| 100          | 192.168.200.202 | 3306 | ONLINE  || 1000         | 192.168.200.132 | 3306 | SHUNNED |+--------------+-----------------+------+---------+

此时读的操作会报超时

sbuser@192.168.200.24 : sbtest 11:30:40>select * from x;
ERROR 9001 (HY000): Max connect timeout reached while reaching hostgroup 1000 after 10000ms

原因是proxysql的核心都在规则shutdown从之后proxysql还是想路由到 hostgroup=1000它不会自动选择默认的100(mysql_users里配置的default_hostgroup) 。

这里解决的办法是在mysql_servers的hostgroup 1000 里面要插一条主库的记录然后把weight设小当读不到从库回去主库查询。

admin@127.0.0.1 : (none) 11:50:13>insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) values(1000,'192.168.200.202',3306,1,1000,10,'test proxysql');
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 11:50:42>update mysql_servers set weight=9 where hostgroup_id=1000 and hostname='192.168.200.132';
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 11:53:41>select hostgroup_id,hostname,port,weight from mysql_servers;+--------------+-----------------+------+--------+| hostgroup_id | hostname        | port | weight |+--------------+-----------------+------+--------+| 100          | 192.168.200.202 | 3306 | 1      || 1000         | 192.168.200.132 | 3306 | 9      || 1000         | 192.168.200.202 | 3306 | 1      |+--------------+-----------------+------+--------+admin@127.0.0.1 : (none) 11:54:03>load mysql servers to runtime;
Query OK, 0 rows affected (0.01 sec)

admin@127.0.0.1 : (none) 11:54:28>save mysql servers to disk;                                                                                                             
Query OK, 0 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 11:54:38>select hostgroup_id,hostname,port,status from runtime_mysql_servers;+--------------+-----------------+------+---------+| hostgroup_id | hostname        | port | status  |+--------------+-----------------+------+---------+| 100          | 192.168.200.202 | 3306 | ONLINE  || 1000         | 192.168.200.132 | 3306 | SHUNNED || 1000         | 192.168.200.202 | 3306 | ONLINE  |+--------------+-----------------+------+---------+

此时读的操作正常

sbuser@192.168.200.24 : sbtest 11:52:37>select * from x;+------+| id   |+------+|  123 ||  123 ||  123 |+------+3 rows in set (0.01 sec)

说明从关闭了之后读操作确实去主上执行了。当从库恢复之后以后的读操作主库也可以处理 1/10 的读请求。

② 从库延迟/从库停止复制

在上一篇文章中已经建立了监控账号proxysql由于需要执行show slave status的命令来获得延迟时间所以需要权限SUPER 和 REPLICATION CLIENT。并且需要设置mysql_servers.max_replication_lag的值由于mysql_servers.max_replication_lag仅适用于从但也可以将其配置为所有主机无论是从还是主不会有任何影响。

-- 设置监控账号权限dba@192.168.200.202 : sbtest 10:44:38>GRANT SUPER, REPLICATION CLIENT ON *.* TO 'proxysql'@'192.168.200.24' IDENTIFIED BY PASSWORD '*BF27B4C7AAD278126E228AA8427806E870F64F39';
Query OK, 0 rows affected (0.00 sec)-- 设置延迟的阈值admin@127.0.0.1 : (none) 11:04:50>UPDATE mysql_servers SET max_replication_lag=5;                                                                                                    Query OK, 3 rows affected (0.00 sec)-- 应用配置admin@127.0.0.1 : (none) 11:04:54>load mysql servers to runtime;                                                                                                                   
Query OK, 0 rows affected (0.01 sec)

admin@127.0.0.1 : (none) 11:05:04>save mysql servers to disk;                                                                                                                      
Query OK, 0 rows affected (0.01 sec)

主从复制正常的情况下后端MySQL的情况

admin@127.0.0.1 : (none) 11:05:13>select hostgroup_id,hostname,port,status,max_replication_lag from runtime_mysql_servers;+--------------+-----------------+------+--------+---------------------+| hostgroup_id | hostname        | port | status | max_replication_lag |+--------------+-----------------+------+--------+---------------------+| 1000         | 192.168.200.132 | 3306 | ONLINE | 5                   || 1000         | 192.168.200.202 | 3306 | ONLINE | 5                   || 100          | 192.168.200.202 | 3306 | ONLINE | 5                   |+--------------+-----------------+------+--------+---------------------+3 rows in set (0.00 sec)

从库执行stop slave之后后端MySQL的情况

admin@127.0.0.1 : (none) 11:06:52>select hostgroup_id,hostname,port,status,max_replication_lag from runtime_mysql_servers;+--------------+-----------------+------+---------+---------------------+| hostgroup_id | hostname        | port | status  | max_replication_lag |+--------------+-----------------+------+---------+---------------------+| 1000         | 192.168.200.132 | 3306 | SHUNNED | 5                   || 1000         | 192.168.200.202 | 3306 | ONLINE  | 5                   || 100          | 192.168.200.202 | 3306 | ONLINE  | 5                   |+--------------+-----------------+------+---------+---------------------+3 rows in set (0.00 sec)

此时132从库不可用读都到了HG 1000的202上去了可以自行测试。 也可以在日志里看到

2017-05-11 11:06:43 MySQL_HostGroups_Manager.cpp:934:replication_lag_action(): [WARNING] Shunning server 192.168.200.132:3306 with replication lag of 60 second

日志显示延迟60s这个是怎么回事这里需要说明下几个变量

mysql-monitor_replication_lag_interval主从延迟检测时间默认10秒。

mysql-monitor_slave_lag_when_null当为null时设置的延迟值默认为60。

admin@127.0.0.1 : (none) 11:08:35>select * from global_variables where variable_name like 'mysql-monitor%lag%';+----------------------------------------+----------------+| variable_name                          | variable_value |+----------------------------------------+----------------+| mysql-monitor_replication_lag_interval | 10000           || mysql-monitor_replication_lag_timeout  | 1000           || mysql-monitor_slave_lag_when_null      | 60             |+----------------------------------------+----------------+3 rows in set (0.00 sec)

根据mysql_servers.max_replication_lag设置的阈值这2个参数可以根据自己的情况来设置比如设置检测时间为1500。延迟的记录也可以通过表来查看

admin@127.0.0.1 : (none) 11:19:47>select * from mysql_server_replication_lag_log limit 3;+-----------------+------+------------------+-----------------+----------+-------+| hostname        | port | time_start_us    | success_time_us | repl_lag | error |+-----------------+------+------------------+-----------------+----------+-------+| 192.168.200.132 | 3306 | 1494472189886932 | 411             | 0        | NULL  || 192.168.200.202 | 3306 | 1494472189887224 | 372             | NULL     | NULL  || 192.168.200.202 | 3306 | 1494472189887640 | 325             | NULL     | NULL  |+-----------------+------+------------------+-----------------+----------+-------+3 rows in set (0.00 sec)

主从延迟的情况和stop slave的情况一样只是stop slave是把延迟设置成了60s。

小结通过上面的测试说明ProxySQL可以在从库不可用时进行下线不需要人为再进行干预等到恢复正常之后自动上线提供服务。

2多路由规则

① 根据库路由

在现有基础上再增加一个主从

M192.168.200.97S192.168.200.245

授权账号程序和监控账号

dba@192.168.200.97 : proxysql 12:39:39>GRANT SUPER, REPLICATION CLIENT ON *.* TO 'proxysql'@'192.168.200.24' IDENTIFIED BY PASSWORD '*BF27B4C7AAD278126E228AA8427806E870F64F39';
Query OK, 0 rows affected (0.01 sec)
dba@192.168.200.97 : proxysql 12:42:50>grant select,insert,update,delete on proxysql.* to proxysql@192.168.200.24 identified by 'proxysql';
Query OK, 0 rows affected (0.00 sec)

配置ProxySQL 

admin.. : (none) ::  mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) (,,,,,, row affected (.. : (none) ::  mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) (,,,,,, row affected (.. : (none) ::  mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) (,,,,,, row affected (.. : (none) ::  mysql_users(username,password,active,default_hostgroup,transaction_persistent) (,,,, row affected (.. : (none) ::  mysql_query_rules(active,schemaname,match_pattern,destination_hostgroup,apply) (,,,, row affected (.. : (none) ::.. : (none) ::  mysql_query_rules(active,schemaname,match_pattern,destination_hostgroup,apply) (,,,, row affected (admin.. : (none) :: mysql servers  runtime;.. : (none) :: mysql users .. : (none) :: mysql query rules .. : (none) :: mysql servers  ;.. : (none) :: mysql users  .. : (none) :: mysql query rules

rules、servers、users信息

admin@127.0.0.1 : (none) 03:28:11>select rule_id,active,username,schemaname,client_addr,destination_hostgroup,match_pattern,flagIN,flagOUT,apply from mysql_query_rules;+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| rule_id | active | username | schemaname | client_addr | destination_hostgroup | match_pattern        | flagIN | flagOUT | apply |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| 3       | 1      | NULL     | NULL       | NULL        | 100                   | ^SELECT.*FOR UPDATE$ | 0      | NULL    | 1     || 4       | 1      | NULL     | NULL       | NULL        | 1000                  | ^SELECT              | 0      | NULL    | 1     || 5       | 1      | NULL     | proxysql   | NULL        | 101                   | ^SELECT.*FOR UPDATE$ | 0      | NULL    | 1     || 6       | 1      | NULL     | proxysql   | NULL        | 1001                  | ^SELECT              | 0      | NULL    | 1     |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+4 rows in set (0.00 sec)

admin@127.0.0.1 : (none) 03:29:10>select username,default_hostgroup from mysql_users;+----------+-------------------+| username | default_hostgroup |+----------+-------------------+| sbuser   | 100               || proxysql | 101               |+----------+-------------------+2 rows in set (0.00 sec)

admin@127.0.0.1 : (none) 03:29:28>select hostgroup_id,hostname,port,status from mysql_servers;+--------------+-----------------+------+--------+| hostgroup_id | hostname        | port | status |+--------------+-----------------+------+--------+| 1000         | 192.168.200.132 | 3306 | ONLINE || 100          | 192.168.200.202 | 3306 | ONLINE || 1000         | 192.168.200.202 | 3306 | ONLINE || 101          | 192.168.200.97  | 3306 | ONLINE || 1001         | 192.168.200.245 | 3306 | ONLINE || 1001         | 192.168.200.97  | 3306 | ONLINE |+--------------+-----------------+------+--------+6 rows in set (0.00 sec)

模拟app连接

/Users/jinyizhou [15:32:09] ~$ mysql -uproxysql -pproxysql -h192.168.200.24 -P6033 -A...
proxysql@192.168.200.24 : (none) 03:32:11>show databases;+--------------------+| Database           |+--------------------+| information_schema || proxysql           |+--------------------+2 rows in set (0.00 sec)

proxysql@192.168.200.24 : (none) 03:32:13>use proxysqlDatabase changed
proxysql@192.168.200.24 : proxysql 03:32:17>show tables;+--------------------+| Tables_in_proxysql |+--------------------+| xx                 |+--------------------+1 row in set (0.00 sec)proxysql@192.168.200.24 : proxysql 03:32:24>insert into xx values(999);
Query OK, 1 row affected (0.00 sec)

proxysql@192.168.200.24 : proxysql 03:35:49>select * from xx;
ERROR 1044 (#4200): Access denied for user 'proxysql'@'192.168.200.24' to database 'proxysql'

只有select的时候没有权限其他insertupdate等都是有权限的为啥呢原因是这里的路由关系ProxySQL的读写分离核心就是路由这里因为select的路由错了到了HG为1000的主从上了

admin@127.0.0.1 : (none) 03:32:28>select hostgroup,schemaname,username,digest_text from stats_mysql_query_digest;+-----------+--------------------+----------+----------------------------------+| hostgroup | schemaname         | username | digest_text                      |+-----------+--------------------+----------+----------------------------------+| 1000      | proxysql           | proxysql | select * from xx                 || 101       | proxysql           | proxysql | show tables                      || 101       | information_schema | proxysql | show databases                   || 1000      | information_schema | proxysql | SELECT DATABASE()                || 101       | information_schema | proxysql | select USER()                    || 101       | information_schema | proxysql | select @@version_comment limit ? |+-----------+--------------------+----------+----------------------------------+

mysql_query_rules是整个ProxySQL的核心上篇文章已经对该表进行了说明在这里对这例子再次讲解下

rule_id是表的自增主键路由规则处理是以 rule_id 的顺序进行匹配若没有找到规则就直接去mysql_users.default_hostgroup字段里找。上面信息中除了select之外的其他操作都找不到规则就直接去users表里取所以这些操作不会报错。而我们执行的select被rule_id为3的规则匹配上因为rule_id=3的是匹配所有库并且apply=1表示该正则匹配后将不再接受其他匹配直接转发。这样就转发到了HG为1000上面的主机上了就报没有权限的错误。若apply=0则继续匹配下面若没有找到路由规则则返回再看flagOUT是否为NULL是NULL则直接匹配否则报错。大致的流程如下

flagIN, flagOUT, apply: 用来定义路由链 chains

通过上面的说明如何读取到正确的HG呢这里可以设置apply=0

admin@127.0.0.1 : (none) 04:18:45>update mysql_query_rules set apply=0 where rule_id in (3,4);                                                                                       
Query OK, 2 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 04:18:56>load mysql query rules to runtime;                                                                                                                 Query OK, 0 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 04:18:59>save mysql query rules to disk;                                                                                                                    Query OK, 0 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 04:19:01>select rule_id,active,username,schemaname,client_addr,destination_hostgroup,match_pattern,flagIN,flagOUT,apply from mysql_query_rules;+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| rule_id | active | username | schemaname | client_addr | destination_hostgroup | match_pattern        | flagIN | flagOUT | apply |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| 3       | 1      | NULL     | NULL       | NULL        | 100                   | ^SELECT.*FOR UPDATE$ | 0      | NULL    | 0     || 4       | 1      | NULL     | NULL       | NULL        | 1000                  | ^SELECT              | 0      | NULL    | 0     || 5       | 1      | NULL     | proxysql   | NULL        | 101                   | ^SELECT.*FOR UPDATE$ | 0      | NULL    | 1     || 6       | 1      | NULL     | proxysql   | NULL        | 1001                  | ^SELECT              | 0      | NULL    | 1     |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+4 rows in set (0.00 sec)

和上面一样模拟app连接得到的信息发现全部走了正确的路由。

admin@127.0.0.1 : (none) 05:58:55>select hostgroup,schemaname,username,digest_text from stats_mysql_query_digest;+-----------+------------+----------+----------------------------------+| hostgroup | schemaname | username | digest_text                      |+-----------+------------+----------+----------------------------------+| 101       | proxysql   | proxysql | insert into xx values(?)         || 1001      | proxysql   | proxysql | select * from xx                 || 1001      | proxysql   | proxysql | SELECT DATABASE()                || 101       | proxysql   | proxysql | select USER()                    || 101       | proxysql   | proxysql | select @@version_comment limit ? |+-----------+------------+----------+----------------------------------+5 rows in set (0.00 sec)

查看路由规则的命中情况

admin@127.0.0.1 : (none) 05:59:19>select * from stats_mysql_query_rules;+---------+------+| rule_id | hits |+---------+------+| 3       | 0    || 4       | 4    || 5       | 0    || 6       | 4    |+---------+------+4 rows in set (0.00 sec)

从上面看到apply=0 & falgOUT=null会继续往下找路由找到了rule_id=6的直接进行转发。apply=1 直接转发flagOUT != null 直接转发。

小结通过上面的测试说明ProxySQL只要设置好路由规则可以有多个主库。

② 根据用户名路由

和多主路由一样区别是写入到路由表的字段不一样

admin@127.0.0.1 : (none) 06:09:20>INSERT INTO mysql_query_rules(active,username,match_pattern,destination_hostgroup,apply) VALUES(1,'proxysql','^SELECT.*FOR UPDATE$',101,1);
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 06:10:09>INSERT INTO mysql_query_rules(active,username,match_pattern,destination_hostgroup,apply) VALUES(1,'proxysql','^SELECT',1001,1);
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 06:10:32>select rule_id,active,username,schemaname,client_addr,destination_hostgroup,match_pattern,flagIN,flagOUT,apply from mysql_query_rules;+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| rule_id | active | username | schemaname | client_addr | destination_hostgroup | match_pattern        | flagIN | flagOUT | apply |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| 3       | 1      | NULL     | NULL       | NULL        | 100                   | ^SELECT.*FOR UPDATE$ | 0      | NULL    | 1     || 4       | 1      | NULL     | NULL       | NULL        | 1000                  | ^SELECT              | 0      | NULL    | 1     || 1405    | 1      | proxysql | NULL       | NULL        | 101                   | ^SELECT.*FOR UPDATE$ | 0      | NULL    | 1     || 1406    | 1      | proxysql | NULL       | NULL        | 1001                  | ^SELECT              | 0      | NULL    | 1     |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+4 rows in set (0.00 sec)

3flagIN/flahOUT规则链实现多实例推荐

和2中的条件一样先配置ProxySQL的serversusers

admin@127.0.0.1 : (none) 01:09:52>insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) 
    -> values
    -> (100, '192.168.200.202', 3306, 1, 10, 'ReadWrite'),    -> (1000, '192.168.200.202', 3306, 1, 10, 'ReadWrite'),    -> (1000, '192.168.200.132', 3306, 9, 10, 'ReadOnly');
Query OK, 3 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 01:09:54>insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) 
    -> values
    -> (101, '192.168.200.97', 3306, 1, 10, 'ReadWrite'),    -> (1001, '192.168.200.97', 3306, 1, 10, 'ReadWrite'),    -> (1001, '192.168.200.245', 3306, 9, 10, 'ReadOnly');
Query OK, 3 rows affected (0.01 sec)

admin@127.0.0.1 : (none) 01:11:01>insert into mysql_users(username, password,active,default_hostgroup,transaction_persistent)    -> values
    -> ('sbuser', 'sbuser', 1, 100, 1),    -> ('proxysql', 'proxysql', 1, 101, 1);
Query OK, 2 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 01:19:44>set mysql-monitor_username='proxysql';
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 01:19:44>set mysql-monitor_password='proxysql';
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 01:56:09>select hostgroup_id,hostname,port,status from mysql_servers;+--------------+-----------------+------+--------+| hostgroup_id | hostname        | port | status |+--------------+-----------------+------+--------+| 100          | 192.168.200.202 | 3306 | ONLINE || 1000         | 192.168.200.202 | 3306 | ONLINE || 1000         | 192.168.200.132 | 3306 | ONLINE || 101          | 192.168.200.97  | 3306 | ONLINE || 1001         | 192.168.200.97  | 3306 | ONLINE || 1001         | 192.168.200.245 | 3306 | ONLINE |+--------------+-----------------+------+--------+6 rows in set (0.00 sec)

admin@127.0.0.1 : (none) 01:58:18>select username,default_hostgroup from mysql_users;+----------+-------------------+| username | default_hostgroup |+----------+-------------------+| sbuser   | 100               || proxysql | 101               |+----------+-------------------+

再配置flagOUT/flagINflag20是读flag21是写

admin@127.0.0.1 : (none) 01:21:34>INSERT INTO mysql_query_rules(rule_id,active,match_pattern,apply,flagOUT) VALUES(49,1,'^SELECT.*FOR UPDATE$',0,21);
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 01:27:18>INSERT INTO mysql_query_rules(rule_id,active,match_pattern,apply,flagOUT) VALUES(50,1,'^SELECT',0,20);
Query OK, 1 row affected (0.00 sec)

admin@127.0.0.1 : (none) 01:32:11>insert into mysql_query_rules(active,schemaname,destination_hostgroup,apply,flagIN,flagOUT) values
    -> (1,'sbtest',100,1,21,21), 
    -> (1,'proxysql',101,1,21,21);
Query OK, 2 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 01:32:53>insert into mysql_query_rules(active,schemaname,destination_hostgroup,apply,flagIN,flagOUT) values
    -> (1,'sbtest',1000,1,20,20),    -> (1,'proxysql',1001,1,20,20);
Query OK, 2 rows affected (0.00 sec)

admin@127.0.0.1 : (none) 01:58:28>select rule_id,active,username,schemaname,client_addr,destination_hostgroup,match_pattern,flagIN,flagOUT,apply from mysql_query_rules;+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| rule_id | active | username | schemaname | client_addr | destination_hostgroup | match_pattern        | flagIN | flagOUT | apply |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+| 49      | 1      | NULL     | NULL       | NULL        | NULL                  | ^SELECT.*FOR UPDATE$ | 0      | 21      | 0     || 50      | 1      | NULL     | NULL       | NULL        | NULL                  | ^SELECT              | 0      | 20      | 0     || 51      | 1      | NULL     | sbtest     | NULL        | 100                   | NULL                 | 21     | 21      | 1     || 52      | 1      | NULL     | proxysql   | NULL        | 101                   | NULL                 | 21     | 21      | 1     || 53      | 1      | NULL     | sbtest     | NULL        | 1000                  | NULL                 | 20     | 20      | 1     || 54      | 1      | NULL     | proxysql   | NULL        | 1001                  | NULL                 | 20     | 20      | 1     |+---------+--------+----------+------------+-------------+-----------------------+----------------------+--------+---------+-------+

最后保存上线

-- 应用load mysql users to runtime;load mysql servers to runtime;load mysql variables to runtime;LOAD MYSQL QUERY RULES TO RUN;-- 保存到磁盘save mysql users to disk;save mysql servers to disk;save mysql variables to disk;SAVE MYSQL QUERY RULES TO DISK;save mysql users to mem;  -- 可以屏蔽看到的明文密码

app连接测试

1连接实例202

[zhoujy@localhost ~]$ mysql -usbuser -psbuser -h192.168.200.24 -P6033
...
sbuser@192.168.200.24 : (none) 02:19:41>show databases;                                                                                                                           +--------------------+| Database           |+--------------------+| information_schema || sbtest             |+--------------------+2 rows in set (0.00 sec)

sbuser@192.168.200.24 : (none) 02:19:44>use sbtestDatabase changed
sbuser@192.168.200.24 : sbtest 02:19:48>show tables;
...
sbuser@192.168.200.24 : sbtest 02:19:57>insert into x values(10000);
...
sbuser@192.168.200.24 : sbtest 02:20:10>select * from x;
...

相关信息路由的信息都是正确的

admin@127.0.0.1 : (none) 02:24:15>select hostgroup,schemaname,username,digest_text,count_star from stats_mysql_query_digest;+-----------+--------------------+----------+----------------------------------+------------+| hostgroup | schemaname         | username | digest_text                      | count_star |+-----------+--------------------+----------+----------------------------------+------------+| 1000      | sbtest             | sbuser   | select * from x                  | 5          || 100       | sbtest             | sbuser   | insert into x values(?)          | 5          || 100       | sbtest             | sbuser   | show tables                      | 2          || 100       | sbtest             | sbuser   | show databases                   | 1          || 100       | information_schema | sbuser   | SELECT DATABASE()                | 1          || 100       | information_schema | sbuser   | show databases                   | 1          || 100       | information_schema | sbuser   | select USER()                    | 1          || 100       | information_schema | sbuser   | select @@version_comment limit ? | 1          |+-----------+--------------------+----------+----------------------------------+------------+8 rows in set (0.00 sec)
--路由命中
admin@127.0.0.1 : (none) 02:25:13>admin@1* from stats_mysql_query_rules;+---------+------+| rule_id | hits |+---------+------+| 49      | 0    || 50      | 6    || 51      | 0    || 52      | 0    || 53      | 5    || 54      | 0    |+---------+------+6 rows in set (0.00 sec)

结论通过实例202的账号访问ProxySQL首先会检查flagIN=0在其上面进行匹配Proxysql入口都是flagIN =0顺序往下 匹配到之后检查flagOUT发现 flagOUT不为NULL且flagIN !0= flagOUT 20则进入以flagIN为上一个flagOUT值的新规则链即20。再去flagIN=20里匹配最终匹配到了rule_id=53的规则最后转发。

2连接实例97

相关情况和上面一样最终通过rule_id=54进行转发。

建议若要用ProxySQL来控制多主从实例的读写分离推荐使用flagIN/flahOUT规则链实现多实例

4flagIN/flahOUT规则链实现分库

目的客户端应用连接上 proxysql 的ip:port连接时指定分库db名自动路由到对应的实例、对应的库。

① 环境

APP192.168.200.25、192.168.200.64M1
     IP192.168.200.202
     Port3306
     DBM1、M2、M3

S1
     IP192.168.200.132
     Port3306
     DBM1、M2、M3

M2
     IP192.168.200.97
     Port3306
     DBM4、M5、M6

S2
     IP192.168.200.245
     Port3306
     DBM4、M5、M6

ProxySQL192.168.200.24

② 搭建

和之前一样先在后端数据库创建程序和检测账号

--程序账号GRANT SELECT,INSERT,UPDATE,DELETE ON `mtest%`.* TO 'mtest'@'192.168.200.24' identified by 'mtest';--健康检测账号GRANT SUPER, REPLICATION CLIENT ON *.* TO 'proxysql'@'192.168.200.24' IDENTIFIED BY 'proxysql';

配置ProxySQL

--插入后端用户信息insert into mysql_users(username, password,active,transaction_persistent) values('mtest','mtest',1,1);--插入后端数据库信息insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) values(100,'192.168.200.202',3306,1,10,'test proxysql'); 
insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) values(1000,'192.168.200.132',3306,9,10,'test proxysql'); 
insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) values(1000,'192.168.200.202',3306,1,10,'test proxysql');insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) values(101,'192.168.200.97',3306,1,10,'test proxysql'); 
insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) values(1001,'192.168.200.245',3306,9,10,'test proxysql'); 
insert into mysql_servers(hostgroup_id,hostname,port,weight,max_replication_lag,comment) values(1001,'192.168.200.97',3306,1,10,'test proxysql');--配置健康检测信息set mysql-monitor_username='proxysql';set mysql-monitor_password='proxysql';

应用保存配置

-- 应用load mysql users to runtime;load mysql servers to runtime;load mysql variables to runtime;-- 保存到磁盘save mysql users to disk;save mysql servers to disk;save mysql variables to disk;save mysql users to mem;  -- 可以屏蔽看到的明文密码

配置路由规则

----添加读写分离的路由--写:写的入口 flagIN=0INSERT INTO mysql_query_rules(rule_id,active,match_pattern,apply,flagOUT) VALUES(49,1,'^SELECT.*FOR UPDATE$',0,21);--读读的入口 flagIN=0INSERT INTO mysql_query_rules(rule_id,active,match_pattern,apply,flagOUT) VALUES(50,1,'^SELECT',0,20);--反向匹配相当于对 match_digest/match_pattern 的匹配取反非select即写INSERT INTO mysql_query_rules(rule_id, active,match_pattern,negate_match_pattern,apply,flagOUT) values(60, 1,'^SELECT',1,0,21);----为后端服务器配置路由--读insert into mysql_query_rules(active,schemaname,destination_hostgroup,apply,flagIN,flagOUT) values(1,'M1',1000,1,20,20),(1,'M2',1000,1,20,20),(1,'M3',1000,1,20,20);insert into mysql_query_rules(active,schemaname,destination_hostgroup,apply,flagIN,flagOUT) values(1,'M4',1001,1,20,20),(1,'M5',1001,1,20,20),(1,'M6',1001,1,20,20);--写insert into mysql_query_rules(active,schemaname,destination_hostgroup,apply,flagIN,flagOUT) values(1,'M1',100,1,21,21),(1,'M2',100,1,21,21),(1,'M3',100,1,21,21);insert into mysql_query_rules(active,schemaname,destination_hostgroup,apply,flagIN,flagOUT) values(1,'M4',101,1,21,21),(1,'M5',101,1,21,21),(1,'M6',101,1,21,21);--连接时若没有指定数据库则进行show databases/tables、use 等会超时出错连接时默认的数据库是在information_schema所以写一条根据information_schema库的路由,直接返回错误信息。insert into mysql_query_rules(rule_id,active,schemaname,apply,flagOUT) values(20,1,'information_schema',0,302);insert into mysql_query_rules(rule_id,active,apply, flagIN,flagOUT,error_msg) values(9999,1,1, 302,302,'No query rules matched (by ProxySQL)');--连接时若没有指定数据库则可以使用 schemaname.tablename 的形式匹配数据。insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply,flagIN,flagOUT) values(1000,1,'([\s\`])M(1|2|3)([\.\`])',100,1,302,302);insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply,flagIN,flagOUT) values(1001,1,'([\s\`])M(4|5|6)([\.\`])',101,1,302,302);

应用规则

LOAD MYSQL QUERY RULES TO RUN;SAVE MYSQL QUERY RULES TO DISK;

最终的路由规则如下

select rule_id,schemaname,destination_hostgroup,match_pattern,negate_match_pattern,flagIN,flagOUT,apply,error_msg from mysql_query_rules;

 
app连接测试

~$ mysql -umtest -pmtest -h192.168.200.24 -P6033 -A
...
mtest@192.168.200.24 : (none) 11:27:29>show databases;  --触发了定义的路由                                                                                                                          ERROR 1148 (42000): No query rules matched (by ProxySQL)mtest@192.168.200.24 : (none) 11:27:34>select * from M5.mtest5; --可以直接用schema.tables 访问+------+| id   |+------+|    5 ||   55 |+------+2 rows in set (0.00 sec)

mtest@192.168.200.24 : (none) 11:27:47>use M1 --切换数据库Database changed
mtest@192.168.200.24 : M1 11:27:52>show tables;  --可以show了+--------------+| Tables_in_M1 |+--------------+| mtest1       |+--------------+1 row in set (0.00 sec)

mtest@192.168.200.24 : M1 11:27:56>select * from mtest1;+------+| id   |+------+|    1 ||   11 ||  111 || 1111 |+------+4 rows in set (0.00 sec)

mtest@192.168.200.24 : M1 11:28:02>insert into mtest1 values(11111);
Query OK, 1 row affected (0.00 sec)

mtest@192.168.200.24 : M1 11:28:11>select * from mtest1;+-------+| id    |+-------+|     1 ||    11 ||   111 ||  1111 || 11111 |+-------+5 rows in set (0.00 sec)

mtest@192.168.200.24 : M1 11:28:12>show databases; --可以show了+--------------------+| Database           |+--------------------+| information_schema || M1                 || M2                 || M3                 |+--------------------+4 rows in set (0.00 sec)

mtest@192.168.200.24 : M1 11:28:20>use M5  --切换到另一个实例的dbDatabase changed
mtest@192.168.200.24 : M5 11:28:52>show databases;+--------------------+| Database           |+--------------------+| information_schema || M4                 || M5                 || M6                 |+--------------------+4 rows in set (0.00 sec)

mtest@192.168.200.24 : M5 11:28:55>select * from mtest5;+------+| id   |+------+|    5 ||   55 |+------+2 rows in set (0.00 sec)

mtest@192.168.200.24 : M5 11:29:03>insert into mtest5 values(555);
Query OK, 1 row affected (0.01 sec)

mtest@192.168.200.24 : M5 11:29:12>select * from mtest5;+------+| id   |+------+|    5 ||   55 ||  555 |+------+3 rows in set (0.00 sec)

查看路由命中率

select active,hits, mysql_query_rules.rule_id, schemaname,match_pattern,destination_hostgroup hostgroup,flagIn,flagOUT   FROM mysql_query_rules NATURAL JOIN stats.stats_mysql_query_rules ORDER BY mysql_query_rules.rule_id;

查看SQL统计信息

admin@127.0.0.1 : (none) 11:36:46>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 |+-----------+--------------------+----------+----------------------------------+------------+| 101       | M5                 | mtest    | show databases                   | 1          || 1000      | M1                 | mtest    | SELECT DATABASE()                | 1          || 101       | M5                 | mtest    | insert into mtest5 values(?)     | 1          || 100       | M1                 | mtest    | show databases                   | 1          || 100       | M1                 | mtest    | insert into mtest1 values(?)     | 1          || 1000      | M1                 | mtest    | select * from mtest1             | 2          || 1001      | M5                 | mtest    | select * from mtest5             | 2          || 100       | M1                 | mtest    | show tables                      | 1          || 101       | information_schema | mtest    | select * from M5.mtest5          | 1          || 0         | information_schema | mtest    | show databases                   | 1          || 0         | information_schema | mtest    | SELECT DATABASE()                | 1          || 0         | information_schema | mtest    | select USER()                    | 1          || 0         | information_schema | mtest    | select @@version_comment limit ? | 1          |+-----------+--------------------+----------+----------------------------------+------------+

具体的说明可以看ProxySQL之读写分离与分库路由演示到此读写分离的测试介绍完毕

5查询重写 

查询重写这种对于线上环境SQL问题引起的紧急故障处理还是很有用处的。如果定位到了问题所在必须修改SQL时间紧急这时查询重写这个东西就非常有用了。类似于MySQL5.7的查询重写插件。这里做下相关的说明

ProxySQL的核心就是路由查询重写也只是添加一条路由而已在4的基础上进行测试

select * from mtest1 order by id 
重写成select * from mtest1

添加路由

--查询的路由flagIN=0当匹配上规则后进行重写并且不应用而通过flagOUT下去继续查询INSERT INTO mysql_query_rules (rule_id,active,match_pattern,replace_pattern,apply,flagOUT) VALUES (48,1,'(.*)order by id','\1',0,20);

其实查询重写的实现在proxysql中也实现为正则匹配替换表示当proxysql匹配到<若干字符>order by id这个模式后就将这个模式的order by id去掉。那么\1是什么意思呢就是sed的向前引用。

加载load和save完rules之后查看是否重写成功

--初始#查询路由命中信息
admin@127.0.0.1 : (none) 02:44:52>select * from stats_mysql_query_rules;                                                                                                             +---------+------+| rule_id | hits |+---------+------+| 20      | 0    || 48      | 0    || 49      | 0    || 50      | 0    || 60      | 0    || 61      | 0    || 62      | 0    || 63      | 0    || 64      | 0    || 65      | 0    || 66      | 0    || 67      | 0    || 68      | 0    || 69      | 0    || 70      | 0    || 71      | 0    || 72      | 0    || 1000    | 0    || 1001    | 0    || 9999    | 0    |+---------+------+20 rows in set (0.00 sec)

#查询统计信息
admin@127.0.0.1 : (none) 02:45:09>select * from stats_mysql_query_digest;
Empty set (0.00 sec)--操作~$ mysql -umtest -pmtest -h192.168.200.24 -P6033 -A
...
mtest@192.168.200.24 : (none) 02:45:27>use M1                                                                                                                                        Database changed
mtest@192.168.200.24 : M1 02:45:31>show tables;+--------------+| Tables_in_M1 |+--------------+| mtest1       |+--------------+1 row in set (0.00 sec)

mtest@192.168.200.24 : M1 02:45:33>select * from mtest1;+-------+| id    |+-------+|     1 ||    11 ||   111 ||  1111 || 11111 |+-------+5 rows in set (0.00 sec)

mtest@192.168.200.24 : M1 02:45:37>select * from mtest1 order by id;+-------+| id    |+-------+|     1 ||    11 ||   111 ||  1111 || 11111 |+-------+5 rows in set (0.00 sec)

mtest@192.168.200.24 : M1 02:45:46>select * from mtest1 order by id;+-------+| id    |+-------+|     1 ||    11 ||   111 ||  1111 || 11111 |+-------+5 rows in set (0.01 sec)----以上执行了2次order by id和1此没有order by id的查询去查询统计应该得到的值是3次没有order by id的查询。--验证#查询统计信息查看没有order by id的SQL出现了3次没有出现有order by id的SQL
admin@127.0.0.1 : (none) 02:49:49>select hostgroup,schemaname,digest_text,count_star from stats_mysql_query_digest;+-----------+--------------------+----------------------------------+------------+| hostgroup | schemaname         | digest_text                      | count_star |+-----------+--------------------+----------------------------------+------------+| 1000      | M1                 | select * from mtest1             | 3          || 100       | M1                 | show tables                      | 1          || 0         | information_schema | SELECT DATABASE()                | 1          || 0         | information_schema | select USER()                    | 1          || 0         | information_schema | select @@version_comment limit ? | 1          |+-----------+--------------------+----------------------------------+------------+5 rows in set (0.00 sec)

#重写查询的路由命中了2次
admin@127.0.0.1 : (none) 02:50:12>select * from stats_mysql_query_rules;+---------+------+| rule_id | hits |+---------+------+| 20      | 1    || 48      | 2    || 49      | 0    || 50      | 1    || 60      | 1    || 61      | 3    || 62      | 0    || 63      | 0    || 64      | 0    || 65      | 0    || 66      | 0    || 67      | 1    || 68      | 0    || 69      | 0    || 70      | 0    || 71      | 0    || 72      | 0    || 1000    | 0    || 1001    | 0    || 9999    | 1    |+---------+------+20 rows in set (0.00 sec)

从上面的结果看查询重写已经测试通过。到此关于ProxySQL的相关测试已经结束下面分析下和DBProxy的特性差别和性能差异。

性能测试 

环境

ProxySQL192.168.200.24DBProxy  192.168.200.24M
     IP192.168.200.202
     Port3306
     DBsbtest
S
     IP192.168.200.132
     Port3306
     DBsbtest

读写混合(oltp_read_write.lua)测试对比

直连数据库

./bin/sysbench --test=./share/sysbench/oltp_read_write.lua --mysql-host=192.168.200.202 --mysql-port=3306 --mysql-user=sbuser --mysql-password=sbuser --mysql-db=sbtest  --report-interval=10  --max-requests=0 --time=120 --threads=1 --tables=3  --table-size=1000000 prepare/run/cleanup

ProxySQL连接数据库

./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=120 --threads=1 --tables=3  --table-size=1000000 --skip-trx=on --db-ps-mode=disable --mysql-ignore-errors=1062 prepare/run/cleanup

DBProxy连接数据库

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

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

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

TPS

QPS

测试小结

在读写混合的模式下线程越少差距越大测试结果和美团点评DBProxy读写分离使用说明里的测试报告基本吻合这里主要对比ProxySQL和DBProxy的性能情况从上图看到二者性能差不多不过DBProxy的CPU消耗是ProxySQL的1到1.5倍。

总结

通过上面的一些基本介绍大致了解了ProxySQL读写分离功能的使用关于ProxySQL的其他功能内容在手册里有了详尽的介绍具体的情况请参考手册说明。现在大致整理下ProxySQL和DBproxy的差别:

连接池是 multiplexing。

②强大的正则路由可以自己干预读写路由算法。

③从库不可用自动下线不需要人为干预支持多主库。

④支持重写SQL。

⑤足够轻量配置简单。



本文转自 sshpp 51CTO博客,原文链接:http://blog.51cto.com/12902932/1927041,如需转载请自行联系原作者

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
关系型数据库 MySQL
mysql join 实践
mysql join 实践
20 0
|
4月前
|
SQL 关系型数据库 MySQL
mysql百万数据实践-索引
mysql百万数据实践-索引
31 0
|
4月前
|
存储 关系型数据库 MySQL
MySQL 读写分离原理
MySQL 读写分离原理
61 0
MySQL 读写分离原理
|
4月前
|
SQL 关系型数据库 MySQL
小白带你学习linux的mysql服务(主从mysql服务和读写分离三十一)
小白带你学习linux的mysql服务(主从mysql服务和读写分离三十一)
63 0
|
3月前
|
SQL 关系型数据库 MySQL
Mycat【Mycat部署安装(核心配置及目录结构、安装以及管理命令详解)Mycat高级特性(读写分离概述、搭建读写分离、MySQL双主双从原理)】(三)-全面详解(学习总结---从入门到深化)
Mycat【Mycat部署安装(核心配置及目录结构、安装以及管理命令详解)Mycat高级特性(读写分离概述、搭建读写分离、MySQL双主双从原理)】(三)-全面详解(学习总结---从入门到深化)
71 0
|
4月前
|
SQL 关系型数据库 MySQL
MySQL中的校对集/大小写敏感/sql_mode实践
MySQL中的校对集/大小写敏感/sql_mode实践
71 0
|
1月前
|
SQL 关系型数据库 MySQL
Sqoop【付诸实践 01】Sqoop1最新版 MySQL与HDFS\Hive\HBase 核心导入导出案例分享+多个WRAN及Exception问题处理(一篇即可学会在日常工作中使用Sqoop)
【2月更文挑战第9天】Sqoop【付诸实践 01】Sqoop1最新版 MySQL与HDFS\Hive\HBase 核心导入导出案例分享+多个WRAN及Exception问题处理(一篇即可学会在日常工作中使用Sqoop)
88 7
|
4月前
|
Java 关系型数据库 MySQL
②⑩② 【读写分离】Sharding - JDBC 实现 MySQL读写分离[SpringBoot框架]
②⑩② 【读写分离】Sharding - JDBC 实现 MySQL读写分离[SpringBoot框架]
40 0
|
4月前
|
SQL 存储 关系型数据库
MySQL中的数据备份与还原(导出导入)实践总结
MySQL中的数据备份与还原(导出导入)实践总结
277 1
|
30天前
|
关系型数据库 MySQL 数据库
使用 Docker 搭建一个“一主一从”的 MySQL 读写分离集群(超详细步骤
使用 Docker 搭建一个“一主一从”的 MySQL 读写分离集群(超详细步骤
59 0