通过Proxysql实现mysql的读写分离、负载均衡

一、Proxysql(Centos7)下载安装


1. 下载

github proxysql-2.0.10-1-centos7.x86_64.rpm
阿里云 proxysql-2.0.10-1-centos7.x86_64.rpm

2. 把下载好的rpm文件移动到主目录(home)

3. 打开命令终端窗口

[root@mn home]# rpm -i proxysql-2.0.10-1-centos7.x86_64.rpm 
# 警告:群组proxysql 不存在 - 使用root
# 警告:群组proxysql 不存在 - 使用root
# Created symlink from /etc/systemd/system/multi-user.target.wants/proxysql.service to /etc/systemd/system/proxysql.service.
#设置proxysql服务开机自启
systemctl enable proxysql
#取消proxysql服务开机自启
systemctl disable proxysql
# 查询proxysql是否开机启动 enabled 代表开机自启
systemctl list-unit-files | grep proxy
# proxysql.service             enabled 

4.安装依赖包

 # 错误:依赖检测失败:
      /usr/bin/perl 被 proxysql-2.0.10-1.x86_64 需要
            libgnutls.so.28()(64bit) 被 proxysql-2.0.10-            1.x86_64 需要
            libgnutls.so.28(GNUTLS_1_4)(64bit) 被 proxysql-        2.0.10-1.x86_64 需要
            libgnutls.so.28(GNUTLS_3_0_0)(64bit) 被 proxysql-    2.0.10-1.x86_64 需要
            libgnutls.so.28(GNUTLS_3_1_0)(64bit) 被 proxysql-    2.0.10-1.x86_64 需要
            perl(DBD::mysql) 被 proxysql-2.0.10-1.x86_64 需要
            perl(DBI) 被 proxysql-2.0.10-1.x86_64 需要
            perl(constant) 被 proxysql-2.0.10-1.x86_64 需要
            perl(strict) 被 proxysql-2.0.10-1.x86_64 需要
            perl(vars) 被 proxysql-2.0.10-1.x86_64 需要
            perl(warnings) 被 proxysql-2.0.10-1.x86_64 需要

    # 安装依赖包
    yum install gnutls;
    yum install perl;
    yum install perl-DBD-MySQL;

二、开启ProxySQL,并连接


# 启动proxysql
[root@mn ~]# systemctl start proxysql
# 连接proxysql的管理
mysql -uadmin -padmin -h127.0.0.1  -P6032
# Welcome to the MariaDB monitor.  Commands end with ; or \g.
# Your MySQL connection id is 1
# 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)]> 


# 查询一下管理数据库
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.01 sec)

其中:

main库是ProxySQL最主要的库,是需要修改配置时使用的库,它其实是一个内存数据库系统。所以,修改main库中的配置后,必须将其持久化到disk上才能永久保存
disk库是磁盘数据库,该数据库结构和内存数据库完全一致。当持久化内存数据库中的配置时,其实就是写入到disk库中。磁盘数据库的默认路径为 $DATADIR/proxysql.db
stats库是统计信息库。这个库中的数据一般是在检索其内数据时临时填充的,它保存在内存中。因为没有相关的配置项,所以无需持久化
monitor库是监控后端MySQL节点相关的库,该库中只有几个log类的表,监控模块收集到的监控信息全都存放到对应的log表中
stats_history库是1.4.4版新增的库,用于存放历史统计数据。默认路径为 $DATADIR/proxysql_stats.db

由于 ProxySQL 的配置全部保存在几个自带的库中,所以通过管理接口,可以非常方便地通过发送一些SQL命令去修改 ProxySQL 的配置。 ProxySQL 会解析通过该接口发送的某些对ProxySQL 有效的特定命令,并将其合理转换后发送给内嵌的 SQLite3 数据库引擎去运行

ProxySQL 的配置几乎都是通过管理接口来操作的,通过 Admin 管理接口,可以在线修改几乎所有的配置并使其生效。只有两个变量的配置是必须重启 ProxySQL 才能生效的,它们是:
**mysql-threads 和 mysql-stacksize**

三、添加管理员帐户


admin-admin_credentials 变量控制的是admin管理接口的管理员账户。默认的管理员账户和密码为admin:admin,但是这个默认的用户只能在本地使用。如果想要远程连接到ProxySQL,例如用windows上的navicat连接Linux上的ProxySQL管理接口,必须自定义一个管理员账户。
# 查询现有的管理员账户
MySQL [(none)]> select @@admin-admin_credentials;
+---------------------------+
| @@admin-admin_credentials |
+---------------------------+
| admin:admin;              |
+---------------------------+
1 row in set (0.01 sec)
# 添加新的mn账户,此账户可以用IP远程连接,admin只能在本机
# 可以使用命令或者小绿叶连接: 
# mysql -uadmin -padmin -h127.0.0.1  -P6032

MySQL [(none)]> set admin-admin_credentials='admin:admin;mn:123456';
Query OK, 1 row affected (0.01 sec)
# 重新查询,已添加mn账户
MySQL [(none)]> select @@admin-admin_credentials;
+---------------------------+
| @@admin-admin_credentials |
+---------------------------+
| admin:admin;mn:123456     |
+---------------------------+
1 row in set (0.01 sec)
# 使修改立即生效
MySQL [(none)]> load admin variables to runtime;
Query OK, 0 rows affected (0.00 sec)
# 是修改持久化到磁盘
MySQL [(none)]> save admin variables to disk;
Query OK, 35 rows affected (0.02 sec)

# **所有的配置操作都是在修改main库中对应的表**
MySQL [(none)]> select * from global_variables where variable_name='admin-admin_credentials';
+-------------------------+-----------------------+
| variable_name           | variable_value        |
+-------------------------+-----------------------+
| admin-admin_credentials | admin:admin;mn:123456 |
+-------------------------+-----------------------+
1 row in set (0.01 sec)

必须要区分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账户权限太大,如果需要建几个普通账户)


  1. admin-stats_credentials 变量控制admin管理接口的普通用户,这个变量中的用户没有超级管理员权限,只能查看monitor库和main库中关于统计的数据,其它库都是不可见的,且没有任何写权限
    默认的普通用户名和密码均为 stats ,与admin一样,它默认也只能用于本地登录,若想让人远程查看则要添加查看的专有用户
MySQL [(none)]> select @@admin-stats_credentials;
+---------------------------+
| @@admin-stats_credentials |
+---------------------------+
| stats:stats;              |
+---------------------------+
1 row in set (0.00 sec)

MySQL [(none)]> set admin-stats_credentials='stats:stats;zhtstats:123456';
Query OK, 1 row affected (0.01 sec)

MySQL [(none)]> select @@admin-stats_credentials;
+---------------------------+
| @@admin-stats_credentials |
+---------------------------+
| stats:stats;              |
+---------------------------+
1 row in set (0.00 sec)

MySQL [(none)]> set admin-stats_credentials='stats:stats;zhtstats:123456';
Query OK, 1 row affected (0.01 sec)

MySQL [(none)]> load admin variables to runtime;
Query OK, 0 rows affected (0.00 sec)

MySQL [(none)]> save admin variables to disk;
Query OK, 35 rows affected (0.01 sec)

MySQL [(none)]> select @@admin-stats_credentials;
+-----------------------------+
| @@admin-stats_credentials   |
+-----------------------------+
| stats:stats;zhtstats:123456 |
+-----------------------------+
1 row in set (0.00 sec)

MySQL [(none)]> load admin variables to runtime;
Query OK, 0 rows affected (0.00 sec)

MySQL [(none)]> save admin variables to disk;
Query OK, 35 rows affected (0.01 sec)

############################################

# 用zhtstats账户登录,查询状态
[root@mn ~]# mysql -uzhtstats -p123456 -h192.168.16.248 -P6032
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
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 tables from main;
+--------------------------------------+
| tables                               |
+--------------------------------------+
| global_variables                     |
| stats_memory_metrics                 |
| stats_mysql_commands_counters        |
| stats_mysql_connection_pool          |
| stats_mysql_connection_pool_reset    |
| stats_mysql_errors                   |
| stats_mysql_errors_reset             |
| stats_mysql_free_connections         |
| stats_mysql_global                   |
| stats_mysql_gtid_executed            |
| stats_mysql_prepared_statements_info |
| stats_mysql_processlist              |
| stats_mysql_query_digest             |
| stats_mysql_query_digest_reset       |
| stats_mysql_query_rules              |
| stats_mysql_users                    |
| stats_proxysql_servers_checksums     |
| stats_proxysql_servers_metrics       |
| stats_proxysql_servers_status        |
+--------------------------------------+
19 rows in set (0.00 sec)


五、Proxysql管理端(端口6032)添加读写分离操作数据库的用户,此用户必须已经在数据库中添加了,并且分配了权限了


-- 先在mysql数据库(三个物理机器)中添加rp root 用户
-- 给 write read1 read2 三台数据库授权,以下语句三台数据库都要执行
-- ---------------------------------------------------------
-- 修改'root'@'localhost'用户的密码为dmjjy1710
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'dmjjy1710'; 
-- ---------------------------------------------------------
-- 授权所有用户远程访问权限
CREATE USER 'root'@'%' IDENTIFIED BY 'dmjjy1710';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
flush privileges;
-- ---------------------------------------------------------
-- 创建'rp'@'%' 用户并授权,用于主从复制
CREATE USER 'rp'@'%' IDENTIFIED BY 'dmjjy1710';
GRANT ALL PRIVILEGES ON *.* TO 'rp'@'%' WITH GRANT OPTION;
flush privileges;

-- 把已经授权的rp、root用户添加到proxysql的用户里面,这个要在管理接口 6032 中添加

insert into mysql_users(username,password,default_hostgroup,transaction_persistent)values('root','dmjjy1710',1,0);
insert into mysql_users(username,password,default_hostgroup,transaction_persistent)values('rp','dmjjy1710',1,0);
MySQL [(none)]> select * from mysql_users\G;
*************************** 1. row ***************************
              username: root        # 后端mysql实例的用户名
              password: dmjjy1710   # 后端mysql实例的密码
                active: 1           # active=1表示用户生效,0表示不生效
               use_ssl: 0
     default_hostgroup: 1           # 用户默认登录到哪个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       # 该用户允许的最大连接数
               comment: 
*************************** 2. row ***************************
              username: rp
              password: dmjjy1710
                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
               comment: 
2 rows in set (0.00 sec)

# 立即加载生效
MySQL [(none)]> load mysql users to runtime;
Query OK, 0 rows affected (0.00 sec)
# 持久化到磁盘
MySQL [(none)]> save mysql users to disk;
Query OK, 0 rows affected (0.03 sec)

-- 在master机器上创建'monitor'@'%' 用户并授权只读权限,用于监控proxysql的监控情况
CREATE USER 'monitor'@'%' IDENTIFIED BY 'monitor';
GRANT SELECT ON *.* TO 'monitor'@'%' WITH GRANT OPTION;
flush privileges;

-- 在proxysql的管理接口6032上设置monitor用户
MySQL [(none)]> set mysql-monitor_username='monitor';
Query OK, 1 row affected (0.00 sec)

MySQL [(none)]> set mysql-monitor_password='monitor';
Query OK, 1 row affected (0.00 sec)

MySQL [(none)]> load mysql variables to runtime;
Query OK, 0 rows affected (0.00 sec)

MySQL [(none)]> save mysql variables to disk;
Query OK, 143 rows affected (0.03 sec)


六、Proxysql管理端(端口6032)添加mysql主机


把读写分离的机器插入到mysql_servers表
使用 insert 语句添加 mysql 主机到 mysql_servers 表中,其中:hostgroup_id 1 表示写组,2表示读组
insert into mysql_servers(hostgroup_id,hostname,port,weight,comment)values(1,'192.168.16.253',17100,1,'Write Group');
insert into mysql_servers(hostgroup_id,hostname,port,weight,comment)values(2,'192.168.16.252',17100,1,'Read Group');
insert into mysql_servers(hostgroup_id,hostname,port,weight,comment)values(2,'192.168.16.250',17100,1,'Read Group');

# 查询mysql主机

MySQL [(none)]> select hostgroup_id,hostname,port,status,weight,max_connections,comment from mysql_servers;
+--------------+----------------+-------+--------+--------+-----------------+-------------+
| hostgroup_id | hostname       | port  | status | weight | max_connections | comment     |
+--------------+----------------+-------+--------+--------+-----------------+-------------+
| 1            | 192.168.16.253 | 17100 | ONLINE | 1      | 1000            | Write Group |
| 2            | 192.168.16.252 | 17100 | ONLINE | 1      | 1000            | Read Group  |
| 2            | 192.168.16.250 | 17100 | ONLINE | 1      | 1000            | Read Group  |
+--------------+----------------+-------+--------+--------+-----------------+-------------+
3 rows in set (0.00 sec)

MySQL [(none)]> load mysql servers to runtime;
Query OK, 0 rows affected (0.01 sec)

MySQL [(none)]> save mysql servers to disk;
Query OK, 0 rows affected (0.09 sec)

-- 设置最大连接数
-- 查询最大连接数
select @@mysql-max_connections;
-- 设置最大连接数
set mysql-max_connections=6000;
load mysql servers to runtime;
save mysql servers to disk;


七、Proxysql管理端(端口6032)添加读写分离的路由规则


delete from mysql_query_rules;
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(1000,1,'^SELECT.*FOR UPDATE$',1,1);
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (2000,1,"\/\*\s*shard_host_HG=.*Write\s*\*.",1,1);
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (3000,1,"\/\*\s*shard_host_HG=.*Read\s*\*.",2,1);
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(4000,1,'^SELECT',2,1);
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(5000,1,'^SHOW',2,1);
select rule_id,active,match_digest,match_pattern,destination_hostgroup,apply from mysql_query_rules;
    
load mysql query rules to runtime;
load admin variables to runtime;
save mysql query rules to disk;
save admin variables to disk;
MySQL [(none)]> select rule_id,active,match_digest,match_pattern,destination_hostgroup,apply from mysql_query_rules;
+---------+--------+----------------------+------------------------------------+-----------------------+-------+
| rule_id | active | match_digest         | match_pattern                      | destination_hostgroup | apply |
+---------+--------+----------------------+------------------------------------+-----------------------+-------+
| 1       | 1      | ^SELECT.*FOR UPDATE$ | NULL                               | 1                     | 1     |
| 2       | 1      | NULL                 | \/\*\s*shard_host_HG=.*Write\s*\*. | 1                     | 1     |
| 3       | 1      | NULL                 | \/\*\s*shard_host_HG=.*Read\s*\*.  | 2                     | 1     |
| 4       | 1      | ^SELECT              | NULL                               | 2                     | 1     |
| 5       | 1      | ^SHOW                | NULL                               | 2                     | 1     |
+---------+--------+----------------------+------------------------------------+-----------------------+-------+


八、Proxysql验证读写分离是否生效


-- proxysql有个类似审计的功能,可以查看各类SQL的执行情况,其需要在proxysql管理端执行 

select *  from stats_mysql_query_digest;

 -- 查询语句可以加hostgroup过滤  或者 sql 关键字like过滤 ,查一下走的是哪个节点


九、排错


ProxySQL 排错 Max connect timeout reached while reaching hostgroup 10 after 10000ms

检查proxysql.log 日志 /var/lib/proxysql/proxysql.log
monitor_ping(): [ERROR] Server 
解决方案:
① 检查ping命令是否通,在不同的windows机器上执行
    netsh firewall set icmpsetting 8  命令开启ping功能
② 检查数据库端口防火墙是否开放
③ 在各个mysql数据库中my.ini[mysqld]节点下加上 skip_name_resolve=1

十、部署SQL汇总

-- mysql -admin -padmin -P6032 -h127.0.0.1 进入mysql,执行下面的sql
-- 添加新的mn(admin管理)、zhtstats(状态查询,权限低)账户,此账户可以用IP远程连接,admin、stats只能在本机
-- 可以使用命令或者小绿叶连接: 
set admin-admin_credentials='admin:admin;mn:123456';
set admin-stats_credentials='stats:stats;zhtstats:123456';
-- 使修改立即生效
load admin variables to runtime;
-- 持久化到磁盘
save admin variables to disk;

-- 插入mysql用户,此用户已经在mysql的各数据库添加并分配了权限
-- 修改'root'@'localhost'用户的密码为dmjjy1710
-- ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'dmjjy1710'; 
-- ---------------------------------------------------------
-- 授权所有用户远程访问权限
-- CREATE USER 'root'@'%' IDENTIFIED BY 'dmjjy1710';
-- GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
-- flush privileges;
insert into mysql_users(username,password,default_hostgroup,transaction_persistent)values('root','dmjjy1710',1,0);
insert into mysql_users(username,password,default_hostgroup,transaction_persistent)values('rp','dmjjy1710',1,0);
load mysql users to runtime;
save mysql users to disk;

-- 设置最大连接数
-- 查询最大连接数
select @@mysql-max_connections;
-- 设置最大连接数
set mysql-max_connections=6000;
load mysql servers to runtime;
save mysql servers to disk;

-- 设置monitor用户,此用户已经在mysql的写数据库添加并分配了只读权限,可以不配置

 CREATE USER 'monitor'@'%' IDENTIFIED BY 'monitor';
 GRANT SELECT ON *.* TO 'monitor'@'%' WITH GRANT OPTION;
 flush privileges;

 set mysql-monitor_username='monitor';
 set mysql-monitor_password='monitor';
 load mysql variables to runtime;
 save mysql variables to disk;

-- 把读写分离的机器插入到mysql_servers表,其中:hostgroup_id 1 表示写组,2表示读组
insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,comment)values(1,'192.168.16.253',17100,1,2000,'Write Group');
insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,comment)values(2,'192.168.16.252',17100,1,2000,'Read Group');
insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,comment)values(2,'192.168.16.250',17100,1,2000,'Read Group');
select hostgroup_id,hostname,port,status,weight,max_connections,comment from mysql_servers;
load mysql servers to runtime;
save mysql servers to disk;

-- 添加读写分离的路由规则
delete from mysql_query_rules;
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(1,1,'^SELECT.*FOR UPDATE$',1,1);
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (2,1,"\/\*\s*shard_host_HG=.*Write\s*\*.",1,1);
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (3,1,"\/\*\s*shard_host_HG=.*Read\s*\*.",2,1);
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(4,1,'^SELECT',2,1);
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(5,1,'^SHOW',2,1);
select rule_id,active,match_digest,match_pattern,destination_hostgroup,apply from mysql_query_rules;
load mysql query rules to runtime;
load admin variables to runtime;
save mysql query rules to disk;
save admin variables to disk;

-- 验证是否成功  
-- proxysql有个类似审计的功能,可以查看各类SQL的执行情况,其需要在proxysql管理端执行 
select *  from stats_mysql_query_digest;
-- 查询语句可以加hostgroup过滤  或者 sql 关键字like过滤 ,查一下走的是哪个节点

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×