15_ProxySQL讀寫分離
備注:文章編寫時間201904-201905期間,後續官方在github的更新沒有被寫入
~
ProxySQL讀寫分離設定[ProxySQL Read Write Split]
查詢路由是ProxySQL的核心功能之一。讀寫分離可能是最常用的查詢路由之一,而另一種最常用的查詢路由實作是用于分片(sharding)。
一、使用不同的端口實作讀寫分離[Read/write split using different ports]
如果使用類似haproxy的代理,可以将其配置為監聽兩個端口:一個端口作為寫入的端口,另一個端口作為讀取的端口。人們經常會問如何使用相同的方法配置ProxySQL,以及如何現實基于傳入端口的路由查詢。
下面是一個關于如何基于傳入端口實作查詢路由的示例,要在ProxySQL的Admin管理界面運作下面的内容。這裡假設您已經在正确的hostgroups中配置了master和slaves:
hostgroup 10中的mysql writer,hostgroup 20中的mysql readers。如果您正在使用Galera或組複制,則将應用類似的方法。步驟如下:
1、修改接入端口相關參數
将ProxySQL配置為監聽兩個端口并重新啟動:mysql-interfaces 是少數幾個在運作時無法動态更改,需要重新啟動的變量之一。
1)目前參數值
Admin> SELECT * FROM global_variables WHERE variable_name ='mysql-interfaces';
+------------------+----------------+
| variable_name | variable_value |
+------------------+----------------+
| mysql-interfaces | 0.0.0.0:6033 |
+------------------+----------------+
1 row in set (0.00 sec)
或用SHOW指令
Admin> SHOW VARIABLES LIKE 'mysql-interfaces';
+------------------+--------------+
| Variable_name | Value |
+------------------+--------------+
| mysql-interfaces | 0.0.0.0:6033 |
+------------------+--------------+
1 row in set (0.00 sec)
2)修改配置
Admin> UPDATE global_variables SET variable_value='0.0.0.0:6401;0.0.0.0:6402' WHERE variable_name ='mysql-interfaces';
或
Admin> SET mysql-interfaces = '0.0.0.0:6401;0.0.0.0:6402';
3)檢視修改結果
Admin> SELECT * FROM global_variables where variable_name ='mysql-interfaces';
+------------------+---------------------------+
| variable_name | variable_value |
+------------------+---------------------------+
| mysql-interfaces | 0.0.0.0:6401;0.0.0.0:6402 |
+------------------+---------------------------+
1 row in set (0.00 sec)
4)将修改結果持久化
Admin> SAVE MYSQL VARIABLES TO DISK;
Query OK, 110 rows affected (0.00 sec)
5)重新開機ProxySQL服務
Admin> PROXYSQL RESTART;
2、根據傳入端口添加路由規則
1)檢視目前後端MySQL主機組配置
Admin> SELECT * FROM mysql_servers;
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 1 | 188.188.0.68 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 2 | 188.188.0.69 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 2 | 188.188.0.70 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
3 rows in set (0.01 sec)
2)清除舊的分組
Admin> DELETE FROM mysql_servers ;
Query OK, 6 rows affected (0.00 sec)
Admin> SELECT * FROM mysql_servers;
Empty set (0.00 sec)
3)新增mysql writer組10和mysql readers組20
Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES(10,'188.188.0.68',3306),(20,'188.188.0.69',3306),(20,'188.188.0.70',3306);
Query OK, 3 rows affected (0.00 sec)
Admin> SELECT * FROM mysql_servers;
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 10 | 188.188.0.68 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 20 | 188.188.0.69 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 20 | 188.188.0.70 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
3 rows in set (0.00 sec)
4)檢視目前擁有的規則
Admin> SELECT rule_id,active,proxy_port,destination_hostgroup,apply FROM mysql_query_rules;
+---------+--------+------------+-----------------------+-------+
| rule_id | active | proxy_port | destination_hostgroup | apply |
+---------+--------+------------+-----------------------+-------+
| 5 | 1 | NULL | NULL | 1 |
| 10 | 1 | NULL | 2 | 1 |
| 20 | 1 | NULL | 2 | 0 |
| 30 | 1 | NULL | NULL | 1 |
+---------+--------+------------+-----------------------+-------+
4 rows in set (0.00 sec)
5)删除舊規則
Admin> DELETE FROM mysql_query_rules;
Query OK, 2 rows affected (0.00 sec)
Admin> SELECT rule_id,active,proxy_port,destination_hostgroup,apply FROM mysql_query_rules;
Empty set (0.00 sec)
6)插入路由規則
Admin> INSERT INTO mysql_query_rules (rule_id,active,proxy_port,destination_hostgroup,apply)
-> VALUES (1,1,6401,10,1), (2,1,6402,20,1);
Query OK, 2 rows affected (0.00 sec)
Admin> SELECT rule_id,active,proxy_port,destination_hostgroup,apply FROM mysql_query_rules;
+---------+--------+------------+-----------------------+-------+
| rule_id | active | proxy_port | destination_hostgroup | apply |
+---------+--------+------------+-----------------------+-------+
| 1 | 1 | 6401 | 10 | 1 |
| 2 | 1 | 6402 | 20 | 1 |
+---------+--------+------------+-----------------------+-------+
2 rows in set (0.00 sec)
7)将修改後的配置加載到RUNTIME層
Admin> LOAD MYSQL SERVERS TO RUNTIME;
Query OK, 0 rows affected (0.01 sec)
Admin> SELECT * FROM runtime_mysql_servers;
+--------------+--------------+------+-----------+--------------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+--------------+------+-----------+--------------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 2 | 188.188.0.70 | 3306 | 0 | OFFLINE_HARD | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 20 | 188.188.0.70 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 20 | 188.188.0.69 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 10 | 188.188.0.68 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
+--------------+--------------+------+-----------+--------------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
4 rows in set (0.00 sec)
Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
Admin> SELECT rule_id,active,proxy_port,destination_hostgroup,apply FROM runtime_mysql_query_rules;
+---------+--------+------------+-----------------------+-------+
| rule_id | active | proxy_port | destination_hostgroup | apply |
+---------+--------+------------+-----------------------+-------+
| 1 | 1 | 6401 | 10 | 1 |
| 2 | 1 | 6402 | 20 | 1 |
+---------+--------+------------+-----------------------+-------+
2 rows in set (0.00 sec)
8)将修改後的配置持久化到DISK層
Admin> SAVE MYSQL SERVERS TO DISK;
Query OK, 0 rows affected (0.02 sec)
Admin> SELECT * FROM disk.mysql_servers;
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 10 | 188.188.0.68 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 20 | 188.188.0.69 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 20 | 188.188.0.70 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
+--------------+--------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
3 rows in set (0.00 sec)
Admin> SAVE MYSQL QUERY RULES TO DISK;
Query OK, 0 rows affected (0.01 sec)
Admin> SELECT rule_id,active,proxy_port,destination_hostgroup,apply FROM disk.mysql_query_rules;
+---------+--------+------------+-----------------------+-------+
| rule_id | active | proxy_port | destination_hostgroup | apply |
+---------+--------+------------+-----------------------+-------+
| 1 | 1 | 6401 | 10 | 1 |
| 2 | 1 | 6402 | 20 | 1 |
+---------+--------+------------+-----------------------+-------+
2 rows in set (0.00 sec)
到此配置完成!
現在,所有到 6401 端口的查詢都将發送到hostgroup 10中的mysql伺服器,而所有到 6402 端口的查詢都将發送到hostgroup 20中的一個mysql伺服器。
3、驗證結果
1)檢視目前規則命中資訊
Admin> SELECT hits, mysql_query_rules.rule_id, match_digest, match_pattern, replace_pattern, cache_ttl, apply
-> FROM mysql_query_rules
-> NATURAL JOIN stats.stats_mysql_query_rules
-> ORDER BY mysql_query_rules.rule_id;
+------+---------+--------------+---------------+-----------------+-----------+-------+
| hits | rule_id | match_digest | match_pattern | replace_pattern | cache_ttl | apply |
+------+---------+--------------+---------------+-----------------+-----------+-------+
| 0 | 1 | NULL | NULL | NULL | NULL | 1 |
| 0 | 2 | NULL | NULL | NULL | NULL | 1 |
+------+---------+--------------+---------------+-----------------+-----------+-------+
2 rows in set (0.01 sec)
2)在後端可寫MySQL上建立測試表
mysql> show create table t1 \G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
3)通過讀寫賬戶 6401 連入 ProxySQL 執行寫入操作:
[root@localhost ~]# mysql -utest_rw -p -h 188.188.0.71 -P 6401
mysql> use test;
mysql> insert into t1 values(1,'a');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 values(2,'b');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)
4)檢視ProxySQL中的資訊
檢視Proxy SQL中程序資訊
Admin> show full processlist;
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| ThreadID | SessionID | user | db | cli_host | cli_port | hostgroup | l_srv_host | l_srv_port | srv_host | srv_port | command | time_ms | info | status_flags |
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| 2 | 134 | test_rw | test | 188.188.0.70 | 37072 | 10 | | | | | Sleep | 10010 | | |
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
1 row in set (0.00 sec)
可以看到,我們在188.188.0.70發起連接配接,操作的是寫組 hostgroup 10,庫為test;和我們預期的一樣。
檢視規則命中資訊
Admin> SELECT hits, mysql_query_rules.rule_id, match_digest, match_pattern, replace_pattern, cache_ttl, apply
-> FROM mysql_query_rules NATURAL JOIN stats.stats_mysql_query_rules
-> ORDER BY mysql_query_rules.rule_id;
+------+---------+--------------+---------------+-----------------+-----------+-------+
| hits | rule_id | match_digest | match_pattern | replace_pattern | cache_ttl | apply |
+------+---------+--------------+---------------+-----------------+-----------+-------+
| 6 | 1 | NULL | NULL | NULL | NULL | 1 |
| 0 | 2 | NULL | NULL | NULL | NULL | 1 |
+------+---------+--------------+---------------+-----------------+-----------+-------+
2 rows in set (0.00 sec)
規則1有了命中資訊。
備注:隻要是向 6401 發出的指令,如show、use等都會使hits增加。
5)通過讀寫賬戶 6402 連入 ProxySQL 執行讀取操作:
[root@localhost ~]# mysql -utest_rw -p -h 188.188.0.71 -P 6402
mysql> SELECT * FROM test.t1 ;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)
6)檢視ProxySQL中的資訊
Admin> show full processlist;
+----------+-----------+---------+--------------------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| ThreadID | SessionID | user | db | cli_host | cli_port | hostgroup | l_srv_host | l_srv_port | srv_host | srv_port | command | time_ms | info | status_flags |
+----------+-----------+---------+--------------------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| 0 | 135 | test_rw | information_schema | 188.188.0.70 | 47732 | 20 | | | | | Sleep | 18018 | | |
+----------+-----------+---------+--------------------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
1 row in set (0.00 sec)
可以看到,我們在188.188.0.70發起連接配接,操作的是寫組 hostgroup 20,庫為test;和我們預期的一樣。
Admin> SELECT hits, mysql_query_rules.rule_id, match_digest, match_pattern, replace_pattern, cache_ttl, apply
-> FROM mysql_query_rules NATURAL JOIN stats.stats_mysql_query_rules
-> ORDER BY mysql_query_rules.rule_id;
+------+---------+--------------+---------------+-----------------+-----------+-------+
| hits | rule_id | match_digest | match_pattern | replace_pattern | cache_ttl | apply |
+------+---------+--------------+---------------+-----------------+-----------+-------+
| 6 | 1 | NULL | NULL | NULL | NULL | 1 |
| 2 | 2 | NULL | NULL | NULL | NULL | 1 |
+------+---------+--------------+---------------+-----------------+-----------+-------+
2 rows in set (0.00 sec)
規則2也有了命中資訊。
說明:如果先前連入過,則會受到連接配接池的影響,在連入時不會産生命中數增加。
到此,不同端口的讀寫分離配置完畢!!
二、基于傳入端口的讀寫分離限制[Limitation of read/write split based on incoming port]
在前面提到過人們經常會問如何配置ProxySQL以使用基于傳入端口的路由。雖然這有時是一種有效的方法,但在我看來它有一個很大的缺點:
使用資料庫的應用程式需要具有内置的讀寫分離功能,以便區分讀和寫。但通常情況并非如此。
通常應用程式隻使用一個連接配接端點,當然它是MySQL主節點。如果使用ProxySQL,則可以在單個端口中接受所有的流量,并可以根據查詢類型分析流量以執行讀/寫拆分。這非常友善,因為它不需要任何應用程式更改。
盡管如此,其主要優勢不是能夠在不更改應用程式的情況下路由流量。主要的優點是DBA現在有了控制發送到資料庫流量的工具。DBA是一個在午夜,由于資料庫伺服器過載,而被叫醒的群體;而且,因為當時周圍沒有開發人員,想在應用程式中進行更改是不可能的;但他們現在有了控制流量的選項。
三、(基于1個端口)使用正規表達式的實作基本讀寫分離[basic read/write split using regex]
在這一段中,将展示一個如何使用正規表達式來實作讀寫分離的示例。
-- >>>>>>>>>>>>>>>>>>配置方式一(不建議):
!!!!請不要在生産中使用下面這個示例配置!!!!
Admin> SHOW VARIABLES LIKE 'mysql_interfaces';
+------------------+---------------------------+
| Variable_name | Value |
+------------------+---------------------------+
| mysql-interfaces | 0.0.0.0:6401;0.0.0.0:6402 |
+------------------+---------------------------+
1 row in set (0.00 sec)
改用一個端口對外提供接入。
Admin> SET mysql-interfaces ='0.0.0.0:6033';
Query OK, 1 row affected (0.00 sec)
将修改持久化
Admin> SAVE MYSQL VARIABLES TO DISK;
Query OK, 110 rows affected (0.02 sec)
重新開機ProxySQL服務,使配置生效
Admin> PROXYSQL RESTART;
檢視修改結果
Admin> SHOW VARIABLES LIKE 'mysql_interfaces';
+------------------+--------------+
| Variable_name | Value |
+------------------+--------------+
| mysql-interfaces | 0.0.0.0:6033 |
+------------------+--------------+
1 row in set (0.00 sec)
2、實驗前删除以前建立的查詢規則:
Admin> DELETE FROM mysql_query_rules;
Query OK, 6 rows affected (0.00 sec)
Admin> SELECT * FROM mysql_query_rules ;
Empty set (0.00 sec)
3、建立讀/寫的基本規則:
1)修改目标使用者的預設組
Admin> SELECT username,active,default_hostgroup FROM mysql_users WHERE username='test_rw';
+----------+--------+-------------------+
| username | active | default_hostgroup |
+----------+--------+-------------------+
| test_rw | 1 | 1 |
+----------+--------+-------------------+
1 row in set (0.00 sec)
# by default, all goes to HG10
Admin> UPDATE mysql_users SET default_hostgroup=10 WHERE username='test_rw';
Query OK, 1 row affected (0.00 sec)
Admin> SELECT username,active,default_hostgroup FROM mysql_users WHERE username='test_rw';
+----------+--------+-------------------+
| username | active | default_hostgroup |
+----------+--------+-------------------+
| test_rw | 1 | 10 |
+----------+--------+-------------------+
1 row in set (0.00 sec)
2)加載配置到RUNTIME層,使其生效
Admin> LOAD MYSQL USERS TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
Admin> SELECT * FROM runtime_mysql_users WHERE username='test_rw';
+----------+-------------------------------------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+---------+
| username | password | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections | comment |
+----------+-------------------------------------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+---------+
| test_rw | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 | 1 | 0 | 10 | | 0 | 1 | 0 | 1 | 0 | 10000 | |
| test_rw | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 | 1 | 0 | 10 | | 0 | 1 | 0 | 0 | 1 | 10000 | |
+----------+-------------------------------------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+---------+
2 rows in set (0.00 sec)
3)持久化配置到DISK層
Admin> SAVE MYSQL USERS TO DISK;
Query OK, 0 rows affected (0.00 sec)
Admin> SELECT * FROM disk.mysql_users WHERE username='test_rw';
+----------+-------------------------------------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+---------+
| username | password | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections | comment |
+----------+-------------------------------------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+---------+
| test_rw | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 | 1 | 0 | 10 | | 0 | 1 | 0 | 1 | 1 | 10000 | |
+----------+-------------------------------------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+---------+
1 row in set (0.00 sec)
4)插入讀寫規則
Admin> INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply)
-> VALUES (1,1,'^SELECT.*FOR UPDATE$',10,1),(2,1,'^SELECT',20,1);
Query OK, 2 rows affected (0.00 sec)
Admin> SELECT rule_id,active,match_digest,destination_hostgroup,apply FROM mysql_query_rules ;
+---------+--------+----------------------+-----------------------+-------+
| rule_id | active | match_digest | destination_hostgroup | apply |
+---------+--------+----------------------+-----------------------+-------+
| 1 | 1 | ^SELECT.*FOR UPDATE$ | 10 | 1 |
| 2 | 1 | ^SELECT | 20 | 1 |
+---------+--------+----------------------+-----------------------+-------+
2 rows in set (0.00 sec)
5)加載配置到RUNTIME層,使其生效
Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
Admin> SELECT rule_id,active,match_digest,destination_hostgroup,apply FROM runtime_mysql_query_rules ;
+---------+--------+----------------------+-----------------------+-------+
| rule_id | active | match_digest | destination_hostgroup | apply |
+---------+--------+----------------------+-----------------------+-------+
| 1 | 1 | ^SELECT.*FOR UPDATE$ | 10 | 1 |
| 2 | 1 | ^SELECT | 20 | 1 |
+---------+--------+----------------------+-----------------------+-------+
2 rows in set (0.00 sec)
6)持久化配置到DISK層
Admin> SAVE MYSQL QUERY RULES TO DISK;
Query OK, 0 rows affected (0.01 sec)
Admin> SELECT rule_id,active,match_digest,destination_hostgroup,apply FROM disk.mysql_query_rules ;
+---------+--------+----------------------+-----------------------+-------+
| rule_id | active | match_digest | destination_hostgroup | apply |
+---------+--------+----------------------+-----------------------+-------+
| 1 | 1 | ^SELECT.*FOR UPDATE$ | 10 | 1 |
| 2 | 1 | ^SELECT | 20 | 1 |
+---------+--------+----------------------+-----------------------+-------+
2 rows in set (0.00 sec)
現在路由将會按如下工作:
1)所有 SELECT FOR UPDATE 查詢都發往 HG10 ;
2)其他所有的 SELECT 查詢都發往 HG20 ;
3)除以上2種查詢以外,其他的查詢都将發往 HG10 (預設組);
4、結果驗證
Admin> SELECT hits, mysql_query_rules.rule_id, match_digest, match_pattern, replace_pattern, cache_ttl, apply
-> FROM mysql_query_rules
-> NATURAL JOIN stats.stats_mysql_query_rules
-> ORDER BY mysql_query_rules.rule_id;
+------+---------+----------------------+---------------+-----------------+-----------+-------+
| hits | rule_id | match_digest | match_pattern | replace_pattern | cache_ttl | apply |
+------+---------+----------------------+---------------+-----------------+-----------+-------+
| 0 | 1 | ^SELECT.*FOR UPDATE$ | NULL | NULL | NULL | 1 |
| 0 | 2 | ^SELECT | NULL | NULL | NULL | 1 |
+------+---------+----------------------+---------------+-----------------+-----------+-------+
2 rows in set (0.00 sec)
規則1、2都是0。
2)通過 6033 端口連入 ProxySQL 執行操作:
[root@localhost ~]# mysql -utest_rw -p -h 188.188.0.71 -P 6033
mysql> use test; ==>觸發一次規則2 hits
mysql> select * from t1; ==>觸發一次規則2 hits
Empty set (0.00 sec)
mysql> insert into t1 values(1,'a');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 values(2,'b');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1; ==>觸發一次規則2 hits
3)操作後,檢視ProxySQL中的資訊
檢視連接配接資訊
Admin> SHOW FULL PROCESSLIST;
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| ThreadID | SessionID | user | db | cli_host | cli_port | hostgroup | l_srv_host | l_srv_port | srv_host | srv_port | command | time_ms | info | status_flags |
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| 2 | 3 | test_rw | test | 188.188.0.70 | 55705 | 20 | | | | | Sleep | 22022 | | |
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
1 row in set (0.00 sec)
最後的select請求流入了H20,與預設一緻。
Admin> SELECT hits, mysql_query_rules.rule_id, match_digest, match_pattern, replace_pattern, cache_ttl, apply
-> FROM mysql_query_rules
-> NATURAL JOIN stats.stats_mysql_query_rules
-> ORDER BY mysql_query_rules.rule_id;
+------+---------+----------------------+---------------+-----------------+-----------+-------+
| hits | rule_id | match_digest | match_pattern | replace_pattern | cache_ttl | apply |
+------+---------+----------------------+---------------+-----------------+-----------+-------+
| 0 | 1 | ^SELECT.*FOR UPDATE$ | NULL | NULL | NULL | 1 |
| 3 | 2 | ^SELECT | NULL | NULL | NULL | 1 |
+------+---------+----------------------+---------------+-----------------+-----------+-------+
2 rows in set (0.00 sec)
三次觸發,使得hits增加了3。
4)測試FOR UPDATE
mysql> select * from t1 for update ;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)
5)操作後,檢視ProxySQL中的資訊
Admin> SHOW FULL PROCESSLIST;
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| ThreadID | SessionID | user | db | cli_host | cli_port | hostgroup | l_srv_host | l_srv_port | srv_host | srv_port | command | time_ms | info | status_flags |
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
| 2 | 3 | test_rw | test | 188.188.0.70 | 55705 | 10 | | | | | Sleep | 12012 | | |
+----------+-----------+---------+------+--------------+----------+-----------+------------+------------+----------+----------+---------+---------+------+--------------+
1 row in set (0.00 sec)
FOR UPDATE請求流入了HG10,與預設一緻。
Admin> SELECT hits, mysql_query_rules.rule_id, match_digest, match_pattern, replace_pattern, cache_ttl, apply
-> FROM mysql_query_rules
-> NATURAL JOIN stats.stats_mysql_query_rules
-> ORDER BY mysql_query_rules.rule_id;
+------+---------+----------------------+---------------+-----------------+-----------+-------+
| hits | rule_id | match_digest | match_pattern | replace_pattern | cache_ttl | apply |
+------+---------+----------------------+---------------+-----------------+-----------+-------+
| 1 | 1 | ^SELECT.*FOR UPDATE$ | NULL | NULL | NULL | 1 |
| 3 | 2 | ^SELECT | NULL | NULL | NULL | 1 |
+------+---------+----------------------+---------------+-----------------+-----------+-------+
2 rows in set (0.00 sec)
一次請求,使得hits增加了1。
請注意,我認為上述方法不是讀取/寫入拆分的好方法。
我經常使用這個例子來描述如何配置規則,但它卻經常被錯誤地解釋為配置讀/寫拆分的方法。
!!!!請不要在生産中使用上述示例配置!!!!
-- >>>>>>>>>>>>>>>>>>配置方式二(推薦):
在下面這段中,将展示一種更好的方法。
現在,讓我們删除所有規則:
Admin> DELETE FROM mysql_query_rules;
Query OK, 2 rows affected (0.00 sec)
Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
Admin> SAVE MYSQL QUERY RULES TO DISK;
Query OK, 0 rows affected (0.02 sec)
Admin> SELECT * FROM runtime_mysql_query_rules;
Empty set (0.00 sec)
Admin> SELECT * FROM disk.mysql_query_rules;
Empty set (0.00 sec)
1、使用regex和digest進行讀/寫拆分[read/write split using regex and digest]
有效設定讀/寫拆分的配置過程如下:
1)配置 ProxySQL 隻将所有流量發送到一個 MySQL 節點——master(寫和讀);
2)在 stats_mysql_query_digest 中查找執行成本較高的 SELECT 語句;
3)确定哪些成本較高的 SELECT 語句應該被移動到讀節點上執行;
4)配置 mysql_query_rules 将需要移動的 SELECT 語句發送到讀節點;
是以,這個想法非常簡單:隻發送你想發送給 Slave/Reader 的内容,而不隻是任何SELECT語句。
2、使用stats_mysql_query_digest查找成本較高的查詢[Find expensive queries using stats_mysql_query_digest]
下面是一個示例清單,說明如何識别可以發送給Readers的潛在查詢。由于 ProxySQL 在表中記錄了所有度量資訊,是以可以建立複雜的查詢來收集資訊。
這些結果是基于一個運作了幾個月的非常繁忙的 ProxySQL 執行個體;到目前為止,該執行個體已經處理了大約千億個查詢。
1)根據總執行時間查找最耗時的前5個查詢:
Admin> SELECT digest,SUBSTR(digest_text,0,25),count_star,sum_time FROM stats.stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%' ORDER BY sum_time DESC LIMIT 5;
+--------------------+--------------------------+------------+---------------+
| digest | SUBSTR(digest_text,0,25) | count_star | sum_time |
+--------------------+--------------------------+------------+---------------+
| 0x037C3E6D996DAFE2 | SELECT a.ip_id as ip_id, | 2030026798 | 1479082636017 |
| 0xB081A85245DEA5B7 | SELECT a.ip_id as ip_id, | 2025902778 | 1206116187539 |
| 0x38BE36BDFFDBE638 | SELECT instance.name as | 59343662 | 1096236803754 |
| 0xB4233552504E43B8 | SELECT ir.type as type, | 1362897166 | 488971769571 |
| 0x4A131A16DCFFD6C6 | SELECT i.id as id, i.sta | 934402293 | 475253770301 |
+--------------------+--------------------------+------------+---------------+
5 rows in set (0.01 sec)
2)根據計數查找前5個查詢:
Admin> SELECT digest,SUBSTR(digest_text,0,25),count_star,sum_time FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%' ORDER BY count_star DESC LIMIT 5;
+--------------------+--------------------------+------------+---------------+
| digest | SUBSTR(digest_text,0,25) | count_star | sum_time |
+--------------------+--------------------------+------------+---------------+
| 0x037C3E6D996DAFE2 | SELECT a.ip_id as ip_id, | 2030040688 | 1479092529369 |
| 0xB081A85245DEA5B7 | SELECT a.ip_id as ip_id, | 2025916528 | 1206123010791 |
| 0x22E0A5C585C53EAD | SELECT id as instanceid, | 1551361254 | 426419508609 |
| 0x3DB4B9FA4B2CB36F | SELECT i.id as instancei | 1465274289 | 415565419867 |
| 0xB4233552504E43B8 | SELECT ir.type as type, | 1362906755 | 488974931108 |
+--------------------+--------------------------+------------+---------------+
5 rows in set (0.00 sec)
嗯...,這些查詢中的一些可以緩存嗎?也許proxysql查詢緩存可以幫助您!
3)根據最長執行時間查找前5個查詢:
Admin> SELECT digest,SUBSTR(digest_text,0,25),count_star,sum_time,sum_time/count_star avg_time, min_time, max_time FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%' ORDER BY max_time DESC LIMIT 5;
+--------------------+--------------------------+------------+--------------+----------+----------+-----------+
| digest | SUBSTR(digest_text,0,25) | count_star | sum_time | avg_time | min_time | max_time |
+--------------------+--------------------------+------------+--------------+----------+----------+-----------+
| 0x36CE5295726DB5B4 | SELECT COUNT(*) as total | 146390 | 185951894994 | 1270249 | 445 | 237344243 |
| 0xDA8C56B5644C0822 | SELECT COUNT(*) as total | 44130 | 24842335265 | 562935 | 494 | 231395575 |
| 0x8C1B0405E1AAB9DB | SELECT COUNT(*) as total | 1194 | 1356742749 | 1136300 | 624 | 216677507 |
| 0x6C03197B4A2C34BE | Select *, DateDiff(Date_ | 4796 | 748804483 | 156131 | 607 | 197881845 |
| 0x1DEFCE9DEF3BDF87 | SELECT DISTINCT i.extid | 592196 | 40209254260 | 67898 | 416 | 118055372 |
+--------------------+--------------------------+------------+--------------+----------+----------+-----------+
5 rows in set (0.01 sec)
具體結果表明,有些查詢的最大執行時間很高,而最小執行時間很小,平均速度也相當慢。
例如,Digest為0x36CE5295726DB5B4的查詢,平均執行時間為1.27秒,最小執行時間為0.4ms,最大執行時間為237.34秒。也許值得調查一下為什麼執行時間不均勻。
4)查找按總執行時間排序的前5個查詢,最小執行時間至少為1毫秒:
Admin> SELECT digest,SUBSTR(digest_text,0,20),count_star,sum_time,sum_time/count_star avg_time, min_time, max_time FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%' AND min_time > 1000 ORDER BY sum_time DESC LIMIT 5;
+--------------------+--------------------------+------------+-------------+----------+----------+----------+
| digest | SUBSTR(digest_text,0,20) | count_star | sum_time | avg_time | min_time | max_time |
+--------------------+--------------------------+------------+-------------+----------+----------+----------+
| 0x9EED412C6E63E477 | SELECT a.id as acco | 961733 | 24115349801 | 25074 | 10994 | 7046628 |
| 0x8DDD43A9EA37750D | Select ( Coalesce(( | 107069 | 3156179256 | 29477 | 1069 | 24600674 |
| 0x9EED412C6E63E477 | SELECT a.id as acco | 91996 | 1883354396 | 20472 | 10095 | 497877 |
| 0x08B23A268C35C08E | SELECT id as reward | 49401 | 244088592 | 4940 | 1237 | 1483791 |
| 0x437C846F935344F8 | SELECT Distinct i.e | 164 | 163873101 | 999226 | 1383 | 7905811 |
+--------------------+--------------------------+------------+-------------+----------+----------+----------+
5 rows in set (0.01 sec)
5)查找按總執行時間排序的前5個查詢,平均執行時間至少為1秒。同時顯示總執行時間的百分比:
Admin> SELECT digest,SUBSTR(digest_text,0,25),count_star,sum_time,sum_time/count_star avg_time, ROUND(sum_time*100.00/(SELECT SUM(sum_time) FROM stats_mysql_query_digest),3) pct FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%' AND sum_time/count_star > 1000000 ORDER BY sum_time DESC LIMIT 5;
+--------------------+--------------------------+------------+--------------+----------+-------+
| digest | SUBSTR(digest_text,0,25) | count_star | sum_time | avg_time | pct |
+--------------------+--------------------------+------------+--------------+----------+-------+
| 0x36CE5295726DB5B4 | SELECT COUNT(*) as total | 146390 | 185951894994 | 1270249 | 2.11 |
| 0xD38895B4F4D2A4B3 | SELECT instance.name as | 9783 | 12409642528 | 1268490 | 0.141 |
| 0x8C1B0405E1AAB9DB | SELECT COUNT(*) as total | 1194 | 1356742749 | 1136300 | 0.015 |
+--------------------+--------------------------+------------+--------------+----------+-------+
3 rows in set (0.00 sec)
所有這些查詢都需要在master上執行?如果一個查詢的平均執行時間超過1秒,則答案可能是"No"。
對于某些應用程式,即使運作平均執行時間為15毫秒的查詢也可能需要轉到slave。
6)編寫查詢規則
在與應用程式開發人員确認之後,例如,我們可以将digset為 0x38BE36BDFFDBE638 的查詢轉移到Slave上執行:
INSERT INTO mysql_query_rules (rule_id,active,digest,destination_hostgroup,apply)
VALUES (1,1,'0x38BE36BDFFDBE638',20,1);
7)同樣,假設要将所有以SELECT COUNT(*)開頭的查詢都可以轉到Slaves
檢視該類語句的執行情況:
SELECT digest,digest_text,count_star,sum_time,sum_time/count_star avg_time, ROUND(sum_time*100.00/(SELECT SUM(sum_time) FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%'),3) pct FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT COUNT%' ORDER BY sum_time DESC;
INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply)
VALUES (1,1,'^SELECT COUNT\(\*\)',20,1);
8)最後,将每個規則加載到RUNTIME層和持久化到DISK層:
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK; # if you want this change to be permanent