MySQL中间件之ProxySQL_读写分离/查询重写配置

1.闲扯几句
读写分离这是一个扯了好多年的话题,实现方式也也是层出不穷。笔者也曾经使用keepalive+lvs的方式给别人做过读写分离,效果还不错,也不是特别麻烦,用起来蛮好,就是应用有点不喜欢,需要配置读IP和写IP,应用感觉麻烦,不愿用。那么引入中间件,这些都不是事,暴露给应用的还是一个IP一个port,什么读写分离,交给中间件好了,想怎么路由SQL就怎么路由SQL,你开心就好。


2.配个读写分离
2.1准备工作
这里我带领大家配个简单的一主两从,搞个读写分离,配个查询缓存,玩下查询重写。

node1 (192.168.56.101:24801) , mysql master
node2 (192.168.56.101:24802) , mysql slave
node3 (192.168.56.101:24803) , mysql slave
app (192.168.56.101:6033) , app+proxysql


看起来还不错,但是mysql高可用这块还需读者自己去想办法,MHA,MMM,group replication,pxc都可以的哟。
mysql一主两从读者自行搭建。

root@localhost [(none)] 01:43:27>>> show slave hosts;
+-----------+------+-------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID |
+-----------+------+-------+-----------+--------------------------------------+
| 3 | | 24803 | 1 | 87626cdd-c918-11e6-b3a6-0800278bcede |
| 2 | | 24802 | 1 | 81850b66-c918-11e6-b22e-0800278bcede |
+-----------+------+-------+-----------+--------------------------------------+
2 rows in set (0.00 sec)

2.2配置
现在我们把一主两从的mysql数据库信息添加到proxysql中。我们将主库master也就是做写入的节点放到group 0中,salve节点做读放到group1。
登录proxysql

admin@127.0.0.1 [(none)] 01:48:32>>>INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (0,'192.168.56.101',24801);
Query OK, 1 row affected (0.00 sec) admin@127.0.0.1 [(none)] 01:48:45>>>INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (1,'192.168.56.101',24802);
Query OK, 1 row affected (0.01 sec) admin@127.0.0.1 [(none)] 01:48:45>>>INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (1,'192.168.56.101',24803);
Query OK, 1 row affected (0.00 sec) admin@127.0.0.1 [(none)] 01:48:46>>>select hostgroup_id,hostname,port,status from mysql_servers;
+--------------+----------------+-------+--------+
| hostgroup_id | hostname | port | status |
+--------------+----------------+-------+--------+
| 0 | 192.168.56.101 | 24801 | ONLINE |
| 1 | 192.168.56.101 | 24802 | ONLINE |
| 1 | 192.168.56.101 | 24803 | ONLINE |
+--------------+----------------+-------+--------+
3 rows in set (0.00 sec)

根据安装部署篇的提示,我们在数据库中需要配置好了监控账号(ProxySQL)和应用账号(sbuser)。以下SQL在master节点执行。

CREATE USER 'ProxySQL'@'%' IDENTIFIED BY 'ProxySQLPa55';
GRANT USAGE ON *.* TO 'ProxySQL'@'%';
CREATE USER 'sbuser'@'%' IDENTIFIED BY 'sbpass';
GRANT ALL ON * . * TO 'sbuser'@'%';
FLUSH PRIVILEGES;

确认账号OK:

root@localhost [(none)] 01:57:12>>>select host,user from mysql.user;
+-----------+-----------+
| host | user |
+-----------+-----------+
| % | proxysql |
| % | rpl_user |
| % | sbuser |
| localhost | mysql.sys |
| localhost | root |
+-----------+-----------+
5 rows in set (0.00 sec)

在proxysql中添加账号,

