ProxySQL实现mysql8主从同步读写分离

一、ProxySQL基本介绍

ProxySQL是 MySQL 的高性能、高可用性、协议感知代理。 简单介绍下ProxySQL及其功能和配置,主要包括:

  • 最基本的读/写分离,且方式有多种;
  • 可定制基于用户、基于schema、基于语句的规则对SQL语句进行路由,规则很灵活;
  • 动态加载配置,即绝大部分的配置可以在线修改,但有少部分参数还是需要重启来生效;
  • 可缓存查询结果。虽然缓存策略比较简陋,但实现了基本的缓存功能;
  • 过滤危险的SQL,增加防火墙等功能;
  • 提供连接池、日志记录、审计日志等功能;

(1)请求流程

流量从客户端发出 → ProxySQL进行处理转发 → 后端处理 → ProxySQL的前端连接 → 返回客户端的基本流程

(2)核心功能

**读写分离:**可查询走从库,写入走主库。

**简单Sharding:**ProxySQL的sharding是通过正则匹配来实现的,对于需要拆分SQL以及合并SQL执行结果的不能支持,所以写了简单sharding。

**连接池管理:**常规功能,为了提高SQL执行效率。

**多路复用:**主要优化点在后端mysql连接的复用,对比smart client,中间层不仅对前端建连也会对后端建连,可自行控制后端连接的复用逻辑。

**流量管控:**kill连接和kill query;whitelist配置。

**高可用:**底层mysql,如果从库挂了,自动摘除流量;主库挂了暂不处理。proxysql自身高可用,提供cluster的功能,cluster内部会自行同步元数据以及配置变更信息。

**查询缓存:**对username+schema+query的key进行缓存,设置ttl过期,不适合写完就查的场景,因为在数据在未过期之前可能是脏数据。

**动态配置:**大部分的配置可动态变更,先load到runtime,在save到disk,通过cluster的功能同步到其他的节点。

**流量镜像:**同一份流量可以多出写入,但是并不保证mirror的流量一定成功。

**SQL改写:**在query rules中配置replace规则,可以对指定的SQL进行改写。

当ProxySQL启动后,将监听两个端口:

  1. admin管理接口,默认端口为6032。该端口用于查看、配置ProxySQL。
  2. 接收SQL语句的接口,默认端口为6033,这个接口类似于MySQL的3306端口。

ProxySQL的admin管理接口是一个使用MySQL协议的接口,所以,可以直接使用mysql客户端、navicat等工具去连接这个管理接口。

二、ProxySQL结构

**Qurey Processor:**用于匹配查询规则并根据规则决定是否缓存查询或者将查询加入黑名单或者重新路由、重写查询或者镜像查询到其他hostgroup。

User Auth:为底层后端数据库认证提供了用户凭证。

**Hostgroup manager:**负责管理发送SQL请求都后端数据库并跟踪SQL请求状态。

**Connection pool:**负责管理后端数据库连接,连接池中建立的连接被所有的前端应用程序共享。

**Monitoring:**负责监控后端数据库健康状态主从复制延时并临时下线不正常的数据库实例。

(1)启动过程

**RUNTIME层:**代表的是ProxySQL当前生效的配置,包括 global_variables, mysql_servers, mysql_users,mysql_query_rules。无法直接修改这里的配置,必须要从下一层load进来。

**MEMORY层:**是平时在mysql命令行修改的 main 里头配置,可以认为是SQLite数据库在内存的镜像。该层级的配置在main库中以mysql_开头的表以及global_variables表,这些表的数据可以直接修改。

DISK|CONFIG FILR层: 持久存储的那份配置,一般在$(DATADIR)/proxysql.db,在重启的时候会从硬盘里加载。 /etc/proxysql.cnf文件只在第一次初始化的时候用到,完了后,如果要修改监听端口,还是需要在管理命令行里修改,再 save 到硬盘。

(2)数据库结构

