墨墨导读:ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎。
ProxySQL提供强大的路由规则。当应用程序自身不支持读写分离时,DBA可以通过配置路由规则为应用程序提供透明的读写分离,使用Keepalived + ProxySQL + Orchestrator为主从提供高可用时,能够有效的避免keepalived + 双主结构 由于keepalived脑裂而造成数据被写错乱的痛点。
介绍
ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎。具有以下特性:
模块
ProxySQL组成模块
安装
/etc/init.d/proxysql start
配置结构
ProxySQL配置可以存储到SQLite数据库并通过SQL语句来管理配置,并通过如下三层配置来管理ProxySQL。
使用ProxySQL修改配置时,可以通过SQL语句直接修改Memory中的配置,然后使用load命令将Memory中的配置加载的到runtime层来验证配置是否正确,如果验证通过可以通过save将配置保存到SQLite数据库中,如果验证不通过也可以通过load命令将DISK层中的配置加载到Memory和runtime层中,达到回滚到效果。
内置库表介绍
登录到proxysql管理端口,默认用户名密码为:admin/admin
mysql -uadmin -padmin -h127.0.0.1 -P6032
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.30 (ProxySQL Admin Module)
Admin> show databases;
+-----+---------+-------------------------------+
| seq | name | file |
+-----+---------+-------------------------------+
| 0 | main | |
| 2 | disk | /var/lib/proxysql/proxysql.db |
| 3 | stats | |
| 4 | monitor | |
+-----+---------+-------------------------------+
Admin> show tables;
+--------------------------------------+
| tables |
+--------------------------------------+
| global_variables |
| mysql_collations |
| mysql_query_rules |
| mysql_replication_hostgroups |
| mysql_servers |
| mysql_users |
| runtime_global_variables |
| runtime_mysql_query_rules |
| runtime_mysql_replication_hostgroups |
| runtime_mysql_servers |
| runtime_mysql_users |
| runtime_scheduler |
| scheduler |
+--------------------------------------+
13 rows in set (0.00 sec)
Admin> show tables from stats;
+--------------------------------+
| tables |
+--------------------------------+
| global_variables |
| stats_mysql_commands_counters |
| stats_mysql_connection_pool |
| stats_mysql_global |
| stats_mysql_processlist |
| stats_mysql_query_digest |
| stats_mysql_query_digest_reset |
| stats_mysql_query_rules |
+--------------------------------+
8 rows in set (0.00 sec)
mysql_servers
Admin> show create table mysql_servers\G
CREATE TABLE mysql_servers (
hostgroup_id INT NOT NULL DEFAULT 0,
hostname VARCHAR NOT NULL,
port INT NOT NULL DEFAULT 3306,
status VARCHAR CHECK (UPPER(status) IN ('ONLINE','SHUNNED','OFFLINE_SOFT', 'OFFLINE_HARD')) NOT NULL DEFAULT 'ONLINE',
weight INT CHECK (weight >= 0) NOT NULL DEFAULT 1,
compression INT CHECK (compression >=0 AND compression <= 102400) NOT NULL DEFAULT 0,
max_connections INT CHECK (max_connections >=0) NOT NULL DEFAULT 1000,
max_replication_lag INT CHECK (max_replication_lag >= 0 AND max_replication_lag <= 126144000) NOT NULL DEFAULT 0,
use_ssl INT CHECK (use_ssl IN(0,1)) NOT NULL DEFAULT 0,
max_latency_ms INT UNSIGNED CHECK (max_latency_ms>=0) NOT NULL DEFAULT 0,
comment VARCHAR NOT NULL DEFAULT '',
PRIMARY KEY (hostgroup_id, hostname, port) )
1 row in set (0.00 sec)
mysql_servers对数据库实例进行了分组和实例信息配置。
mysql_replication_hostgroups
此表用于传统的异步/半同步的主从复制,对于MGT/GALERA需要使用mysql_group_replication_hostgroups或者mysql_galera_hostgroups(ProxySQL 2.x之后)。在此表中的每一行代表一对writer_hostgroup和reader_hostgroup。ProxySQL将监控read_only的值,ProxySQL将基于read_only的值来分配MySQL实例为reader_hostgroup还是writer_hostgroup,,如果发现从库的 read_only 变为0、主库变为1,则认为角色互换了,自动改写 mysql_servers 表里面 hostgroup 关系,达到自动 Failover 效果。
Admin> 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')) NOT NULL DEFAULT 'read_only',
comment VARCHAR,
UNIQUE (reader_hostgroup))
1 row in set (0.00 sec)
mysql_users
CREATE TABLE mysql_users (
username VARCHAR NOT NULL,
password VARCHAR,
active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1,
use_ssl INT CHECK (use_ssl IN (0,1)) NOT NULL DEFAULT 0,
default_hostgroup INT NOT NULL DEFAULT 0,
default_schema VARCHAR,
schema_locked INT CHECK (schema_locked IN (0,1)) NOT NULL DEFAULT 0,
transaction_persistent INT CHECK (transaction_persistent IN (0,1)) NOT NULL DEFAULT 0,
fast_forward INT CHECK (fast_forward IN (0,1)) NOT NULL DEFAULT 0,
backend INT CHECK (backend IN (0,1)) NOT NULL DEFAULT 1,
frontend INT CHECK (frontend IN (0,1)) NOT NULL DEFAULT 1,
max_connections INT CHECK (max_connections >=0) NOT NULL DEFAULT 10000,
PRIMARY KEY (username, backend),
UNIQUE (username, frontend)
)
mysql_query_rules
CREATE TABLE mysql_query_rules (
rule_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 0,
username VARCHAR,
schemaname VARCHAR,
flagIN INT NOT NULL DEFAULT 0,
client_addr VARCHAR,
proxy_addr VARCHAR,
proxy_port INT,
digest VARCHAR,
match_digest VARCHAR,
match_pattern VARCHAR,
negate_match_pattern INT CHECK (negate_match_pattern IN (0,1)) NOT NULL DEFAULT 0,
flagOUT INT,
replace_pattern VARCHAR,
destination_hostgroup INT DEFAULT NULL,
cache_ttl INT CHECK(cache_ttl > 0),
reconnect INT CHECK (reconnect IN (0,1)) DEFAULT NULL,
timeout INT UNSIGNED,
retries INT CHECK (retries>=0 AND retries <=1000),
delay INT UNSIGNED,
mirror_flagOUT INT UNSIGNED,
mirror_hostgroup INT UNSIGNED,
error_msg VARCHAR,
log INT CHECK (log IN (0,1)),
apply INT CHECK(apply IN (0,1)) NOT NULL DEFAULT 0,
comment VARCHAR)
读写分离
ProxySQL作为中间件能够监听接收到应用程序端的数据库请求,并解析前端的SQL语句并将解析结果与查询规则进行匹配,将匹配的SQL发送到相应的MySQL实例从而实现读写分离。这个过程入下图所示:
1. 搭建MySQL主从,结构如下:
master: 192.168.20.31:3306
slave1: 192.168.20.32:3306
slave2: 192.168.20.33:3306
2. 在主库中创建监控账号用于ProxySQL监控目标主机。
create user 'monitor'@'%' identified with mysql_native_password by 'monitor';
grant select on sys.* to 'monitor'@'%';
grant select on performance_schema.* to 'monitor'@'%';
flush privileges;
3. 插入3个数据库实例节点到ProxySQL
Admin> insert into mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup) values(10,20);
Admin> insert into mysql_servers (hostgroup_id,hostname,port) values (10,'192.168.20.31',3306);
Admin> insert into mysql_servers (hostgroup_id,hostname,port) values (20,'192.168.20.32',3306);
Admin> insert into mysql_servers (hostgroup_id,hostname,port) values (10,'192.168.20.33',3306);
Admin> save mysql servers to disk;
Admin> load mysql servers to runtime;
4. 配置读写分离规则
Admin> 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);
Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Admin> SAVE MYSQL QUERY RULES TO DISK;
5. 配置MySQL用户到ProxySQL
Admin> INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('app','pass',10);
Admin> save mysql users to disk;
Admin> load mysql users to runtime;
ProxySQL + Orchestrator实现高可用
Orchestrator(https://github.com/github/orchestrator)是MySQL复制结构的一个拓扑管理工具,能够自动检测MySQL拓扑结构,当主库出现故障时能够自动将“最优”从库提升为主库。Orchestrator提供了丰富的API接口和故障检测以及故障切换的钩子函数,MyData正是通过Orchestrator提供的钩子函数配合ProxySQL、Keepalived、HAProxy实现对应用程序无感知的故障切换。
切换流程:
1. 首先Orchestrator检测的后端主库出现故障后会通过从库二次确认主库出现故障,避免了主库高负载时出现误判主库down机。
2. Orchestrator切换之前将调用PreFailoverProcesses钩子函数,MyData在钩子函数中首先将“down”机的主库从ProxySQL中踢出,避免应用程序将请求写入到“假死”的主库,执行语句如下:
#强制关闭与假死主库的连接,避免数据被写入的假死的老主库。
Admin> update runtime_mysql_servers set status="HARD_OFFLINE" where hostname='192.168.20.31' and port='3306'
#将假死的老主库提出集群,避免后续数据被写入到老主库
Admin> delete from mysql_servers where hostname='192.168.20.31' and port='3306'
Admin> load mysql servers to runtime
Admin> save mysql serbers to disk
3. MyData能够根据用户RPO、RTO“智能”作出恢复决定。
4. MyData通知Orchestrator开发切换,Orchestrator切换完成之后,将修改新主库的read_only值为0。
5. ProxySQL将新的写请求路由到主库。
整个流程之中MyData作了大量优化,能够在满足用户RTO情况下,能将用户丢失的数据减少到原来的10%以下,MyData还为ProxySQL本身提供了高可用方案,避免了ProxySQL本身的单点故障。
总结
ProxySQL提供强大的路由规则。当应用程序自身不支持读写分离时,DBA可以通过配置路由规则为应用程序提供透明的读写分离,使用Keepalived + ProxySQL + Orchestrator为主从提供高可用时,能够有效的避免keepalived + 双主结构 由于keepalived脑裂而造成数据被写错乱的痛点。
关于MyData