一、为什么要用 ProxySQL?
在业务增长过程中,数据库往往是最先出现瓶颈的环节。读写分离是提升数据库性能最直接有效的手段之一。然而,直接在应用层硬编码读写分离逻辑,不仅代码侵入性强,后期维护成本也很高。
ProxySQL 是一个高性能、轻量级的 MySQL 代理中间件,用 C++ 开发,性能损耗极低。它能帮你:
-
透明地实现读写分离:应用只需连接 ProxySQL,无需关心后端主从结构
-
自动故障切换:后端节点宕机时自动摘除,对应用无感知
-
灵活的路由规则:可按 SQL 类型、正则表达式、事务状态等精细控制路由
与 MyCAT、ShardingSphere 等竞品相比,ProxySQL 的定位非常明确——专注于纯读写分离场景,部署简单,配置方式灵活(通过 SQL 即可完成),学习曲线平缓。如果你的需求只是读写分离,不需要分库分表,ProxySQL 是性价比最高的选择。
二、环境准备
| 项目 | 说明 |
|---|---|
| ProxySQL 版本 | 3.0.9(最新稳定版) |
| 操作系统 | CentOS 7/8/9、Ubuntu 20.04/22.04/24.04 等 |
| 后端数据库 | MySQL 5.7 或 8.0,已完成主从复制配置 |
| 必备工具 | MySQL 客户端(用于连接管理端口) |
网络规划参考:
| 角色 | IP 地址 | 端口 |
|---|---|---|
| ProxySQL 服务节点 | 192.168.0.10 | 6033(应用连接)/ 6032(管理端口) |
| MySQL 主库 | 192.168.0.11 | 3306 |
| MySQL 从库 1 | 192.168.0.12 | 3306 |
| MySQL 从库 2 | 192.168.0.13 | 3306 |
三、安装 ProxySQL
方式一:通过 YUM 仓库安装(推荐,CentOS/RHEL)
1. 配置官方 YUM 仓库
cat <<EOF | tee /etc/yum.repos.d/proxysql.repo [proxysql_repo] name=ProxySQL repository baseurl=https://repo.proxysql.com/ProxySQL/proxysql-3.0.x/centos/\$releasever gpgcheck=1 gpgkey=https://repo.proxysql.com/ProxySQL/proxysql-3.0.x/repo_pub_key EOF
2. 安装 ProxySQL
yum install proxysql -y
如果要安装特定版本,例如 3.0.9:
yum install proxysql-3.0.9 -y
方式二:通过 APT 仓库安装(Ubuntu/Debian)
# 添加仓库密钥和源 wget -nv -O /usr/share/keyrings/proxysql-3.0.x-keyring.gpg \ 'https://repo.proxysql.com/ProxySQL/proxysql-3.0.x/repo_pub_key.gpg' echo "deb [signed-by=/usr/share/keyrings/proxysql-3.0.x-keyring.gpg] \ https://repo.proxysql.com/ProxySQL/proxysql-3.0.x/$(lsb_release -sc)/ ./" \ | tee /etc/apt/sources.list.d/proxysql.list # 安装 apt update apt install proxysql -y
方式三:下载 DEB/RPM 包直接安装
访问 下载对应系统的安装包:
# Ubuntu 示例 wget https://github.com/sysown/proxysql/releases/download/v3.0.9/proxysql_3.0.9-ubuntu22_amd64.deb dpkg -i proxysql_3.0.9-ubuntu22_amd64.deb
四、启动与基本验证
安装完成后,使用 systemd 管理服务:
# 启动 ProxySQL systemctl start proxysql # 设置开机自启 systemctl enable proxysql # 查看状态 systemctl status proxysql
检查端口监听:
netstat -tlnp | grep -E '6032|6033'
-
6032:管理端口,用于配置 ProxySQL
-
6033:应用连接端口,业务程序通过此端口访问数据库
五、核心概念:三层配置体系
在动手配置之前,必须先理解 ProxySQL 独特的配置架构,这是最容易踩坑的地方:
┌─────────────────────────────────────┐ │ RUNTIME │ ← 正在生效的配置(内存中) ├─────────────────────────────────────┤ │ MEMORY │ ← 你正在编辑的配置(修改后未生效) ├─────────────────────────────────────┤ │ DISK │ ← 持久化到 SQLite 的配置(重启不丢失) └─────────────────────────────────────┘
操作逻辑:
-
在管理界面中修改
MEMORY层的表 -
执行
LOAD ... TO RUNTIME使配置生效 -
执行
SAVE ... TO DISK持久化到磁盘
⚠️ 常见错误:修改后忘了 SAVE TO DISK,重启 ProxySQL 后配置全部丢失。
六、配置读写分离
6.1 连接管理界面
mysql -u admin -padmin -h 127.0.0.1 -P6032 --prompt='ProxySQL> '
注意:必须用
127.0.0.1,不能用localhost,否则客户端会用 Unix Socket 连接,无法到达 TCP 端口。
默认管理员账号密码为 admin/admin。如果 MySQL 客户端版本为 8.04 或更高,可能需要添加 --default-auth=mysql_native_password。
6.2 在后端 MySQL 创建监控账号
ProxySQL 需要通过监控账号检测后端节点的健康状况和复制延迟:
-- 在主库上执行 CREATE USER 'monitor'@'%' IDENTIFIED BY 'monitor'; GRANT SUPER, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'monitor'@'%'; FLUSH PRIVILEGES;
6.3 配置后端 MySQL 实例
在管理界面中执行:
-- 清空已有配置(首次配置或重置时使用) DELETE FROM mysql_servers; -- 添加主库(写组,hostgroup_id = 10) INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight) VALUES (10, '192.168.0.11', 3306, 100); -- 添加从库(读组,hostgroup_id = 20) INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight) VALUES (20, '192.168.0.12', 3306, 100); INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight) VALUES (20, '192.168.0.13', 3306, 100); -- 加载到 RUNTIME 并持久化 LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;
参数说明:
-
hostgroup_id:分组编号,10 为写组,20 为读组 -
weight:权重,值越大分配流量越多 -
max_replication_lag:可单独为从库设置延迟阈值,超过则自动摘除
6.4 配置应用连接账号
-- 添加应用使用的账号 INSERT INTO mysql_users (username, password, default_hostgroup, transaction_persistent) VALUES ('appuser', 'your_password', 10, 1); -- 加载并持久化 LOAD MYSQL USERS TO RUNTIME; SAVE MYSQL USERS TO DISK;
参数说明:
-
default_hostgroup=10:默认路由到写组 -
transaction_persistent=1:事务内的所有 SQL 保持在同一 hostgroup,避免读写不一致
注意:
appuser需要提前在后端 MySQL 主库上创建并授权。
6.5 配置读写分离规则——最关键的一步
规则按 rule_id 从小到大顺序匹配,匹配到 apply=1 的规则后停止匹配,顺序很重要。
-- 删除旧规则(重置用) DELETE FROM mysql_query_rules; -- 规则1:SELECT ... FOR UPDATE(需要加锁的读)→ 走写组 INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (1, 1, '^SELECT.*FOR UPDATE$', 10, 1); -- 规则2:普通 SELECT → 走读组 INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (2, 1, '^SELECT', 20, 1); -- 规则3:写操作(INSERT/UPDATE/DELETE/REPLACE)→ 走写组 INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (3, 1, '^(INSERT|UPDATE|DELETE|REPLACE)', 10, 1); -- 规则4:兜底规则,所有其他 SQL → 走写组 INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (4, 1, '.*', 10, 1); -- 加载并持久化 LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;
规则匹配逻辑:
| rule_id | 匹配条件 | 路由目标 | 说明 |
|---|---|---|---|
| 1 | ^SELECT.*FOR UPDATE$ |
写组(10) | 带锁的查询必须走主库 |
| 2 | ^SELECT |
读组(20) | 普通查询走从库 |
| 3 | ^(INSERT|UPDATE|DELETE|REPLACE) |
写组(10) | 所有写操作走主库 |
| 4 | .* |
写组(10) | 未被匹配的请求兜底到主库 |
6.6 配置主从复制组(自动故障切换)
配置 mysql_replication_hostgroups 后,ProxySQL 会自动检测后端节点的 read_only 状态,当主库宕机、从库提升为新主库时,自动调整路由。
INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, comment) VALUES (10, 20, '读写分离组'); LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;
6.7 启用监控和复制延迟检查
-- 启用监控 UPDATE global_variables SET variable_value='true' WHERE variable_name='mysql-monitor_enabled'; -- 设置监控间隔(单位:毫秒) UPDATE global_variables SET variable_value='2000' WHERE variable_name IN ( 'mysql-monitor_connect_interval', 'mysql-monitor_ping_interval', 'mysql-monitor_read_only_interval' ); -- 设置从库最大复制延迟(单位:秒),超过则自动摘除 UPDATE mysql_servers SET max_replication_lag = 10 WHERE hostgroup_id = 20; -- 加载全局变量 LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;
6.8 利用配置文件配置
根据官方文档,配置文件主要用于提供初始/基础配置,复杂的配置推荐通过管理界面进行动态修改。
# ProxySQL 配置文件示例 # 用于 MySQL 读写分离场景 # ========================================== # 1. 基础路径设置 (始终从配置文件读取) # ========================================== datadir="/var/lib/proxysql" # 数据目录,存放 proxysql.db 等文件 errorlog="/var/lib/proxysql/proxysql.log" # 错误日志路径 # ========================================== # 2. 管理模块 (admin_variables) # ========================================== admin_variables = { # 管理员账号密码(强烈建议生产环境修改) # 格式: 用户名:密码,多个用分号隔开 admin_credentials="admin:admin;radmin:radmin" # 管理接口监听地址和端口(默认 6032) mysql_ifaces="0.0.0.0:6032" # 是否启用 REST API(可选) restapi_enabled="true" restapi_port=6070 # 管理界面刷新间隔(毫秒) refresh_interval=2000 } # ========================================== # 3. MySQL 代理模块 (mysql_variables) # ========================================== mysql_variables = { # 业务端口,应用程序连接此端口(默认 6033) interfaces="0.0.0.0:6033" # 后端 MySQL 版本,ProxySQL 会向客户端报告此版本 server_version="5.7.39" # 线程数与连接数 threads=4 max_connections=2048 # 查询超时(毫秒),默认 36,000,000 ms = 10 小时 default_query_timeout=36000000 # 监控账号(需提前在后端 MySQL 创建) monitor_username="monitor" monitor_password="monitor" # 监控间隔(毫秒) monitor_connect_interval=2000 monitor_ping_interval=2000 monitor_read_only_interval=1500 # 启用压缩 have_compress=true } # ========================================== # 4. 后端服务器列表 (mysql_servers) # 注:官方推荐通过管理界面动态配置,此处仅作初始化示例 # ========================================== mysql_servers = ( # 主库(写组 hostgroup_id = 10) { address="192.168.0.11" port=3306 hostgroup=10 weight=100 max_replication_lag=0 }, # 从库1(读组 hostgroup_id = 20) { address="192.168.0.12" port=3306 hostgroup=20 weight=100 max_replication_lag=10 }, # 从库2(读组 hostgroup_id = 20) { address="192.168.0.13" port=3306 hostgroup=20 weight=100 max_replication_lag=10 } ) # ========================================== # 5. 应用用户 (mysql_users) # ========================================== mysql_users = ( { username="appuser" password="your_password" default_hostgroup=10 # 默认路由到写组 transaction_persistent=1 # 事务内保持同一 hostgroup active=1 max_connections=100 } ) # ========================================== # 6. 读写分离路由规则 (mysql_query_rules) # 匹配顺序:按 rule_id 从小到大 # ========================================== mysql_query_rules = ( # 规则1:SELECT ... FOR UPDATE(加锁读)→ 写组 { rule_id=1 active=1 match_digest="^SELECT.*FOR UPDATE$" destination_hostgroup=10 apply=1 }, # 规则2:普通 SELECT → 读组 { rule_id=2 active=1 match_digest="^SELECT" destination_hostgroup=20 apply=1 }, # 规则3:写操作(INSERT/UPDATE/DELETE/REPLACE)→ 写组 { rule_id=3 active=1 match_digest="^(INSERT|UPDATE|DELETE|REPLACE)" destination_hostgroup=10 apply=1 }, # 规则4:兜底规则,其他所有 SQL → 写组 { rule_id=4 active=1 match_digest=".*" destination_hostgroup=10 apply=1 } ) # ========================================== # 7. 复制组自动管理(可选) # 根据后端 read_only 状态自动分配 hostgroup # ========================================== mysql_replication_hostgroups = ( { writer_hostgroup=10 reader_hostgroup=20 comment="读写分离组" } )
配置要点说明
1. 配置文件 vs 动态命令:配置文件仅在 ProxySQL 首次启动或使用 --initial 参数时被读取,主要用于初始化 proxysql.db。日常运维推荐通过管理端口(6032)使用 INSERT/UPDATE + LOAD/SAVE 命令动态修改配置。
2. 管理员账号:admin_credentials 的默认账号是 admin:admin,生产环境务必修改,否则存在严重安全风险。示例中添加了 radmin 作为只读管理账号。
3. 监控账号:monitor_username 和 monitor_password 需要提前在后端 MySQL 主库上创建并授予 SUPER, REPLICATION CLIENT, REPLICATION SLAVE 权限,否则 ProxySQL 无法检测后端节点健康状态。
4. 路由规则匹配顺序:规则按 rule_id 从小到大依次匹配,apply=1 表示匹配成功后停止继续匹配后续规则。因此要先匹配特殊规则(如 FOR UPDATE),再匹配通用规则。
5. 命令速查:
-- 连接管理界面 mysql -u admin -padmin -h 127.0.0.1 -P6032 -- 修改配置后必须执行: LOAD MYSQL SERVERS TO RUNTIME; -- 使配置生效 SAVE MYSQL SERVERS TO DISK; -- 持久化到磁盘(防止重启丢失)
这份配置文件可以作为搭建读写分离的起点,实际部署时请根据你的主从 IP、端口、账号密码等信息替换对应值。
七、验证读写分离效果
7.1 应用连接测试
使用配置的应用账号连接 ProxySQL 的服务端口(6033):
mysql -u appuser -p'your_password' -h 192.168.0.10 -P6033
7.2 查看路由状态
在管理界面(6032 端口)查看连接池状态:
SELECT hostgroup, srv_host, srv_port, status, ConnUsed FROM stats_mysql_connection_pool;
如果从库连接显示 hostgroup = 20,主库连接显示 hostgroup = 10,且状态均为 ONLINE,说明注册成功。
7.3 查看查询路由统计
SELECT digest_text, count_star, sum_time, hostgroup FROM stats_mysql_query_digest ORDER BY count_star DESC LIMIT 10;
如果 SELECT 查询的 hostgroup 是 20,INSERT/UPDATE 的 hostgroup 是 10,说明读写分离已生效。
7.4 强制路由调试
如需临时强制查询走主库,可使用注释 Hint:
/* hostgroup=10 */ SELECT * FROM your_table;
八、常见踩坑与解决方案
💣 坑 1:事务中的读操作被路由到从库
现象:事务中先插入了一条数据,紧接着查询时查不到。
原因:SELECT 被匹配到读组,从库复制有延迟,数据还未同步。
解决:
-
确保应用使用事务包裹“写后立即读”的操作
-
或使用 Hint 强制走主库:
/* hostgroup=10 */ SELECT ...
💣 坑 2:修改配置后重启丢失
现象:重启 ProxySQL 后所有配置消失。
原因:只执行了 LOAD ... TO RUNTIME,未执行 SAVE ... TO DISK。
解决:养成习惯,每次改完都执行 SAVE MYSQL ... TO DISK。
💣 坑 3:忘记配置监控账号
现象:后端节点状态一直是 OFFLINE 或连接报错。
原因:未在 MySQL 创建 monitor 账号,或密码与配置不匹配。
解决:检查 mysql-monitor_username 和 mysql-monitor_password 配置,确保与 MySQL 实际账号一致。
💣 坑 4:从库宕机后流量未自动切换
现象:从库宕机后,部分查询仍然报错。
原因:未配置 mysql_replication_hostgroups 或监控间隔过长。
解决:配置 mysql_replication_hostgroups,并适当缩短监控间隔。
九、常用管理命令速查
| 操作 | 命令 |
|---|---|
| 连接管理界面 | mysql -u admin -padmin -h 127.0.0.1 -P6032 |
| 查看后端节点状态 | SELECT * FROM runtime_mysql_servers; |
| 查看连接池状态 | SELECT * FROM stats_mysql_connection_pool; |
| 查看查询路由统计 | SELECT * FROM stats_mysql_query_digest ORDER BY sum_time DESC LIMIT 10; |
| 查看当前生效的规则 | SELECT * FROM runtime_mysql_query_rules; |
| 重启 ProxySQL | systemctl restart proxysql |
| 查看版本 | proxysql --version |
十、总结
ProxySQL 是一款非常适合 MySQL 读写分离场景的轻量级中间件。通过这篇文章,你已经掌握了从安装到配置读写分离的完整流程。
配置要点回顾:
-
三层配置体系:记住
LOAD和SAVE缺一不可 -
规则顺序:按
rule_id从小到大匹配,先特殊后通用 -
事务一致性:开启
transaction_persistent=1,避免读写不一致 -
监控先行:配置监控账号,启用健康检查和复制延迟检查
希望这份指南能帮助你顺利落地 ProxySQL 读写分离方案。如果在配置过程中遇到其他问题,欢迎交流讨论。

您可以选择一种方式赞助本站
支付宝扫一扫赞助
微信钱包扫描赞助
赏