ProxySQL自身共有5个库,分别为3个保存在内存中的库,和2个保存在磁盘的SQLite库。 通过6032管理端口登入后,默认就是main库,所有的配置更改都必须在这个库中进行,disk存档库不会直接受到影响。

复制代码
# mysql -uadmin -padmin -h127.0.0.1 -P6032
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.5.30 (ProxySQL Admin Module)
mysql> 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 |
+-----+---------------+-------------------------------------+
5 rows in set (0.00 sec)

main: 内存配置数据库,表里存放后端db实例、用户验证、路由规则等信息。表名以 runtime开头的表示proxysql当前运行的配置内容,不能通过dml语句修改,只能修改对应的不以 runtime 开头的(在内存)里的表,然后 LOAD 使其生效, SAVE 使其存到硬盘以供下次重启加载。

**disk:**是持久化到硬盘的配置,sqlite数据文件。SQLite3 数据库,默认位置为 $(DATADIR)/proxysql.db,在重新启动时,未保留的内存中配置将丢失。因此,将配置保留在 DISK 中非常重要。(SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎)

**stats:**proxysql运行抓取的统计信息,包括到后端各命令的执行次数、流量、processlist、查询种类汇总/执行时间等等。

**monitor:**库存储 monitor 模块收集的信息,主要是对后端db的健康/延迟检查。

**stats_history:**统计信息历史库。

(3)核心配置表

1.mysql_servers

2.mysql_users

3.mysql_replication_hostgroups

4.mysql_group_replication_hostgroups(MGR)

5.mysql_galera_hostgroups(PXC)

6.mysql_query_rules

(4)命令

(5)小结

这些数据库的功能实现了实用化内容:

  • 允许轻松动态更新配置,便于运维管理,与MySQL兼容的管理界面可用于此目的。

  • 允许尽可能多的配置项目动态修改,而不需要重新启动ProxySQL进程

  • 可以毫不费力地回滚无效配置

  • 通过多级配置系统实现的,其中设置从运行时移到内存,并根据需要持久保存到磁盘

三、实验:实现数据库主从复制

1、实验环境

机器名称 IP配置 服务角色 备注
proxy 192.168.121.51 proxysql控制器 用于监控管理
master 192.168.121.11 数据库主服务器
slave1 192.168.121.12 数据库从服务器
slave2 192.168.121.13 数据库从服务器

实验前提:基于GTID实现mysql8.0主从同步。

2、安装ProxySQL

(1)安装ProxySQL

复制代码
[root@localhost ~]# wget -c https://repo.proxysql.com/ProxySQL/proxysql-2.7.x/centos/8/proxysql-2.7.0-1-centos8.x86_64.rpm

[root@localhost ~]# dnf install proxysql-2.7.0-1-centos8.x86_64.rpm -y

(2)启动ProxySQL

复制代码
[root@localhost ~]# systemctl enable --now proxysql

(3)管理员登录

复制代码
[root@localhost ~]# mysql -uadmin -padmin -h 127.0.0.1 -P 6032


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 |
+-----+---------------+-------------------------------------+
5 rows in set (0.000 sec)

# 可见有五个库: main、disk、stats、monitor 和 stats_history
# main: 内存配置数据库,即 MEMORY,表里存放后端 db 实例、用户验证、路由规则等信息。
# main 库中有如下信息:

MySQL [(none)]> show tables from main;
+----------------------------------------------------+
| tables                                             |
+----------------------------------------------------+
| coredump_filters                                   |
| 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_hostgroup_attributes                         |
| mysql_query_rules                                  |
| mysql_query_rules_fast_routing                     |
| mysql_replication_hostgroups                       |
| mysql_servers                                      |
| mysql_servers_ssl_params                           |
| mysql_users                                        |
| proxysql_servers                                   |
| restapi_routes                                     |
| runtime_checksums_values                           |
| runtime_coredump_filters                           |
| 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_hostgroup_attributes                 |
| runtime_mysql_query_rules                          |
| runtime_mysql_query_rules_fast_routing             |
| runtime_mysql_replication_hostgroups               |
| runtime_mysql_servers                              |
| runtime_mysql_servers_ssl_params                   |
| runtime_mysql_users                                |
| runtime_proxysql_servers                           |
| runtime_restapi_routes                             |
| runtime_scheduler                                  |
| scheduler                                          |
+----------------------------------------------------+
38 rows in set (0.000 sec)

# 库下的主要表:
# mysql_servers: 后端可以连接 MySQL 服务器的列表
# mysql_users: 配置后端数据库的账号和监控的账号。
# mysql_query_rules: 指定 Query 路由到后端不同服务器的规则列表。

# 注: 表名以 runtime_开头的表示 ProxySQL 当前运行的配置内容,不能通过 DML 语句修改。
# 只能修改对应的不以 runtime 开头的表,然后 "LOAD" 使其生效,"SAVE" 使其存到硬盘以供下次重启加载。

# disk :持久化的磁盘的配置
# stats: 统计信息的汇总
# monitor:一些监控的收集信息,比如数据库的健康状态等
# stats_history: 这个库是 ProxySQL 收集的有关其内部功能的历史指标

3、配置ProxySQL

使用proxysql,主要需要完成以下几项内容的配置:

1.配置监控账号。监控账号用于检测后端mysql实例是否健康(是否能连接、复制是否正常、复制是否有延迟等)。

2.到后端mysql实例创建监控账号。

3.配置后端mysql实例连接信息。实例连接信息存储在mysql_servers表。

4.配置连接proxysql和后端实例的账号。账号信息存储在mysql_users表。

5.配置查询路由信息。路由信息存储在mysql_query_rules表。

6.配置后端mysql集群信息。根据后端mysql集群架构,配置分别存储在mysql_replication_hostgroups、mysql_group_replication_hostgroups、runtime_mysql_galera_hostgroups、runtime_mysql_aws_aurora_hostgroups等表中。

7.根据具体需要,调优相关参数。参数存储在global_variables表。

(1)编辑主库、从库的配置文件

在 Master 的配置文件添加 read_only=0;在 Slave 的配置文件添加 read_only=1。

复制代码
[root@openEuler-1 ~]# vim /etc/my.cnf
read_only=0
[root@openEuler-1 ~]# systemctl restart mysqld

[root@openEuler-2 ~]# vim /etc/my.cnf
read_only=1
[root@openEuler-2 ~]# systemctl restart mysqld

[root@openEuler-3 ~]# vim /etc/my.cnf
read_only=1
[root@openEuler-3 ~]# systemctl restart mysqld

(2)配置ProxySQL所需账户

在 Master 的MySQL 上创建 ProxySQL 的监控账户和对外访问账户

复制代码
# 监控账户:
mysql> create user 'monitor'@'192.168.121.%' identified with mysql_native_password b
y 'Monitor@123.com';
mysql> grant all privileges on *.* to 'monitor'@'192.168.121.%' with grant option;


# 对外访问账户:
mysql> create user 'proxysql'@'192.168.121.%' identified with mysql_native_password
by '123456';
mysql> grant all privileges on *.* to 'proxysql'@'192.168.121.%' with grant option;

(3)配置ProxySQL主从分组信息

复制代码
# 1、查看mysql_replication_hostgroup表结构如下:
MySQL [(none)]> show create table mysql_replication_hostgroups \G;
*************************** 1. row ***************************
       table: mysql_replication_hostgroups
Create Table: CREATE TABLE mysql_replication_hostgroups (
    writer_hostgroup INT CHECK (writer_hostgroup>=0) NOT NULL PRIMARY KEY,
    reader_hostgroup INT NOT NULL CHECK (reader_hostgroup<>writer_hostgroup AND reader_hostgroup>=0),
    check_type VARCHAR CHECK (LOWER(check_type) IN ('read_only','innodb_read_only','super_read_only','read_only|innodb_read_only','read_only&innodb_read_only')) NOT NULL DEFAULT 'read_only',
    comment VARCHAR NOT NULL DEFAULT '', UNIQUE (reader_hostgroup))
