mysql中间件proxysql
mysql实现读写分离的方式
mysql 实现读写分离的方式有以下几种:
- 程序修改mysql操作,直接和数据库通信,简单快捷的读写分离和随机的方式实现的负载均衡,权限独立分配,需要开发人员协助。
- amoeba,直接实现读写分离和负载均衡,不用修改代码,有很灵活的数据解决方案,自己分配账户,和后端数据库权限管理独立,权限处理不够灵活
- Mysql-proxy:,直接实现读写分离和负载均衡,不用修改代码,master和slave用一样的帐号,效率低
- Atlas是一个位于应用程序与MySQL之间中间件。在后端DB看来,Atlas相当于连接它的客户端,在前端应用看来,Atlas相当于一个DB。Atlas作为服务端与应用程序通讯,它实现了MySQL的客户端和服务端协议,同时作为客户端与MySQL通讯。它对应用程序屏蔽了DB的细节,同时为了降低MySQL负担,它还维护了连接池。
- Mycat:基于开源cobar演变而来,对cobar的代码进行了彻底的重构,使用NIO重构了网络模块,并且优化了Buffer内核,增强了聚合,Join等基本特性,同时兼容绝大多数数据库成为通用的数据库中间件。1.4 版本以后 完全的脱离基本cobar内核,结合Mycat集群管理、自动扩容、智能优化,成为高性能的中间件。
- ProxySQL等。
读写分离原理
读写分离就是用户在发送请求时,请求经过中间件,中间件将请求中的读和写操作分辨出来将读请求发送给后端的从服务器,将写请求发送给后端的主服务器,再又主服务器通过主从复制将数据复制给其他从服务器.
ProxySQL简介
ProxySQL为MySQL的中间件,其有两个版本官方版和percona版,percona版是基于官方版基础上修改而来。ProxySQL是由C++语言开发,轻量级但性能优异(支持处理千亿级数据),其具有中间件所需要的绝大多数功能,如:
- 最基本的读/写分离,且方式有多种
- 可定制基于用户、基于schema、基于语句的规则对SQL语句进行路由。换句话说,规则很灵活。基于schema和与语句级的规则,可以实现简单的sharding(分库分表)
- 可缓存查询结果。虽然ProxySQL的缓存策略比较简陋,但实现了基本的缓存功能,绝大多数时候也够用了。此外,作者已经打算实现更丰富的缓存策略
- 监控后端节点。ProxySQL可以监控后端节点的多个指标,包括:ProxySQL和后端的心跳信息,后端节点的read-only/read-write,slave和master的数据同步延迟性(replication lag)
官方站点:https://proxysql.com/ 官方手册:https://github.com/sysown/proxysql/wiki
ProxySQL安装
[root@localhost ~]# yum -y install http://repo.proxysql.com/ProxySQL/proxysql-2.1.x/centos/8/proxysql-2.1.1-1-centos8.x86_64.rpm //安装后生成的文件 [root@localhost ~]# rpm -ql proxysql /etc/logrotate.d/proxysql /etc/proxysql.cnf /etc/systemd/system/proxysql-initial.service /etc/systemd/system/proxysql.service /usr/bin/proxysql /usr/lib/.build-id /usr/lib/.build-id/35 /usr/lib/.build-id/35/7b92aa992a41d607f1fcb3718656bf90e48bed /usr/share/proxysql/tools/proxysql_galera_checker.sh /usr/share/proxysql/tools/proxysql_galera_writer.pl
ProxySQL的Admin管理接口
当 ProxySQL 启动后,将监听两个端口:
- admin管理接口,默认端口为6032。该端口用于查看、配置ProxySQL
- 接收SQL语句的接口,默认端口为6033,这个接口类似于MySQL的3306端口
ProxySQL 的 admin 管理接口是一个使用 MySQL 协议的接口,所以,可以直接使用 mysql 客户端、navicat 等工具去连接这个管理接口,其默认的用户名和密码均为 admin
ProxySQL内置数据库
[root@localhost ~]# yum -y install mariadb [root@localhost ~]# mysql -uadmin -padmin -P6032 -h127.0.0.1 MySQL [(none)]> show databases; +-----+---------------+-------------------------------------+ | seq | name | file | +-----+---------------+-------------------------------------+ | 0 | main | | | 2 | disk | /var/lib/proxysql/proxysql.db | | 3 | stats | | | 4 | monitor | | | 5 | stats_history | /var/lib/proxysql/proxysql_stats.db | +-----+---------------+-------------------------------------+
由于 ProxySQL 的配置全部保存在几个自带的库中,所以通过管理接口,可以非常方便地通过发送一些SQL命令去修改 ProxySQL 的配置。 ProxySQL 会解析通过该接口发送的某些对ProxySQL 有效的特定命令,并将其合理转换后发送给内嵌的 SQLite3 数据库引擎去运行
ProxySQL 的配置几乎都是通过管理接口来操作的,通过 Admin 管理接口,可以在线修改几乎所有的配置并使其生效。只有两个变量的配置是必须重启 ProxySQL 才能生效的,它们是:mysql-threads 和 mysql-stacksize
其中:
- main库是ProxySQL最主要的库,是需要修改配置时使用的库,它其实是一个内存数据库系统。所以,修改main库中的配置后,必须将其持久化到disk上才能永久保存
- disk库是磁盘数据库,该数据库结构和内存数据库完全一致。当持久化内存数据库中的配置时,其实就是写入到disk库中。磁盘数据库的默认路径为 $DATADIR/proxysql.db
- stats库是统计信息库。这个库中的数据一般是在检索其内数据时临时填充的,它保存在内存中。因为没有相关的配置项,所以无需持久化
- monitor库是监控后端MySQL节点相关的库,该库中只有几个log类的表,监控模块收集到的监控信息全都存放到对应的log表中
- stats_history库是1.4.4版新增的库,用于存放历史统计数据。默认路径为 $DATADIR/proxysql_stats.db
ProxySQL 内部使用的是 SQLite3 数据库,无论是内存数据库还是磁盘数据库,都是通过SQLite3引 擎进行解析、操作的。它和 MySQL 的语法可能稍有不同,但ProxySQL会对不兼容的语法自动进行调整,最大程度上保证MySQL语句的有效率。
上面描述main库的时候,只是说了内存数据库需要持久化到disk库才能永久保存配置。但实际上,修改了main库中的配置后,并不会立即生效,它还需要load到runtime的数据结构中才生效,只有在runtime数据结构中的配置才是对ProxySQL当前有效的配置。
ProxySQL main库内的表
MySQL [(none)]> show tables; +----------------------------------------------------+ | tables | +----------------------------------------------------+ | global_variables | | mysql_aws_aurora_hostgroups | | mysql_collations | | mysql_firewall_whitelist_rules | | mysql_firewall_whitelist_sqli_fingerprints | | mysql_firewall_whitelist_users | | mysql_galera_hostgroups | | mysql_group_replication_hostgroups | | mysql_query_rules | | mysql_query_rules_fast_routing | | mysql_replication_hostgroups | | mysql_servers | | mysql_users | | proxysql_servers | | restapi_routes | | runtime_checksums_values | | runtime_global_variables | | runtime_mysql_aws_aurora_hostgroups | | runtime_mysql_firewall_whitelist_rules | | runtime_mysql_firewall_whitelist_sqli_fingerprints | | runtime_mysql_firewall_whitelist_users | | runtime_mysql_galera_hostgroups | | runtime_mysql_group_replication_hostgroups | | runtime_mysql_query_rules | | runtime_mysql_query_rules_fast_routing | | runtime_mysql_replication_hostgroups | | runtime_mysql_servers | | runtime_mysql_users | | runtime_proxysql_servers | | runtime_restapi_routes | | runtime_scheduler | | scheduler | +----------------------------------------------------+ 32 rows in set (0.000 sec)
main库中的表分为runtime开头和非runtime开头, runtime开头为运行时的设置 ,非runtime开头为需要设置的配置 ,所有的配置修改后需要执行命令才能加载到runtime生效。
LOAD ... TO RUNTIME; #使修改立即生效
SAVE ... TO DISK; #使修改永久保存到磁盘
和admin管理接口相关的变量
admin-admin_credentials:控制的是admin管理接口的管理员账户。默认的管理员账户和密码为admin:admin,但是这个默认的用户只能在本地使用。如果想要远程连接到ProxySQL,例如用windows上的navicat连接Linux上的ProxySQL管理接口,必须自定义一个管理员账户。
//查看当前用户 MySQL [(none)]> show variables like 'admin_admin_%'; +-------------------------+-------------+ | Variable_name | Value | +-------------------------+-------------+ | admin-admin_credentials | admin:admin | +-------------------------+-------------+ //添加管理员账号TestUser密码123 MySQL [(none)]> set admin-admin_credentials='admin:admin;TestUser:123'; Query OK, 1 row affected (0.000 sec) MySQL [(none)]> select @@admin-admin_credentials; +---------------------------+ | @@admin-admin_credentials | +---------------------------+ | admin:admin;TestUser:123 | +---------------------------+ 1 row in set (0.001 sec) //使修改立即生效 MySQL [(none)]> load admin variables to runtime; Query OK, 0 rows affected (0.002 sec) //使修改永久保存到磁盘 MySQL [(none)]> save admin variables to disk; Query OK, 43 rows affected (0.004 sec)
修改后,就可以使用该用户名和密码连接管理接口
[root@localhost ~]# mysql -uTestUser -p123 -P6032 -h192.168.44.128 Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.5.30 (ProxySQL Admin Module) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]>
必须要区分admin管理接口的用户名和mysql_users中的用户名
- admin管理接口的用户是连接到管理接口(默认端口6032)上用来管理、配置ProxySQL的
- mysql_users表中的用户名是应用程序连接ProxySQL(默认端口6033),以及ProxySQL连接后端MySQL Servers使用的用户。它的作用是发送、路由SQL语句,类似于MySQL Server的3306端口。所以,这个表中的用户必须已经在后端MySQL Server上存在且授权了
admin管理接口的用户必须不能存在于mysql_users中,这是出于安全的考虑,防止通过admin管理接口用户猜出mysql_users中的用户。
admin-stats_credentials:控制admin管理接口的普通用户,这个变量中的用户没有超级管理员权限,只能查看monitor库和main库中关于统计的数据,其它库都是不可见的,且没有任何写权限。
默认的普通用户名和密码均为 stats ,与admin一样,它默认也只能用于本地登录,若想让人远程查看则要添加查看的专有用户。
MySQL [(none)]> select @@admin-stats_credentials; +---------------------------+ | @@admin-stats_credentials | +---------------------------+ | stats:stats | +---------------------------+ //添加专有用户mystats MySQL [(none)]> set admin-stats_credentials='admin:admin;mystats:123'; Query OK, 1 row affected (0.00 sec) MySQL [(none)]> select @@admin-stats_credentials; +---------------------------+ | @@admin-stats_credentials | +---------------------------+ | stats:stats;mystats:123 | +---------------------------+ 1 row in set (0.001 sec) MySQL [(none)]> load admin variables to runtime; Query OK, 0 rows affected (0.001 sec) MySQL [(none)]> save admin variables to disk; Query OK, 43 rows affected (0.001 sec)
同样,这个变量中的用户必须不能存在于mysql_users表中
使用mystats用户远程连接查看
//如果连接不上请关闭防火墙和selinux [root@localhost ~]# mysql -umystats -p123 -P6032 -h192.168.44.128 Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 7 Server version: 5.5.30 (ProxySQL Admin Module) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> show databases; +-----+---------------+-------------------------------------+ | seq | name | file | +-----+---------------+-------------------------------------+ | 0 | main | | | 2 | monitor | | | 3 | stats_history | /var/lib/proxysql/proxysql_stats.db | +-----+---------------+-------------------------------------+ 3 rows in set (0.000 sec)
admin-mysql_ifaces:指定admin接口的监听地址,格式为冒号分隔的hostname:port列表。默认监听在 0.0.0.0:6032
此时我们如果要修改admin接口的监听地址为0.0.0.0:6000
MySQL [(none)]> show variables like 'admin-mysql%'; +--------------------+--------------+ | Variable_name | Value | +--------------------+--------------+ | admin-mysql_ifaces | 0.0.0.0:6032 | +--------------------+--------------+ //修改监听端口为6000 MySQL [(none)]> set admin-mysql_ifaces='0.0.0.0:6000'; Query OK, 1 row affected (0.000 sec) MySQL [(none)]> show variables like 'admin-mysql%'; +--------------------+--------------+ | Variable_name | Value | +--------------------+--------------+ | admin-mysql_ifaces | 0.0.0.0:6000 | +--------------------+--------------+ 1 row in set (0.000 sec) MySQL [(none)]> load admin variables to runtime; Query OK, 0 rows affected (0.001 sec) MySQL [(none)]> save admin variables to disk; Query OK, 43 rows affected (0.001 sec) [root@localhost ~]# ss -antl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 0.0.0.0:22 0.0.0.0:* LISTEN 0 128 0.0.0.0:6000 0.0.0.0:* LISTEN 0 128 0.0.0.0:6033 0.0.0.0:* LISTEN 0 128 0.0.0.0:6033 0.0.0.0:* LISTEN 0 128 0.0.0.0:6033 0.0.0.0:* LISTEN 0 128 0.0.0.0:6033 0.0.0.0:*
mysql-interfaces:指定远程接口的监听地址,格式为冒号分隔的hostname:port列表。默认监听在 0.0.0.0:6033
此时我们如果要修改远程接口接口的监听地址为0.0.0.0:3306
MySQL [(none)]> show variables like '%interfaces%'; +------------------+--------------+ | Variable_name | Value | +------------------+--------------+ | mysql-interfaces | 0.0.0.0:6033 | +------------------+--------------+ 1 row in set (0.000 sec) MySQL [(none)]> set mysql-interfaces='0.0.0.0:3306'; Query OK, 1 row affected (0.000 sec) MySQL [(none)]> show variables like '%interfaces%'; +------------------+--------------+ | Variable_name | Value | +------------------+--------------+ | mysql-interfaces | 0.0.0.0:3306 | +------------------+--------------+ 1 row in set (0.000 sec) MySQL [(none)]> save mysql variables to disk; Query OK, 140 rows affected (0.002 sec) [root@localhost ~]# systemctl restart proxysql [root@localhost ~]# ss -antl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 0.0.0.0:22 0.0.0.0:* LISTEN 0 128 0.0.0.0:3306 0.0.0.0:* LISTEN 0 128 0.0.0.0:3306 0.0.0.0:* LISTEN 0 128 0.0.0.0:3306 0.0.0.0:* LISTEN 0 128 0.0.0.0:3306 0.0.0.0:* LISTEN 0 128 0.0.0.0:6000 0.0.0.0:*
如果是用的yum安装的proxysql服务的话,想要修改监控端口只需要修改配置文件
[root@localhost ~]# vim /etc/proxysql.cnf interfaces="0.0.0.0:6033;/tmp/proxysql.sock" interfaces="0.0.0.0:3306" default_schema="information_schema" stacksize=1048576 server_version="5.5.30" connect_timeout_server=3000 [root@localhost ~]# systemctl stop proxysql [root@localhost ~]# systemctl start proxysql
ProxySQL多层配置系统
ProxySQL 的配置系统非常强大,它能在线修改几乎所有配置(仅有的两个需要重启才能生效的变量为 mysql-threads 和 mysql-stacksize ),并在线生效、持久化保存。这得益于它采用的多层配置系统。
多层配置系统结构如下:
+-------------------------+ | RUNTIME | +-------------------------+ /|\ | | | [1] | [2] | | \|/ +-------------------------+ | MEMORY | +-------------------------+ _ /|\ | |\ | | \ [3] | [4] | \ [5] | \|/ \ +-------------------------+ +---------------+ | DISK | | CONFIG FILE | +-------------------------+ +---------------+
最底层的是 disk 库和 config file 。这里需要注意,这里的 config file 就是传统的配置文件,默认为 /etc/proxysql.cnf , ProxySQL 启动时,主要是从 disk 库中读取配置加载到内存并最终加载到 runtime 生效,只有极少的几个特定配置内容是从 config file 中加载的,除非是第一次初始化 ProxySQL 运行环境(或者disk库为空)。
中间层的是 memory ,表示的是内存数据库,其实就是 main 库。通过管理接口修改的所有配置,都保存在内存数据库(main)中。当 ProxySQL 重启或者崩溃时,这个内存数据库中的数据会丢失,所以需要 save 到 disk 库中。
最上层的是 runtime ,它是 ProxySQL 有关线程运行时读取的数据结构。换句话说,该数据结构中的配置都是已生效的配置。所以,修改了 main 库中的配置后,必须 load 到 runtime 数据结构中才能使其生效。
在上面的多层配置系统图中,标注了[1]、[2]、[3]、[4]、[5]的序号。每个序号都有两个操作方向from/to,其实只是所站角度不同而已。以下是各序号对应的操作:
[1] :将内存数据库中的配置加载到RUNTIME数据结构中 LOAD XXX FROM MEMORY LOAD XXX TO RUNTIME [2] :将RUNTIME数据结构中的配置持久化到内存数据库中 SAVE XXX FROM RUNTIME SAVE XXX TO MEMORY [3] :将磁盘数据库中的配置加载到内存数据库中 LOAD XXX FROM DISK LOAD XXX TO MEMORY [4] :将内存数据库中的配置持久化到磁盘数据库中 SAVE XXX FROM MEMORY SAVE XXX TO DISK [5] :从传统配置文件中读取配置加载到内存数据库中 LOAD XXX FROM CONFIG
上面的XXX表示要加载/保存的是哪类配置
MySQL [(none)]> show tables; +----------------------------------------------------+ | tables | +----------------------------------------------------+ | global_variables | #1 | mysql_aws_aurora_hostgroups | #2 | mysql_collations | #3 | mysql_firewall_whitelist_rules | #4 | mysql_firewall_whitelist_sqli_fingerprints | #5 | mysql_firewall_whitelist_users | #6 | mysql_galera_hostgroups | #7 | mysql_group_replication_hostgroups | #8 | mysql_query_rules | #9 | mysql_query_rules_fast_routing | #10 | mysql_replication_hostgroups | #11 | mysql_servers | #12 | mysql_users | #13 | proxysql_servers | #14 | restapi_routes | #15 | scheduler | #16
上面的结果中我给这些表都标注了一些序号,其所对应的表的内容有以下讲究:
- (1)中包含两类变量,以amdin-开头的表示admin variables,以mysql-开头的表示mysql variables。修改哪类变量,前文的XXX就代表哪类
- (2)
- (3)只是一张表,保存的是ProxySQL支持的字符集和排序规则,它是不用修改的
- (4)(5)(6)对应mysql firewall
- (7)
- (8)(11)(12)对应mysql server
- (13)对应mysql users
- (14)对应proxysql servers
- (15)对应restapi routes
- (16)对应scheduler
启动ProxySQL时如何加载配置
如果 ProxySQL 是刚安装的,或者磁盘数据库文件为空(甚至不存在),或者启动 ProxySQL 时使用了选项 --initial,这几种情况启动 ProxySQL 时,都会从传统配置文件 config file 中读取配置加载到内存数据库,并自动 load 到 runtime 数据结构、save到磁盘数据库,这是初始化 ProxySQL 运行环境的过程。
如果不是第一次启动 ProxySQL ,由于已经存在磁盘数据库文件,这时 ProxySQL 会从磁盘数据库中读取几乎所有的配置(即使传统配置文件中配置了某项,也不会去解析),但有3项是必须从传统配置文件中读取,它们分别是:
- datadir:ProxySQL启动时,必须从配置文件中确定它的数据目录,因为磁盘数据库文件、日志以及其它一些文件是存放在数据目录下的。如果使用/etc/init.d/proxysql管理ProxySQL,则除了修改/etc/proxysql.cnf的datadir,还需要修改该脚本中的datadir。
- restart_on_missing_heartbeats:MySQL线程丢失多少次心跳,就会杀掉这个线程并重启它。默认值为10。
- execute_on_exit_failure:如果设置了该变量,ProxySQL父进程将在每次ProxySQL崩溃的时候执行已经定义好的脚本。建议使用它来生成一些崩溃时的警告和日志。注意,ProxySQL的重启速度可能只有几毫秒,因此很多其它的监控工具可能无法探测到ProxySQL的一次普通故障,此时可使用该变量
不同类型的读写分离方案解析
数据库中间件最基本的功能就是实现读写分离, ProxySQL 当然也支持。而且 ProxySQL 支持的路由规则非常灵活,不仅可以实现最简单的读写分离,还可以将读/写都分散到多个不同的组,以及实现分库 sharding (分表sharding的规则比较难写,但也能实现)。
本文只描述通过规则制定的语句级读写分离,不讨论通过 ip/port, client, username, schemaname 实现的读写分离。
下面描述了ProxySQL能实现的常见读写分离类型
最简单的读写分离
这种模式的读写分离,严格区分后端的master和slave节点,且slave节点必须设置选项read_only=1
在ProxySQL上,分两个组,一个写组HG=10,一个读组HG=20。同时在ProxySQL上开启monitor模块的read_only监控功能,让ProxySQL根据监控到的read_only值来自动调整节点放在HG=10(master会放进这个组)还是HG=20(slave会放进这个组)
这种模式的读写分离是最简单的,只需在mysql_users表中设置用户的默认路由组为写组HG=10,并在mysql_query_rules中加上两条简单的规则(一个select for update,一个select)即可
这种读写分离模式,在环境较小时能满足绝大多数需求。但是需求复杂、环境较大时,这种模式就太过死板,因为一切都是monitor模块控制的
多个读组或写组的分离模式
前面那种读写分离模式,是通过 monitor 模块监控 read_only 来调整的,所以每一个后端集群必须只能分为一个写组,一个读组。
但如果想要区分不同的 select ,并将不同的 select 路由到不同的节点上。例如有些查询语句的开销非常大,想让它们独占一个节点/组,其它查询共享一个节点/组,怎么实现?
例如,下面这种模式
看上去非常简单。但是却能适应各种需求。例如,后端做了分库,对某库的查询要路由到特定的主机组
至于各个主机组是同一个主从集群(下图左边),还是互相独立的主从集群环境(下图右边),要看具体的需求,不过这种读写分离模式都能应付
在实现这种模式时,前提是不能开启monitor模块的read_only监控功能,也不要设置mysql_replication_hostgroup 表
例如,下面的配置实现的是上图左边的结构:写请求路由给HG=10,对test1库的select语句路由给HG=20,其它select路由给HG=30
mysql_servers: +--------------+----------+------+--------+--------+ | hostgroup_id | hostname | port | status | weight | +--------------+----------+------+--------+--------+ | 10 | host1 | 3306 | ONLINE | 1 | | 20 | host2 | 3306 | ONLINE | 1 | | 30 | host3 | 3306 | ONLINE | 1 | +--------------+----------+------+--------+--------+ mysql_users: +----------+-------------------+ | username | default_hostgroup | +----------+-------------------+ | root | 10 | +----------+-------------------+ mysql_query_rules: +---------+-----------------------+----------------------+ | rule_id | destination_hostgroup | match_digest | +---------+-----------------------+----------------------+ | 1 | 10 | ^SELECT.*FOR UPDATE$ | | 2 | 20 | ^SELECT.*test1\..* | | 3 | 30 | ^SELECT | +---------+-----------------------+----------------------+
mysql_servers: +--------------+----------+------+--------+--------+ | hostgroup_id | hostname | port | status | weight | +--------------+----------+------+--------+--------+ | 10 | host1 | 3306 | ONLINE | 1 | | 20 | host2 | 3306 | ONLINE | 1 | | 30 | host3 | 3306 | ONLINE | 1 | +--------------+----------+------+--------+--------+ mysql_users: +----------+-------------------+ | username | default_hostgroup | +----------+-------------------+ | root | 10 | +----------+-------------------+ mysql_query_rules: +---------+-----------------------+----------------------+ | rule_id | destination_hostgroup | match_digest | +---------+-----------------------+----------------------+ | 1 | 10 | ^SELECT.*FOR UPDATE$ | | 2 | 20 | ^SELECT.*test1\..* | | 3 | 30 | ^SELECT | +---------+-----------------------+----------------------+
ProxySQL实现读写分离示例
环境说明
主机名 | IP | 应用 | 角色 |
localhost | 192.168.44.128 | prxoysql | 读写分离解析主机 |
node1 | 192.168.44.129 | mariadb | master |
node2 | 192.168.44.130 | mariiadb | slave |
- 三台主机关闭防火墙
- 三台主机关闭SELINUX
- 三台主机安装mariadb且另外两台主机已经好配置主从
安装proxysql
[root@localhost ~]# yum -y install http://repo.proxysql.com/ProxySQL/proxysql-2.1.x/centos/8/proxysql-2.1.1-1-centos8.x86_64.rpm [root@localhost ~]# systemctl start proxysql [root@localhost ~]# systemctl enable proxysql
node1上mysql主库添加proxysql可以增删改查的账号
MariaDB [(none)]> grant all on *.* to 'proxysql'@'192.168.44.128' identified by '123'; Query OK, 0 rows affected (0.002 sec) MariaDB [(none)]> flush privileges; Query OK, 0 rows affected (0.001 sec)
Proxysql管理端添加后端连接mysql主从数据库的配置
MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,comment) values(100,'192.168.44.129',3306,1,'Write Group'); Query OK, 1 row affected (0.000 sec) MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,comment) values(200,'192.168.44.128',3306,1,'Read Group'); Query OK, 1 row affected (0.000 sec) MySQL [(none)]> select * from mysql_servers; +--------------+----------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+ | hostgroup_id | hostname | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment | +--------------+----------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+ | 100 | 192.168.44.129 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | Write Group | | 200 | 192.168.44.130 | 3306 | 0 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | Read Group | +--------------+----------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+ 2 rows in set (0.000 sec) //修改后,需要加载到RUNTIME,并保存到disk MySQL [(none)]> load mysql servers to runtime; Query OK, 0 rows affected (0.004 sec) MySQL [(none)]> save mysql servers to disk; Query OK, 0 rows affected (0.008 sec)
在 proxysql 主机的 mysql_users 表中添加刚才在 master 上创建的账号 proxysql,proxysql 客户端需要使用这个账号来访问数据库
default_hostgroup 默认组设置为写组,也就是100;
当读写分离的路由规则不符合时,会访问默认组的数据库;
MySQL [(none)]> insert into mysql_users(username,password,default_hostgroup,transaction_persistent)values('proxysql','123',100,1); Query OK, 1 row affected (0.000 sec) MySQL [(none)]> select * from mysql_users \G *************************** 1. row *************************** username: proxysql # 后端mysql实例的用户名 password: 123 # 后端mysql实例的密码 active: 1 # active=1表示用户生效,0表示不生效 use_ssl: 0 default_hostgroup: 100 # 用户默认登录到哪个hostgroup_id下的实例 default_schema: NULL # 用户默认登录后端mysql实例时连接的数据库,这个地方为NULL的话,则由全局变量mysql-default_schema决定,默认是information_schema schema_locked: 0 transaction_persistent: 1 # 如果设置为1,连接上ProxySQL的会话后,如果在一个hostgroup上开启了事务,那么后续的sql都继续维持在这个hostgroup上,不论是否会匹配上其它路由规则,直到事务结束。虽然默认是0 fast_forward: 0 # 忽略查询重写/缓存层,直接把这个用户的请求透传到后端DB。相当于只用它的连接池功能,一般不用,路由规则 .* 就行了 backend: 1 frontend: 1 max_connections: 10000 # 该用户允许的最大连接数 attributes: comment: 1 row in set (0.000 sec) //保存到disk MySQL [(none)]> load mysql users to runtime; Query OK, 0 rows affected (0.000 sec) MySQL [(none)]> save mysql users to disk; Query OK, 0 rows affected (0.002 sec)
添加健康检测的帐号
在mysql的 master 端添加属于proxysql的只读账号
MariaDB [(none)]> grant select on *.* to 'check-up'@'192.168.44.%' identified by '123'; Query OK, 0 rows affected (0.001 sec) MariaDB [(none)]> flush privileges; Query OK, 0 rows affected (0.001 sec)
在proxysql主机端修改变量设置健康检测的账号
MySQL [(none)]> set mysql-monitor_username='check-up'; Query OK, 1 row affected (0.001 sec) MySQL [(none)]> set mysql-monitor_password='123'; Query OK, 1 row affected (0.000 sec) MySQL [(none)]> load mysql variables to runtime; Query OK, 0 rows affected (0.003 sec) MySQL [(none)]> save mysql variables to disk; Query OK, 140 rows affected (0.005 sec)
添加读写分离的路由规则
需求:
- 将 select 查询语句全部路由至 hostgroup_id=200 的组(也就是读组)
- 但是 select * from tb for update 这样的语句是会修改数据的,所以需要单独定义,将它路由至 hostgroup_id=100 的组(也就是写组)
- 其他没有被规则匹配到的组将会被路由至用户默认的组(mysql_users 表中的 default_hostgroup)
MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(100,1,'^SELECT.*FOR UPDATE$',100,1); Query OK, 1 row affected (0.000 sec) //关于读操作分配到200对组 MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(200,1,'^SELECT',200,1); Query OK, 1 row affected (0.000 sec) MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(201,1,'^SHOW',200,1); Query OK, 1 row affected (0.000 sec) MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(202,1,'^DESC',200,1); Query OK, 1 row affected (0.000 sec) MySQL [(none)]> load mysql query rules to runtime; Query OK, 0 rows affected (0.001 sec) MySQL [(none)]> save mysql query rules to disk; Query OK, 0 rows affected (0.003 sec)
登录 proxysql 客户端
登录用户是刚才在 mysql_user 表中创建的用户,端口为6033
[root@localhost ~]# mysql -uproxysql -p123 -h127.0.0.1 -P6033 //插入数据测试 MySQL [(none)]> create database Test; Query OK, 1 row affected (0.001 sec) MySQL [(none)]> show databases; +--------------------+ | Database | +--------------------+ | Test | | information_schema | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.001 sec) MySQL [(none)]> create table Test.test(id int not null auto_increment,name varchar(20) not null,age tinyint(4) not null,primary key(id)); Query OK, 0 rows affected (0.012 sec) MySQL [(none)]> desc Test.test; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | age | tinyint(4) | NO | | NULL | | +-------+-------------+------+-----+---------+----------------+ 3 rows in set (0.007 sec)
验证读写分离是否成功
roxysql有个类似审计的功能,可以查看各类SQL的执行情况,其需要在proxysql管理端执行
[root@localhost ~]# mysql -uadmin -padmin -h127.0.0.1 -P6032 MySQL [(none)]> select * from stats_mysql_query_digest\G *************************** 1. row *************************** hostgroup: 200 schemaname: information_schema username: proxysql client_address: digest: 0x40B75DE8A4AD05EE digest_text: select * from mysql.user count_star: 1 first_seen: 1622952914 last_seen: 1622952914 sum_time: 1284 min_time: 1284 max_time: 1284 sum_rows_affected: 0 sum_rows_sent: 6 *************************** 2. row *************************** hostgroup: 100 schemaname: information_schema username: proxysql client_address: digest: 0xED8223135CE6EAB8 digest_text: create database Test count_star: 1 first_seen: 1622951695 last_seen: 1622951695 sum_time: 1354 min_time: 1354 max_time: 1354 sum_rows_affected: 1 sum_rows_sent: 0 *************************** 3. row *************************** hostgroup: 100 schemaname: information_schema username: proxysql client_address: digest: 0xF66168CAF346CE53 digest_text: create databases Test count_star: 1 first_seen: 1622951688 last_seen: 1622951688 sum_time: 1787 min_time: 1787 max_time: 1787 sum_rows_affected: 0 sum_rows_sent: 0 *************************** 4. row *************************** hostgroup: 200 schemaname: information_schema username: proxysql client_address: digest: 0x51EDBBC96C11F8F0 digest_text: select * from Test.test count_star: 1 first_seen: 1622952846 last_seen: 1622952846 sum_time: 1098 min_time: 1098 max_time: 1098 sum_rows_affected: 0 sum_rows_sent: 0
从上面的 hostgroup 和 digest_text 值来看,所有的写操作都被路由至100组,读操作都被路由至200组,其中100组为写组,200组为读组!