1. 为什么需要读写分离
在数据库架构设计中,读写分离是提升系统性能的经典方案。当单台MySQL服务器遇到性能瓶颈时,最常见的表现就是查询响应变慢、写入操作排队。这种情况在Web应用、电商平台等高频访问场景中尤为明显。
我经历过一个典型的案例:某内容平台的数据库在流量高峰期CPU利用率长期保持在90%以上,简单的文章查询都要2-3秒才能返回。通过部署读写分离架构,查询性能提升了5倍,主服务器的负载下降了60%。
读写分离的核心原理其实很简单:将读操作(SELECT)和写操作(INSERT/UPDATE/DELETE)分流到不同的服务器。主库(Master)负责处理所有写操作和核心业务读请求,从库(Slave)则承担大部分读流量。这种架构特别适合读多写少的业务场景。
2. ProxySQL的核心优势
在众多MySQL中间件中,ProxySQL之所以成为我的首选,主要基于以下几个关键特性:
- 查询规则引擎:支持基于模式匹配、正则表达式等复杂规则的路由策略
- 连接池管理:有效复用数据库连接,降低建立新连接的开销
- 故障自动转移:当后端服务器宕机时能自动切换流量
- 实时监控:提供详细的查询统计和性能指标
- 动态配置:绝大多数配置可以运行时修改而无需重启服务
与传统的MySQL Router相比,ProxySQL的配置更加灵活,性能开销更小。我在压力测试中发现,ProxySQL在10,000 QPS下的延迟比MySQL Router低30%左右,内存占用也少了近一半。
3. 环境准备与安装
3.1 系统要求
建议在专用服务器上部署ProxySQL,最低配置要求:
- CPU: 4核以上
- 内存: 8GB以上
- 磁盘: 100GB以上(日志文件可能增长较快)
- 操作系统: CentOS 7+/Ubuntu 18.04+
注意:生产环境务必避免将ProxySQL与MySQL部署在同一台服务器上,否则会失去故障隔离的优势。
3.2 安装步骤
对于CentOS系统,安装过程如下:
# 添加ProxySQL官方仓库 cat > /etc/yum.repos.d/proxysql.repo <<EOF [proxysql] name=ProxySQL YUM repository baseurl=https://repo.proxysql.com/ProxySQL/proxysql-2.4.x/centos/\$releasever gpgcheck=1 gpgkey=https://repo.proxysql.com/ProxySQL/proxysql-2.4.x/repo_pub_key EOF # 安装ProxySQL yum install -y proxysql mysql-client # 启动服务 systemctl start proxysql systemctl enable proxysql安装完成后,默认的管理接口端口为6032,客户端连接端口为6033。可以通过以下命令验证安装:
mysql -u admin -padmin -h 127.0.0.1 -P 6032 --prompt='ProxySQL> '4. 基础配置详解
4.1 后端服务器配置
首先需要定义MySQL服务器组。通常我们会设置:
- 写组(writer_hostgroup):通常为组ID 10
- 读组(reader_hostgroup):通常为组ID 20
INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (10,'master.example.com',3306), (20,'slave1.example.com',3306), (20,'slave2.example.com',3306); LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;4.2 监控配置
ProxySQL需要定期检查后端服务器的状态:
UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username'; UPDATE global_variables SET variable_value='monitor_password' WHERE variable_name='mysql-monitor_password'; LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;然后在MySQL服务器上创建监控用户:
CREATE USER 'monitor'@'%' IDENTIFIED BY 'monitor_password'; GRANT USAGE ON *.* TO 'monitor'@'%';4.3 用户认证配置
配置客户端连接ProxySQL的用户名和密码:
INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('app_user','app_password',10); LOAD MYSQL USERS TO RUNTIME; SAVE MYSQL USERS TO DISK;5. 读写分离规则配置
5.1 基本路由规则
最常见的配置是将SELECT查询路由到读组,其他操作路由到写组:
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (1,1,'^SELECT.*FOR UPDATE',10,1), (2,1,'^SELECT',20,1), (3,1,'^INSERT',10,1), (4,1,'^UPDATE',10,1), (5,1,'^DELETE',10,1); LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;5.2 高级路由策略
对于特定表或特殊查询,可以设置更精确的路由规则:
-- 将reporting相关的查询路由到专用从库 INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply) VALUES (6,1,'^SELECT.*FROM reporting\.',30,1); -- 将后台管理系统的查询全部路由到主库 INSERT INTO mysql_query_rules (rule_id,active,client_addr,destination_hostgroup,apply) VALUES (7,1,'^10\.10\.20\.%',10,1);6. 性能优化技巧
6.1 连接池调优
UPDATE global_variables SET variable_value='300' WHERE variable_name='mysql-connection_pool_size'; UPDATE global_variables SET variable_value='60' WHERE variable_name='mysql-connection_pool_max';6.2 查询缓存
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,cache_ttl,apply) VALUES (8,1,'^SELECT.*FROM products WHERE id=\d+$',60000,1);6.3 负载均衡策略
UPDATE mysql_servers SET weight=100 WHERE hostgroup_id=10; UPDATE mysql_servers SET weight=50 WHERE hostgroup_id=20;7. 监控与维护
7.1 关键监控指标
-- 查看查询统计 SELECT hostgroup, count_star, sum_time FROM stats_mysql_query_digest; -- 查看连接池状态 SELECT * FROM stats_mysql_connection_pool; -- 查看服务器状态 SELECT * FROM monitor.mysql_server_ping_log ORDER BY time_start_us DESC LIMIT 3;7.2 常见问题排查
问题1:查询被路由到错误的服务器
检查步骤:
- 确认查询匹配的规则:
SELECT * FROM stats_mysql_query_rules - 检查服务器状态:
SELECT * FROM runtime_mysql_servers - 查看具体查询的路由:
SELECT * FROM stats_mysql_query_digest WHERE digest_text LIKE '%your_query%'
问题2:连接池耗尽
解决方案:
- 增加连接池大小
- 优化应用连接管理
- 检查是否有连接泄漏
8. 高可用配置
8.1 主库故障转移
INSERT INTO mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup) VALUES (10,20); LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;8.2 从库自动剔除
UPDATE global_variables SET variable_value='2000' WHERE variable_name='mysql-monitor_slave_lag_when_null'; UPDATE global_variables SET variable_value='60' WHERE variable_name='mysql-monitor_slave_lag_count';9. 生产环境最佳实践
配置备份:定期备份ProxySQL配置
mysqldump -u admin -padmin -h 127.0.0.1 -P 6032 --databases main > proxysql_config.sql日志管理:配置适当的日志级别和轮转
# /etc/proxysql.cnf datadir="/var/lib/proxysql" errorlog="/var/lib/proxysql/proxysql.log" log_level=2安全加固:
- 修改默认管理员密码
- 限制管理接口访问IP
- 启用SSL加密
性能监控:集成Prometheus监控
UPDATE global_variables SET variable_value='true' WHERE variable_name='admin-web_enabled'; UPDATE global_variables SET variable_value='0.0.0.0:6080' WHERE variable_name='admin-web_port';
10. 进阶配置示例
10.1 分片配置
INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (100,'shard1.example.com',3306), (200,'shard2.example.com',3306); INSERT INTO mysql_query_rules (rule_id,active,schemaname,destination_hostgroup,apply) VALUES (20,1,'user_db_',100,1), (21,1,'order_db_',200,1);10.2 读写分离权重调整
UPDATE mysql_servers SET weight=100 WHERE hostgroup_id=10; UPDATE mysql_servers SET weight=50 WHERE hostgroup_id=20 AND hostname='slave1.example.com'; UPDATE mysql_servers SET weight=30 WHERE hostgroup_id=20 AND hostname='slave2.example.com';10.3 查询重写
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,replace_pattern,apply) VALUES (30,1,'^SELECT \* FROM products','SELECT id,name,price FROM products',1);在实际生产环境中,ProxySQL的配置需要根据业务特点不断调整优化。我建议每季度进行一次全面的配置审查,重点关注查询规则的有效性和服务器负载均衡情况。