1 row in set (0.000 sec)

# 2、创建组:(定义写为1,读为0)
MySQL [(none)]> insert into mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup,comment) values (1,0,'proxy');
MySQL [(none)]> load mysql servers to runtime;
MySQL [(none)]> save mysql servers to disk;

# 注意:ProxySQL会根据server的read_only的取值将服务器进行分组。read_only=0的server,master被分到编号为1的写组,read_only=1的server,slave则分到编号为0的读组

# 3、添加主从服务器节点
MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port) values (1,'192.168.121.11',3306);

MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port) values (0,'192.168.121.12',3306);

MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port) values (0,'192.168.121.13',3306);

MySQL [(none)]> load mysql servers to runtime;
MySQL [(none)]> save mysql servers to disk;

(4)为ProxySQL监控MySQL后端节点

复制代码
MySQL [(none)]> use monitor

MySQL [monitor]> set mysql-monitor_username='monitor';

MySQL [monitor]> set mysql-monitor_password='Monitor@123.com';

MySQL [monitor]> load mysql variables to runtime;
MySQL [monitor]> save mysql variables to disk;

(5)验证监控信息

复制代码
MySQL [(none)]> select * from mysql_server_ping_log;

(6)查看read_only日志监控

复制代码
MySQL [(none)]> select * from mysql_server_read_only_log;

(7)将对外访问账号添加到mysql_users表中

复制代码
MySQL [(none)]> insert into mysql_users (username,password,default_hostgroup,transaction_persistent) values ('proxysql','123456',1,1);

MySQL [(none)]> load mysql users to runtime;
MySQL [(none)]> save mysql users to disk;

MySQL [(none)]> select * from mysql_users\G
*************************** 1. row ***************************
              username: proxysql
              password: 123456
                active: 1
               use_ssl: 0
     default_hostgroup: 1
        default_schema: NULL
         schema_locked: 0
transaction_persistent: 1
          fast_forward: 0
               backend: 1
              frontend: 1
       max_connections: 10000
            attributes:
               comment:
1 row in set (0.000 sec)


# 在从库端上通过对方访问账户proxy连接,测试是否路由能默认到hostgroup_id=1(写组)
[root@openEuler-2 ~]# mysql -uproxysql -p123456 -h 192.168.121.51 -P 6033

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| db1                |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.04 sec)

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          11 |
+-------------+
1 row in set (0.00 sec)

mysql> create database gigigi;
Query OK, 1 row affected (0.02 sec)

# 在另外一个从库上查询验证是否建立成功

(8)添加读写分离规则(mysql_query_rules)

proxysql支持正则,这里添加两条匹配规则: 1) 表示像select * from xxx for update这种语句都会分到到写组,2)表示像select这种语句都会被分配到读组。

复制代码
MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) values(1,1,'^select .* for update$',1,1);

MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) values(2,1,'^select',0,1);

MySQL [(none)]> load mysql query rules to runtime;
MySQL [(none)]> save mysql query rules to disk;

4、测试读写分离

复制代码
[root@openEuler-2 ~]# mysql -uproxysql -p123456 -h 192.168.121.51 -P 6033 -e "select @@server_id"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@server_id |
+-------------+
|          12 |
+-------------+
[root@openEuler-2 ~]# mysql -uproxysql -p123456 -h 192.168.121.51 -P 6033 -e "select @@server_id"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@server_id |
+-------------+
|          13 |
+-------------+
[root@openEuler-2 ~]# mysql -uproxysql -p123456 -h 192.168.121.51 -P 6033 -e "begin;select @@server_id commit;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------+
| commit |
+--------+
|     11 |
+--------+
相关推荐
IvorySQL10 分钟前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·20 分钟前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德23 分钟前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫1 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i1 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.1 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn1 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露2 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星2 小时前
sql语言之分组语句group by
java·数据库·sql
符哥20082 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql