proxysql和mycat比硬编码多数据源更可靠、侵入性更低,但配置错误会导致全走主库或连接失败;关键在路由规则是否覆盖真实sql模式,而非能否配置。

直接说结论:用 ProxySQL 或 MyCAT 做读写分离,比在代码里硬编码多数据源更可靠、侵入性更低,但配置错误会导致查询全走主库或连接直连失败——关键不在“能不能配”,而在“路由规则是否覆盖真实 SQL 模式”。
ProxySQL 的读写分离必须靠 mysql_query_rules 匹配 SQL 类型
ProxySQL 不会自动识别 SELECT 就走从库。它靠规则表 mysql_query_rules 逐条匹配,没命中规则就按默认 default_hostgroup 转发(通常是主库)。
- 必须显式插入规则,例如:
INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply) VALUES (1,1,'^SELECT',2,1);
其中destination_hostgroup=2对应你定义的从库 hostgroup -
match_digest比正则快,但不支持复杂条件;若业务里有SELECT ... FOR UPDATE,得单独加一条规则指向主库 hostgroup(比如match_digest='^SELECT.*FOR UPDATE$'),否则会脏读或锁等待失败 - 规则顺序重要:
apply=1表示匹配后终止匹配;若先写了宽泛的^SELECT,再写FOR UPDATE规则就永远不生效 - 改完规则必须执行
LOAD MYSQL QUERY RULES TO RUNTIME,再SAVE MYSQL QUERY RULES TO DISK,否则重启丢失
MyCAT 的 scheme.xml 里 readHost 和 writeHost 不是“自动分流”
MyCAT 默认只把 SELECT 发给 readHost,但以下情况会绕过从库:
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 事务内任何语句(哪怕第一个是
SELECT)全部走writeHost;MyCAT 检测到BEGIN或隐式开启事务(如SELECT ... FOR UPDATE)就锁定路由 - 如果
writeHost下挂了多个从节点,readHost必须显式配置weight属性才能负载均衡,否则永远只用第一个 -
scheme.xml中<datanode></datanode>的database名必须和后端 MySQL 实例实际库名完全一致,大小写敏感,否则连接时抛java.sql.SQLException: Unknown database 'xxx' - DDL 语句(如
CREATE TABLE)默认不支持跨readHost/writeHost,必须确保主从库结构一致,否则从库同步中断
两类中间件都绕不开的主从延迟坑
ProxySQL 和 MyCAT 都不解决“写完立刻读不到”的问题——它们只做请求分发,不干预复制延迟。真实场景中:
- 主库写入后,从库可能滞后几百毫秒甚至几秒;若业务依赖“刚写完就查”,必须在应用层加
/*+ FORCE_MASTER */注释(ProxySQL 支持)或用 MyCAT 的/*#mycat:db_type=master*/强制走主库 - ProxySQL 的
mysql_servers表里,status字段为ONLINE不代表能查数据——得配合monitor模块的ping_port和自定义select_for_read_only查询检测从库是否真正可读(例如SELECT 1 FROM information_schema.tables LIMIT 1) - MyCAT 的心跳检测默认只 ping 端口,若从库
read_only=OFF但复制线程已停,它仍会把读请求打过去,导致脏写;需在schema.xml中启用heartbeat的show slave status检查
最易被忽略的一点:无论 ProxySQL 还是 MyCAT,上线前必须用真实流量压测路由行为——用 tcpdump 抓包确认 SELECT 是否真发到了从库 IP,而不是靠日志里的“rule matched”自我安慰。复制延迟、事务边界、SQL 解析歧义,这三个点任何一个没对齐,读写分离就等于白配。