admin@127.0.0.1 [(none)] 01:59:49>>>INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('sbuser','sbpass',0);
Query OK, 1 row affected (0.00 sec) admin@127.0.0.1 [(none)] 01:59:50>>>UPDATE global_variables SET variable_value='proxysql' WHERE variable_name='mysql-monitor_username';
Query OK, 1 row affected (0.01 sec) admin@127.0.0.1 [(none)] 01:59:50>>>UPDATE global_variables SET variable_value='ProxySQLPa55' WHERE variable_name='mysql-monitor_password';
Query OK, 1 row affected (0.00 sec) admin@127.0.0.1 [(none)] 01:59:51>>>LOAD MYSQL SERVERS TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
/*将配置应用于proxysql运行环境*/ admin@127.0.0.1 [(none)] 02:00:45>>>SAVE MYSQL SERVERS TO DISK;
Query OK, 0 rows affected (0.05 sec)
/*将配置存储到sqlite数据库中*/

查看proxysql监控数据库的一些指标

admin@127.0.0.1 [(none)] 02:20:48>>>SELECT * FROM monitor.mysql_server_connect_log ORDER BY time_start DESC LIMIT 10;
+----------------+-------+------------+----------------------+---------------+
| hostname | port | time_start | connect_success_time | connect_error |
+----------------+-------+------------+----------------------+---------------+
| 192.168.56.101 | 24803 | 604984013 | 360 | NULL |
| 192.168.56.101 | 24802 | 604983410 | 383 | NULL |
| 192.168.56.101 | 24801 | 604982712 | 472 | NULL |
| 192.168.56.101 | 24803 | 602983284 | 301 | NULL |
| 192.168.56.101 | 24802 | 602982770 | 360 | NULL |
| 192.168.56.101 | 24801 | 602982025 | 576 | NULL |
| 192.168.56.101 | 24803 | 600982931 | 394 | NULL |
| 192.168.56.101 | 24802 | 600982168 | 1540 | NULL |
| 192.168.56.101 | 24801 | 600981405 | 507 | NULL |
| 192.168.56.101 | 24803 | 598982425 | 982 | NULL |
+----------------+-------+------------+----------------------+---------------+
10 rows in set (0.00 sec) admin@127.0.0.1 [(none)] 02:20:50>>>SELECT * FROM monitor.mysql_server_ping_log ORDER BY time_start DESC LIMIT 10;
+----------------+-------+------------+-------------------+------------+
| hostname | port | time_start | ping_success_time | ping_error |
+----------------+-------+------------+-------------------+------------+
| 192.168.56.101 | 24803 | 606998231 | 58 | NULL |
| 192.168.56.101 | 24802 | 606997865 | 75 | NULL |
| 192.168.56.101 | 24801 | 606996830 | 93 | NULL |
| 192.168.56.101 | 24803 | 604997051 | 39 | NULL |
| 192.168.56.101 | 24802 | 604996633 | 42 | NULL |
| 192.168.56.101 | 24801 | 604996278 | 54 | NULL |
| 192.168.56.101 | 24803 | 602997075 | 42 | NULL |
| 192.168.56.101 | 24802 | 602996658 | 85 | NULL |
| 192.168.56.101 | 24801 | 602996275 | 63 | NULL |
| 192.168.56.101 | 24803 | 600997230 | 47 | NULL |
+----------------+-------+------------+-------------------+------------+
10 rows in set (0.00 sec)

使用业务用户通过proxysql登录mysql。如果卡住了,可能是DNS解析引起。

[mysql@hpc01 ~]$ mysql -u sbuser -psbpass -h 127.0.0.1 -P6033 -e "select @@port"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------+
| @@port |
+--------+
| 24801 |
+--------+

好了,用户通过proxysql登录master,现在初始化数据库。

[mysql@hpc01 ~]$ sysbench --test=oltp --oltp-table-size=4000 --oltp-read-only=off --init-rng=on --num-threads=5 --max-requests=0 --oltp-dist-type=uniform --max-time=36 --mysql-user=sbuser  --mysql-password='sbpass' --db-driver=mysql --mysql-port=6033 --mysql-table-engine=innodb  --mysql-host=127.0.0.1  --mysql-db=sbtest --oltp-tables-count=5 --report-interval=1 prepare
sysbench 1.0: multi-threaded system evaluation benchmark Creating table 'sbtest1'...
Inserting 4000 records into 'sbtest1'
Creating secondary indexes on 'sbtest1'...
Creating table 'sbtest2'...
Inserting 4000 records into 'sbtest2'
Creating secondary indexes on 'sbtest2'...
Creating table 'sbtest3'...
Inserting 4000 records into 'sbtest3'
Creating secondary indexes on 'sbtest3'...
Creating table 'sbtest4'...
Inserting 4000 records into 'sbtest4'
Creating secondary indexes on 'sbtest4'...
Creating table 'sbtest5'...
Inserting 4000 records into 'sbtest5'
Creating secondary indexes on 'sbtest5'...

清空查询统计,然后运行一次sysbench,查看proxysql统计信息。
下面这个sql在proxysql是专门清空stats_mysql_query_digest表的。

admin@127.0.0.1 [(none)] 02:24:45>>>SELECT 1 FROM stats_mysql_query_digest_reset LIMIT 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.01 sec)

使用sysbench来压测mysql。

[mysql@hpc01 ~]$ sysbench --test=oltp --oltp-table-size=4000 --oltp-read-only=off --init-rng=on --num-threads=5 --max-requests=0 --oltp-dist-type=uniform --max-time=36 --mysql-user=sbuser  --mysql-password='sbpass' --db-driver=mysql --mysql-port=6033 --mysql-table-engine=innodb  --mysql-host=127.0.0.1  --mysql-db=sbtest --oltp-tables-count=5 --report-interval=1 run

然后我们查看proxysql为我们统计了那些信息呢。

非常不错的统计信息,各种命令的聚合统计,方便我们监控数据库负载趋势,一眼就可以看出读多还是写多。
还有更具体的统计信息。

proxysql converts queries into fingerprints。然后对每类sql做聚合统计。
2.3读写分离配置
细心同学就发现了,现在所有的sql都发送到了group 0,也就是我们的master主库节点。好咯,我们来做一个读写分离。
要注意,proxysql没有设计多么复杂的读写分离算法,proxysql的实现方式很简单,就是做sql路由,我们自定义sql路由规则就可以实现读写分离。这里我们将所有除了select*
from update的select全部发送到slave,其他的的语句发送到master。
登录proxysql配置路由项。

admin@127.0.0.1 [(none)] 02:41:58>>>INSERT INTO mysql_query_rules(active,match_pattern,destination_hostgroup,apply) VALUES(1,'^SELECT.*FOR UPDATE$',0,1);
Query OK, 1 row affected (0.00 sec) admin@127.0.0.1 [(none)] 02:41:59>>>INSERT INTO mysql_query_rules(active,match_pattern,destination_hostgroup,apply) VALUES(1,'^SELECT',1,1);
Query OK, 1 row affected (0.00 sec) admin@127.0.0.1 [(none)] 02:41:59>>>LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec) admin@127.0.0.1 [(none)] 02:42:20>>>SAVE MYSQL QUERY RULES TO DISK;
Query OK, 0 rows affected (0.04 sec)

聪明的同学就发现了,原来这么匹配是基于正则表达式的啊。right。
active表示是否启用这个sql路由项,
match_pattern就是我们正则匹配项,
destination_hostgroup表示我们要将该类sql转发到哪些mysql上面去,这里我们将select转发到group 1,也就是两个slave上。
apply为1表示该正则匹配后,将不再接受其他匹配,直接转发。
添加了sql路由,我们来看看是否实现了读写分离呢。
首先记得清空proxysql的query统计

admin@127.0.0.1 [(none)] 02:48:56>>>SELECT 1 FROM stats_mysql_query_digest_reset LIMIT 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

直接用sysbench压测

[mysql@hpc01 ~]$ sysbench --test=oltp --oltp-table-size=4000 --oltp-read-only=off --init-rng=on --num-threads=5 --max-requests=0 --oltp-dist-type=uniform --max-time=36 --mysql-user=sbuser  --mysql-password='sbpass' --db-driver=mysql --mysql-port=6033 --mysql-table-engine=innodb  --mysql-host=127.0.0.1  --mysql-db=sbtest --oltp-tables-count=5 --report-interval=1 run

查看结果

可以看到,所有的非select*for update的查询语句都已经转发到slave了,也就是group 1.
登录到slave上我们确实可以到很多查询已经切过来了。


3.查询重写
3.1配置
查询重写这种东西,对于线上环境紧急故障处理还是很有用处的。如果定位到了问题所在,必须修改SQL,时间紧急,让应用重新发布上线是不太现实了,这时查询重写这个东西就非常有用了。
举个简单的例子

SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c

这类SQL是有优化余地的,我们可以去掉ORDER BY c,多此一举,因为在做DISTINCT时我们已经做了排序。需要改成如下模式

SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? AND ?

其实查询重写的实现在proxysql中也实现为正则匹配替换。是不是非常赞,不需要非常复杂的算法就实现了,非常赞,用户可控度也大,用着非常舒服。
我们编辑一条SQL路由规则。

INSERT INTO mysql_query_rules (active,match_pattern,replace_pattern,apply) VALUES (1,'DISTINCT(.*)ORDER BY c','DISTINCT\1',1);

这条规则什么意思呢,这条路由规则表示当proxysql匹配到DISTINCT<若干字符>ORDER BY c这个模式后,就将这个模式的ORDER BY c去掉。那么DISTINCT\1中的\1是什么意思呢,玩过sed的同学很快就反应过来,这不就是向前引用么,哈哈答对了。举个例子你就明白了。
假如我们有这样一个文件:

[root@hpc01 ~]$ cat aaa
,90909
,dasfsdfssd98908

我们想把每行的逗号去掉,我们可以利用sed的向前引用:

[root@hpc01 ~]$ sed "s/,\(.*\)/\1/g" aaa
90909
dasfsdfssd98908

逗号后面的所有字符用\(.*\)表示,在后面替换我们用\1表示这部分。sed最大支持到\9,还是很强大的。如果实在不明白什么意思,推荐学习学习《sed与awk》。反正要玩proxysql正则表达式要玩得溜溜的。
还要注意,我们要重写的selete语句,你可曾记得,我们已经将所有非seletc* from
update语句已经重定向到slave,也就是select语句已经有了一条路由规则了。而且那条路由已经将apply字段设为1,也就是不再接受其他路由规则。好,我们需要更改apply字段才可以使这条规则生效。

update mysql_query_rules set apply=0 where match_pattern='^SELECT';

使配置生效

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

注意,以上配置都是在proxysql中操作的,别跑错了跑到数据库中搞啊。

3.2验证配置
首先清空以前的统计信息,不然混在一起,无法辨认。

admin@127.0.0.1 [(none)] 03:16:37>>>SELECT 1 FROM stats_mysql_query_digest_reset LIMIT 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.01 sec)

直接使用sysbench压测
然后查看proxysql统计信息。

哈哈,是不是distinct后面的order by已经没有了。厉害了,word proxysql。

文章导航

Write a Reply or Comment

电子邮件地址不会被公开。 必填项已用*标注

评论

姓名 *

电子邮件 *

站点

 

Search for:

For DBA 介绍

For DBA 是一个热爱数据库,Linux的团队,曾就职于某省级运营商,现就职于某知名互联网公司,热衷于研究和分享各种Oracle,MySQL,MongoDB,以及Linux的使用经验和心得。

标签

近期评论

书签

文章归档

http://fordba.com/mysql_proxysql_rw_split.html

MySQL中间件之ProxySQL_读写分离/查询重写配置的更多相关文章

  1. MySql的主从复制以及读写分离详解

    MySQL主从复制(Master-Slave)与读写分离(MySQL-Proxy)实践 Mysql作为目前世界上使用最广泛的免费数据库,相信所有从事系统运维的工程师都一定接触过.但在实际的生产环境中, ...

  2. MySQL+Amoeba实现数据库读写分离

    参考:https://www.cnblogs.com/liuyisai/p/6009379.html 一,Amoeba是什么 Amoeba(变形虫)项目,专注 分布式数据库 proxy 开发.座落与C ...

  3. mysql数据库主从同步读写分离(一)主从同步

    1.mysql数据库主从同步读写分离 1.1.主要解决的生产问题 1.2.原理 a.为什么需要读写分离? 一台服务器满足不了访问需要.数据的访问基本都是2-8原则. b.怎么做?  不往从服务器去写了 ...

  4. 030:Cetus中间件和MHA读写分离

    030:Cetus中间件和MHA读写分离 line:V1.1 mail: gczheng@139.com date: 2018-08-30 一.主机环境 虚拟机配置 CPU 内存 硬盘 OS版本 My ...

  5. 基于Mysql-Proxy实现Mysql的主从复制以及读写分离(上)

    基于Mysql-Proxy实现Mysql的主从复制以及读写分离(上) 上周BOSS给分配任务让实现一下Mysql数据库的主从复制以及读写分离,然后花了一盏茶的功夫进行了调研,发现主从复制数据库进行一番 ...

  6. MySQL主从同步、读写分离配置步骤、问题解决笔记

    MySQL主从同步.读写分离配置步骤.问题解决笔记 根据要求配置MySQL主从备份.读写分离,结合网上的文档,对搭建的步骤和出现的问题以及解决的过程做了如下笔记:       现在使用的两台服务器已经 ...

  7. redis 作为 mysql的缓存服务器(读写分离)

    redis 作为 mysql的缓存服务器(读写分离) 一.redis简介 Redis是一个key-value存储系统.和Memcached类似,为了保证效率,数据都是缓存在内存中.区别的是redis会 ...

  8. MySQL主从同步和读写分离的配置

    主服务器:192.168.1.126 从服务器:192.168.1.163 amoeba代理服务器:192.168.1.237 系统全部是CentOS 6.7 1.配置主从同步 1.1.修改主服务器( ...

  9. 基于Mysql-Proxy实现Mysql的主从复制以及读写分离(下)

    基于Mysql-Proxy实现Mysql的主从复制以及读写分离(下) 昨天谈到了Mysql实现主从复制,但由于时间原因并未讲有关读写分离的实现,之所以有读写分离,是为了使数据库拥有双机热备功能,至于双 ...

随机推荐

  1. synchronized(4)修饰语句块之:synchronized(this)

    synchronized(this) 此时,线程获得的是对象锁.例如: public class Thread8 extends Thread { public void sync_fun() { s ...

  2. JavaScript的执行

    下面内容参考:http://blog.csdn.net/cxiaokai/article/details/7552653 http://www.jb51.net/article/36755.htm 首 ...

  3. 463 Island Perimeter 岛屿的周长

    详见:https://leetcode.com/problems/island-perimeter/description/ C++: class Solution { public: int isl ...

  4. 223 Rectangle Area 矩形面积

    在二维平面上计算出两个由直线构成的矩形叠加覆盖后的面积. 假设面积不会超出int的范围. 详见:https://leetcode.com/problems/rectangle-area/descrip ...

  5. 一命令安装nginx

    #!/bin/bash yum -y install wget pcre-devel openssl openssl-devel libtool gcc automake autoconf libto ...

  6. ATM机(非函数版)

    #include<stdio.h>#include<stdlib.h>int main(void){char zhangHao[]="123";int mi ...

  7. 5 Transforms 转移 笔记

    5 Transforms 转移 笔记   Transforms    Unfortunately, no one can be told what the Matrix is. You have to ...

  8. MySQL学习随笔--视图

    视图概念 数据库中的视图指的是一个虚拟表,其内容由查询定义.同真实的表一样,视图也是由行与列构成的.视图的数据来源由SQL语句查询得到,不存储数据 视图创建方法 格式 : create view 视图 ...

  9. [转]c++应用程序文件的编译过程

    原文地址 这里讲下C++文件的编译过程及其中模板的编译过程: 一:一般的C++应用程序的编译过程.    一般说来,C++应用程序的编译过程分为三个阶段.模板也是一样的. 在cpp文件中展开inclu ...

  10. python远程控制电脑

    python拥有大量的第三方库,且语法简单.今天老杨就用python实现远程控制电脑 ​ 所谓,谋定而后动,在实现任何一个需求之前,我们需要先分析,捋清楚一个思路,远程控制电脑,无非就是接收远程的命令 ...