共计 13729 个字符,预计需要花费 35 分钟才能阅读完成。
MySQL 读写分离(ProxySQL)
读写分离原理
读写分离就是用户在发送请求时,请求经过中间件,中间件将请求中的读和写操作分辨出来将读请求发送给后端的从服务器,将写请求发送给后端的主服务器,再又主服务器通过主从复制将数据复制给其他从服务器
常见 MySQL 中间件
名称 | 公司 | 站点地址 |
---|---|---|
mysql-proxy | Oracle | https://downloads.mysql.com/archives/proxy |
Atlas | Qihoo | https://github.com/Qihoo360/Atlas/blob/master/README_ZH.md |
dbproxy | 美团 | https://github.com/Meituan-Dianping/DBProxy |
Cetus | 网易乐得 | https://github.com/Lede-Inc/cetus |
Amoeba | https://sourceforge.net/projects/amoeba/ | |
Cobar | 阿里巴巴 | Amoeba 的升级版 |
Mycat | 基于 Cobar | http://www.mycat.io |
ProxySQL | https://proxysql.com/ |
本文以 ProxySQL 为例来介绍读写分离的使用方法
ProxySQL 简介
ProxySQL 为 MySQL 的中间件,其有两个版本官方版和 percona 版,percona 版是基于官方版基础上修改而来。ProxySQL 是由 C ++ 语言开发,轻量级但性能优异(支持处理千亿级数据),其具有中间件所需要的绝大多数功能,如:
- 多种方式的读写分离
- 定制基于用户、基于 schema、基于语言的规则对 SQL 语句进行路由
- 缓存查询结果
- 后端节点的控制
…
官方站点:https://proxysql.com/
官方手册:https://github.com/sysown/proxysql/wiki
ProxySQL 安装后生成的文件
/etc/init.d/proxysql #此为服务脚本存放在 init.d 目录下所以需要使用 service 命令去启动他
/etc/proxysql.cnf
/usr/bin/proxysql
/usr/share/proxysql/tools/proxysql_galera_checker.sh
/usr/share/proxysql/tools/proxysql_galera_writer.pl
ProxySQL 所使用的端口
ProxySQL 所使用的端口为 6032 和 6033
6032: 用来配置 ProxySQL,是个管理接口
6033: 用来被远程用户连接端口
ProxySQL 内置数据库
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.00 sec)
以上这些库中主要配置的库为 main 库,里面存放了 ProxySQL 的各种配置。
ProxySQL main 库内的表
MySQL [(none)]> show tables;
+--------------------------------------------+
| tables |
+--------------------------------------------+
| global_variables |
| mysql_collations |
| mysql_group_replication_hostgroups |
| mysql_query_rules |
| mysql_query_rules_fast_routing |
| mysql_replication_hostgroups |
| mysql_servers |
| mysql_users |
| proxysql_servers |
| runtime_checksums_values |
| runtime_global_variables |
| 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_scheduler |
| scheduler |
+--------------------------------------------+
20 rows in set (0.00 sec)
main 库中的表分为 runtime 开头和非 runtime 开头
runtime 开头为运行时的设置
非 runtime 开头为需要设置的配置
所有的配置修改后需要执行命令才能加载到 runtime 生效
LOAD ... TO RUNTIME
所有的配置修改后需要执行命令才能永久保存
SAVE ... TO DISK
日志查看
查看 read_only 和 replication_log 的监控日志
MySQL [(none)]> select * from mysql_server_read_only_log;
Empty set (0.00 sec)
MySQL [(none)]> select * from mysql_server_replication_lag_log;
Empty set (0.00 sec)
ProxySQL 实现读写分离
ProxySQL 在实现读写分离之前先要实现主从复制的共功能
本实验总计使用 4 台主机,详细配置如下
主机 | ip 地址 |
---|---|
Client | 192.168.73.113 |
ProxySQL | 192.168.73.112 |
Master | 192.168.73.110 |
Slave | 192.168.73.111 |
注意事项:在实现主从复制时从节点在配置文件中必须要设置 read_only,这是 ProxySQL 区分是用来作为读服务器还是写服务器的依据
一、实现主从复制
主节点配置
1. 修改配置文件
[root@Master ~]# vim /etc/my.cnf
[mysqld]
server-id=1
log-bin
binlog-format=row
2. 启动 MySQL 服务
[root@Master ~]# systemctl start mariadb
[root@Master ~]# mysql -e "SHOW MASTER LOGS;"
+--------------------+-----------+
| Log_name | File_size |
+--------------------+-----------+
| mariadb-bin.000001 | 245 |
+--------------------+-----------+
3. 创建用来复制的账号
[root@Master ~]# mysql -e "GRANT REPLICATION SLAVE ON *.* TO'repluser'@'192.168.73.%'IDENTIFIED BY'CentOS';"
从节点配置
1. 修改配置文件
[root@Slave ~]# vim /etc/my.cnf
[mysqld]
server-id=2
log-bin
binlog-format=row
read-only #必须写
2. 启动数据库服务
[root@Slave ~]# systemctl start mariadb
3. 写入 CHANGE MASTSER TO 信息
MariaDB [(none)]> CHANGE MASTER TO MASTER_HOST='192.168.73.110', MASTER_USER='repluser',MASTER_PASSWORD='centos',MASTER_PORT=3306,MASTER_LOG_FILE='mariadb-bin.000001',MASTER_LOG_POS=245;
Query OK, 0 rows affected (0.00 sec)
4. 启动复制线程
MariaDB [(none)]> START SLAVE;
Query OK, 0 rows affected (0.00 sec)
5. 查看状态
MariaDB [(none)]> SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.73.110
Master_User: repluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mariadb-bin.000001
Read_Master_Log_Pos: 402
Relay_Log_File: mariadb-relay-bin.000002
Relay_Log_Pos: 688
Relay_Master_Log_File: mariadb-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
测试
1. 主节点导入数据库
[root@Master ~]# mysql < hellodb_innodb.sql
[root@Master ~]# mysql -e "SHOW DATABASES;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| hellodb |
| mysql |
| performance_schema |
| test |
+--------------------+
2. 从节点查看
[root@Slave ~]# mysql -e "SHOW DATABASES;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| hellodb |
| mysql |
| performance_schema |
| test |
+--------------------+
主从复制配置完毕
二、在 ProxySQL 上配置读写分离
1. 在 ProxySQL 主机上配置 yum 源
[root@ProxySQL ~]# vim /etc/yum.repos.d/proxysql.repo
[proxysql_repo]
name= ProxySQL YUM repository
baseurl=http://repo.proxysql.com/ProxySQL/proxysql-1.4.x/centos/\$releasever
gpgcheck=1
gpgkey=http://repo.proxysql.com/ProxySQL/repo_pub_key
2. 安装 ProxySQL 和 mariadb 客户端
ProxySQL 内置了一个轻量级的数据库,所以需要有 MySQL 客户端连上去对其进行配置
[root@ProxySQL ~]# yum install proxysql mariadb -y
3. 启动 ProxySQL 服务
[root@ProxySQL ~]# service proxysql start
Starting ProxySQL: 2019-05-08 14:03:07 [INFO] Using config file /etc/proxysql.cnf
DONE!
4. 连接管理端口
[root@ProxySQL ~]# mysql -uadmin -padmin -P6032 -h127.0.0.1
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.
5. 将 MySQL 主从服务器信息添加入 mysql_servers 表中
先将主从服务器存放在同一组内,等指定好读写规则后,系统会根据配置文件中的 read-only 值自动将其分别添加至读组和写组。
MySQL [(none)]> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (10,'192.168.73.110',3306);
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (10,'192.168.73.111',3306);
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> SELECT * FROM mysql_servers
-> ;
+--------------+----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 10 | 192.168.73.110 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 10 | 192.168.73.111 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
+--------------+----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
2 rows in set (0.00 sec)
6. 在 MySQL 服务器的主节点上为 ProxySQL 添加账号用来查看 MySQL 节点是主还是从
[root@Master ~]# mysql -e "GRANT REPLICATION SLAVE ON *.* TO'monitor'@'192.168.73.%'IDENTIFIED BY'centos';"
7. 在 Proxy 上配置监控账号
MySQL [(none)]> SET mysql-monitor_username='monitor';
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> SET mysql-monitor_password='centos';
Query OK, 1 row affected (0.00 sec)
8. 将配置加载至内存,将配置保存至磁盘
MySQL [(none)]> LOAD MYSQL VARIABLES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
MySQL [(none)]> SAVE MYSQL VARIABLES TO DISK;
Query OK, 97 rows affected (0.00 sec)
9. 测试
9.1 查看连接状态
MySQL [(none)]> select * from mysql_server_connect_log;
+----------------+------+------------------+-------------------------+-------------------------------------------------------------------------+
| hostname | port | time_start_us | connect_success_time_us | connect_error |
+----------------+------+------------------+-------------------------+-------------------------------------------------------------------------+
| 192.168.73.110 | 3306 | 1557296528658352 | 0 | Access denied for user 'monitor'@'192.168.73.112' (using password: YES) |
| 192.168.73.111 | 3306 | 1557296648056186 | 0 | Access denied for user 'monitor'@'192.168.73.112' (using password: YES) |
| 192.168.73.110 | 3306 | 1557296649025169 | 0 | Access denied for user 'monitor'@'192.168.73.112' (using password: YES) |
| 192.168.73.110 | 3306 | 1557296708057600 | 0 | Access denied for user 'monitor'@'192.168.73.112' (using password: YES) |
| 192.168.73.111 | 3306 | 1557296708872496 | 0 | Access denied for user 'monitor'@'192.168.73.112' (using password: YES) |
| 192.168.73.110 | 3306 | 1557296758752550 | 2763 | NULL | #此前由于没有创建监控账号所以连接一直失败
| 192.168.73.111 | 3306 | 1557296759862679 | 3205 | NULL |
| 192.168.73.110 | 3306 | 1557296818752346 | 1014 | NULL |
| 192.168.73.111 | 3306 | 1557296819498108 | 3120 | NULL |
| 192.168.73.110 | 3306 | 1557296878752978 | 3245 | NULL |
| 192.168.73.111 | 3306 | 1557296879410404 | 3063 | NULL |
+----------------+------+------------------+-------------------------+-------------------------------------------------------------------------+
22 rows in set (0.00 sec)
9.2 测试连接 ping
MySQL [(none)]> select * from mysql_server_ping_log;
+----------------+------+------------------+----------------------+-------------------------------------------------------------------------+
| hostname | port | time_start_us | ping_success_time_us | ping_error |
+----------------+------+------------------+----------------------+-------------------------------------------------------------------------+
| 192.168.73.111 | 3306 | 1557296508118738 | 0 | Access denied for user 'monitor'@'192.168.73.112' (using password: YES) |
| 192.168.73.110 | 3306 | 1557296508302837 | 0 | Access denied for user 'monitor'@'192.168.73.112' (using password: YES) |
... 中间省略...
| 192.168.73.110 | 3306 | 1557297088874658 | 675 | NULL |
| 192.168.73.111 | 3306 | 1557297089037256 | 435 | NULL |
| 192.168.73.110 | 3306 | 1557297098875954 | 1144 | NULL |
| 192.168.73.111 | 3306 | 1557297099069333 | 1252 | NULL |
+----------------+------+------------------+----------------------+-------------------------------------------------------------------------+
122 rows in set (0.00 sec)
#已经可以联通
10. 设置读写分组
MySQL [(none)]> INSERT INTO mysql_replication_hostgroups VALUES(10,20,"test");
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> SELECT * FROM mysql_replication_hostgroups;
+------------------+------------------+---------+
| writer_hostgroup | reader_hostgroup | comment |
+------------------+------------------+---------+
| 10 | 20 | test |
+------------------+------------------+---------+
1 row in set (0.00 sec)
11. 让读写表生效
MySQL [(none)]> LOAD MYSQL SERVERS TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
12. 查看 mysql_server 表此时已经将服务器分组
MySQL [(none)]> SELECT * FROM mysql_servers;
+--------------+----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 10 | 192.168.73.110 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
| 20 | 192.168.73.111 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | |
+--------------+----------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
2 rows in set (0.00 sec)
13. 保存配置至磁盘
MySQL [(none)]> SAVE MYSQL SERVERS TO DISK;
Query OK, 0 rows affected (0.02 sec)
至此读写分离配置完毕,接下来需要定义读写分离的规则
三、定义读写分离规则
1. 在主节点上创建一个账户让客户端连接调度器去访问主从服务器(此处授予的权限较大,实际生产中可以根据需要定义指定的那张表)
[root@Master ~]# mysql -e "GRANT ALL ON *.* TO'sqluser'@'192.168.73.%'IDENTIFIED BY'centos';"
2. 在 ProxySQL 服务器上,将 sqluser 用户添加至 mysql_users 表中
MySQL [(none)]> INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('sqluser','centos',10);
Query OK, 1 row affected (0.00 sec)
3. 查看 mysql_user 表信息
MySQL [(none)]> SELECT * FROM mysql_users;
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
| username | password | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections |
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
| sqluser | centos | 1 | 0 | 10 | NULL | 0 | 1 | 0 | 1 | 1 | 10000 |
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
1 row in set (0.00 sec)
4. 生效存盘
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.00 sec)
5. 测试
目前尚未设置读写路由规则,所有的请求都是发往主节点
[root@Client ~]# mysql -usqluser -pcentos -h192.168.73.112 -P6033 -e "SELECT @@server_id;"
+-------------+
| @@server_id |
+-------------+
| 1 |
+-------------+
6. 在 ProxySQL 上定义调度规则
MySQL [(none)]> INSERT INTO mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply) VALUES (1,1,'^SELECT.*FOR UPDATE$',10,1),(2,1,'^SELECT',20,1);
Query OK, 2 rows affected (0.00 sec)
7. 查看定义规则
MySQL [(none)]> SELECT * FROM mysql_query_rules\G;
*************************** 1. row ***************************
rule_id: 1
active: 1
username: NULL
schemaname: NULL
flagIN: 0
client_addr: NULL
proxy_addr: NULL
proxy_port: NULL
digest: NULL
match_digest: ^SELECT.*FOR UPDATE$
match_pattern: NULL
negate_match_pattern: 0
re_modifiers: CASELESS
flagOUT: NULL
replace_pattern: NULL
destination_hostgroup: 10
cache_ttl: NULL
reconnect: NULL
timeout: NULL
retries: NULL
delay: NULL
next_query_flagIN: NULL
mirror_flagOUT: NULL
mirror_hostgroup: NULL
error_msg: NULL
OK_msg: NULL
sticky_conn: NULL
multiplex: NULL
log: NULL
apply: 1
comment: NULL
*************************** 2. row ***************************
rule_id: 2
active: 1
username: NULL
schemaname: NULL
flagIN: 0
client_addr: NULL
proxy_addr: NULL
proxy_port: NULL
digest: NULL
match_digest: ^SELECT
match_pattern: NULL
negate_match_pattern: 0
re_modifiers: CASELESS
flagOUT: NULL
replace_pattern: NULL
destination_hostgroup: 20
cache_ttl: NULL
reconnect: NULL
timeout: NULL
retries: NULL
delay: NULL
next_query_flagIN: NULL
mirror_flagOUT: NULL
mirror_hostgroup: NULL
error_msg: NULL
OK_msg: NULL
sticky_conn: NULL
multiplex: NULL
log: NULL
apply: 1
comment: NULL
2 rows in set (0.00 sec)
8. 生效存盘
MySQL [(none)]> LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
MySQL [(none)]> SAVE MYSQL QUERY RULES TO DISK;
Query OK, 0 rows affected (0.00 sec)
四、在 Client 端测试
1. 查询操作
[root@Client ~]# mysql -usqluser -pcentos -h192.168.73.112 -P6033 -e "SELECT @@server_id;"
+-------------+
| @@server_id |
+-------------+
| 2 |
+-------------+
2. 写操作
[root@Client ~]# mysql -usqluser -pcentos -h192.168.73.112 -P6033 -e "BEGIN;INSERT hellodb.teachers VALUE(5,'Long',30,'M');SELECT @@server_id;commit;"
+-------------+
| @@server_id |
+-------------+
| 1 |
+-------------+
: