- 1. proxysql读写分离意义
- 2. 相关表
- 3. 使用不同的端口的读写分离策略
- 3.1. 使用admin登陆交互界面,并修改配置
- 3.2. 增加正对Port的访问规则
- 3.3. 尝试访问
- 4. 使用账号的读写分离
- 4.1. 增加用户的访问规则
- 5. 使用区别sql的读写分离
- 5.1. 关键表:
- 5.2. 查询相关日志记录的例子
- 5.3. 增加访问规则
proxysql 读写分离配置
1. proxysql读写分离意义
使用中间间进行读写分离有点像是一个妥协产物,因为前期架构规划未到位,导致数据库压力日益剧增,需要进行数据库的压力负载转移,进行读写分离。
proxysql作为中间间,可以进行读写分离的操作,在不改动代码的前提下进行sql的分发实现读写分离。
优点:
- 可以降低数据库的负载
- 对应用侵入性较小
缺点
- 增加了出故障的概率
- 增加了维护的难度
2. 相关表
关键表: mysql_query_rules
字段解释
show create table mysql_query_rules;
- rule_id :自增主键,用于唯一标识每个查询规则。
- active :整型字段,用于控制规则的启用/禁用状态。0 表示禁用,1 表示启用。
- username : 字符串字段,用于指定要应用规则的用户名。可以是具体的用户名,也可以是通配符或正则表达式。
- schemaname : 字符串字段,用于指定要应用规则的数据库模式名。可以是具体的模式名,也可以是通配符或正则表达式。
- flagIN : 整型字段,用于设置输入标志。该字段用于确定如何处理查询的输入部分。默认为 0。
- client_addr : 字符串字段,用于指定要应用规则的客户端地址。可以是具体的 IP 地址,也可以是 IP 地址范围或通配符。
- proxy_addr : 字符串字段,用于指定代理服务器的地址。
- proxy_port : 整型字段,用于指定代理服务器的端口。该字段应在 0 到 65535 之间。
- digest : 字符串字段,用于存储查询的摘要信息。
- match_digest : 字符串字段,用于存储与查询摘要匹配的模式。
- match_pattern : 字符串字段,用于存储与查询文本匹配的模式。
- negate_match_pattern : 整型字段,用于控制是否对匹配模式进行取反操作。0 表示不进行取反,1 表示进行取反。
- re_modifiers : 字符串字段,用于指定正则表达式的修改器。默认值为CASELESS,表示忽略大小写。
- flagOUT : 整型字段,用于设置输出标志。该字段用于确定如何处理查询的输出部分。
- replace_pattern : 字符串字段,用于指定要替换查询结果的模式。
- destination_hostgroup : 整型字段,用于指定要将查询转发到的目标主机组。默认为 NULL。
- cache_ttl : 整型字段,用于设置查询结果的缓存时间。该字段应大于 0。
- cache_empty_result : 整型字段,用于控制是否缓存空结果。默认为 NULL。
- cache_timeout : 整型字段,用于设置查询结果的缓存超时时间。该字段应大于等于 0。
- reconnect : 整型字段,用于控制是否重新连接到目标主机组。0 表示不重新连接,1 表示重新连接。
- timeout : 无符号整型字段,用于设置查询的超时时间。
- retries : 整型字段,用于设置查询的重试次数。该字段应在 0 到 1000 之间。
- delay : 无符号整型字段,用于设置查询的延迟时间。
- next_query_flagIN : 无符号整型字段,用于设置下一个查询的输入标志。
- mirror_flagOUT : 无符号整型字段,用于设置镜像查询的输出标志。
- mirror_hostgroup : 无符号整型字段,用于指定镜像查询的目标主机组。
- error_msg : 字符串字段,用于存储错误消息。
- OK_msg : 字符串字段,用于存储成功消息。
- sticky_conn : 整型字段,用于控制是否启用粘性连接。0 表示不启用,1 表示启用。
- multiplex : 整型字段,用于控制是否启用复用连接。0 表示不启用,1 表示启用,2 表示仅在第一个连接上启用复用。
- gtid_from_hostgroup : 无符号整型字段,用于指定从中获取全局事务标识符(gtid)的主机组。
- log : 整型字段,用于控制是否记录查询日志。0 表示不记录,1 表示记录。
- apply : 整型字段,用于控制是否应用规则。该字段应设置为 1。
其中 rule_id 因为是自增的,所以可以不用指定,
active 的层级 > apply 层级,只有当 active 字段为 1 且 apply 字段为 1 时,规则才会被应用于匹配的查询
active 和 apply 因为涉及到策略是否开启,必须指定
注意: 读写分离策略,从mysql_query_rules第一条记录开始匹配,匹配成功后返回对应的策略
3. 策略1: 使用不同的端口的读写分离策略
方案: 将不同的proxy_port绑定到不同的destination_hostgroup组上面
比如
proxy_port 6033 --> 绑定到destination_hostgroup为读写的组上面
proxy_port 6043 --> 绑定到destination_hostgroup为只读的组上面
3.1. 使用admin登陆交互界面,并修改配置
需要在admin界面修改 mysql-interfaces = “0.0.0.0:6033;0.0.0.0:6043”;
监听两个端口,一个端口作为读写主端口,一个端口作为只读端口
mysql-interfaces 修改后,需要重启proxysql生效
set mysql-interfaces = "0.0.0.0:6033;0.0.0.0:6043";
SAVE MYSQL VARIABLES TO DISK;
PROXYSQL RESTART;
3.2. 增加正对Port的访问规则
insert into mysql_query_rules (rule_id,active,proxy_port,destination_hostgroup,apply) VALUES (1,1,6033,1,1), (2,1,6043,2,1);
3.3. 尝试访问
# 尝试访问6033端口
[root@test ~]# mysql -u monitor -p -P6033 -h 127.0.0.1 -e "select @@server_id"
Logging to file '/data/mysql/mysql3306/logs/query.log'
Enter password:
+-------------+
| @@server_id |
+-------------+
| 1721602 |
+-------------+
# 访问的是第一个节点172.16.0.1
# 尝试访问6043端口
[root@test ~]# mysql -u monitor -p -P6043 -h 127.0.0.1 -e "select @@server_id"
Logging to file '/data/mysql/mysql3306/logs/query.log'
Enter password:
+-------------+
| @@server_id |
+-------------+
| 1721601 |
+-------------+
# 访问的是第备库节点172.16.0.2
4. 策略1: 使用账号的读写分离
方案: 将不同的username绑定到不同的destination_hostgroup
比如
user1 --> 绑定到destination_hostgroup为读写的组上面
user2 --> 绑定到destination_hostgroup为只读的组上面
4.1. 增加用户的访问规则
INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('monitor','password',1);
delete from mysql_query_rules;
insert into mysql_query_rules (active, apply, username, destination_hostgroup) VALUES (1,1,'monitor',2);
insert into mysql_query_rules (active,proxy_port,destination_hostgroup,apply) VALUES (1,6035,2,1);
SAVE MYSQL USERS TO DISK;
LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS FROM RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
LOAD MYSQL QUERY RULES TO RUNTIME;
5. 策略1: 使用区别sql的读写分离
方案: 将不同的 digest/match_digest/match_pattern 绑定到不同的 destination_hostgroup组 上面
比如
sql1 (select xxxx from tb1) --> 绑定到destination_hostgroup为读写的组上面
sql2 (select count(*) from tb1) --> 绑定到destination_hostgroup为只读的组上面
5.1. 关键表:
stats_mysql_query_digest
mysql> select hostgroup,schemaname,username,digest,digest_text,count_star,from_unixtime(first_seen),from_unixtime(last_seen),sum_time/1000,min_time/1000,max_time/1000,sum_rows_affected,sum_rows_sent from stats_mysql_query_digest limit 3;
hostgroup | schemaname | username | digest | digest_text | count_star | from_unixtime(first_seen) | from_unixtime(last_seen) | sum_time/1000 | min_time/1000 | max_time/1000 | sum_rows_affected | sum_rows_sent |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2 | information_schema | monitor | 0xDA65260DF35B8D13 | select @@server_id | 9 | 2024-04-10 07:11:17 | 2024-04-10 07:11:29 | 17 | 0 | 2 | 0 | 9 |
1 | information_schema | monitor | 0x99531AEFF718C501 | show tables | 1 | 2024-04-10 06:40:20 | 2024-04-10 06:40:20 | 0 | 0 | 0 | 0 | 61 |
1 | information_schema | monitor | 0x02033E45904D3DF0 | show databases | 1 | 2024-04-10 06:40:15 | 2024-04-10 06:40:15 | 0 | 0 | 0 | 0 | 8 |
CREATE TABLE stats_mysql_query_digest (
hostgroup INT,
schemaname VARCHAR NOT NULL,
username VARCHAR NOT NULL,
client_address 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,
sum_rows_affected INTEGER NOT NULL,
sum_rows_sent INTEGER NOT NULL,
PRIMARY KEY(hostgroup, schemaname, username, client_address, digest))
字段解释
- hostgroup : 记录的hostgroup组
- schemaname : 记录的schema
- username : 访问的username
- client_address : 访问客户端
- digest : sql摘要id
- digest_text : sql摘要的语句
- count_star : 执行次数
- first_seen : 首次执行时间,最后执行时间,timestamp
- last_seen : 最后执行时间,timestamp
- sum_time : 总共执行时间,单位毫秒,平均时间可以用 sum_time/count_star 计算出来
- min_time : 最小执行时间,单位毫秒
- max_time : 最大执行时间,单位毫秒
- sum_rows_affected : 影响行数
- sum_rows_sent : 发送行数
5.2. 查询相关日志记录的例子
目的:先搂出符合需求的语句出来,再进行合理的读写分离策略制定
- 如何根据总执行时间查找前 5 个查询:
SELECT digest,SUBSTR(digest_text,0,25),count_star,sum_time FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%' ORDER BY sum_time DESC LIMIT 2;
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 |
- 如何根据计数查找前 5 个查询:
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 2;
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 |
- 如何根据最大执行时间查找前 5 个查询:
SELECT digest,SUBSTR(digest_text,0,40),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 2;
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 |
- 如何查找按总执行时间排序的前 5 个查询,并且最小执行时间至少为 1 毫秒:
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 2;
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 |
- 如何找到按总执行时间排序的前 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 2;
|--------------------|--------------------------|------------|--------------|----------|-------|
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 |
- 如何查找按总执行时间排序的前 5 个查询,平均执行时间至少为 15 毫秒,并显示占总执行时间的百分比:
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 WHERE digest_text LIKE 'SELECT%'),3) pct FROM stats_mysql_query_digest WHERE digest_text LIKE 'SELECT%' AND sum_time/count_star > 15000 ORDER BY sum_time DESC LIMIT 2;
digest | SUBSTR(digest_text,0,25) | count_star | sum_time | avg_time | pct |
---|---|---|---|---|---|
0x38BE36BDFFDBE638 | SELECT instance.name as | 59360371 | 1096562204931 | 18472 | 13.006 |
0x36CE5295726DB5B4 | SELECT COUNT(*) as total | 146390 | 185951894994 | 1270249 | 2.205 |
5.3. 增加访问规则
hostgroup | schemaname | username | client_address | digest | digest_text | count_star | first_seen | last_seen | sum_time | min_time | max_time | sum_rows_affected | sum_rows_sent |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2 | information_schema | monitor | 0xDA65260DF35B8D13 | select @@server_id | 9 | 1712733077 | 1712733089 | 17463 | 944 | 2656 | 0 | 9 | |
1 | information_schema | monitor | 0xCA0825C1C5C3C490 | select sleep(?) | 3 | 2024-04-10 06:35:11 | 2024-04-10 07:06:24 | 15005 | 5000 | 5003 | 0 | 3 |
需求: 查询到两个sql,测试需要将这两个sql调整到备库查询
digest 为 0xDA65260DF35B8D13, digest_text为 select @@server_id
digest 为 0xCA0825C1C5C3C490, digest_text为 select sleep(?)
- 根据digest的id去做读写分离
delete from mysql_query_rules;
INSERT INTO mysql_query_rules (apply,active,digest,destination_hostgroup)
VALUES (1,1,'0xDA65260DF35B8D13',1);
INSERT INTO mysql_query_rules (apply,active,digest,destination_hostgroup)
VALUES (1,1,'0xCA0825C1C5C3C490',2);
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
- 根据match_digest的正则
delete from mysql_query_rules;
INSERT INTO mysql_query_rules (apply,active,username,match_digest,destination_hostgroup)
VALUES (1,1,'monitor','select @@server_id',2);
INSERT INTO mysql_query_rules (apply,active,username,match_digest,destination_hostgroup)
VALUES (1,1,'monitor','select sleep(?)',2);
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
以上情形都可以尝试使用
select @@server_id ;
-- or
select sleep(5);
该语句去查proxysql的暴露的端口,发现查询的均可以分离到destination_hostgroup为2的库也就是备库中
以上都是使用例子,如需生产使用,需根据实际业务,做更多开发测试才行