所有性能、安全、一致性细节都被设计为「不需要业务侧改一行代码」。MySQL 客户端走 MySQL 路径,PostgreSQL 客户端走 PG 路径,两者共享同一份连接池、缓存、失效语义。
同一个 listen 既可对外说 MySQL 也可对外说 PostgreSQL;启动时代理拨测后端(MySQL Initial Handshake / PG SSLRequest)自动识别要走哪条路径,也可用 backend.protocol 显式覆盖。
命中走 write_all 字节回放,不解码任何列类型;JSON / GEOMETRY / DECIMAL / DATETIME 全部字节级一致,无 round-trip 损耗。
用 sqlparser 解析每条 SQL 抽出受影响表集合;INSERT/UPDATE/DELETE 落在表 T,则所有命中过 T 的缓存条目(含所有参数变体)原子失效。
按凭据隔离的空闲连接池,跨客户端会话复用已认证的后端连接。release 时跑 DISCARD ALL(PG)或 COM_RESET_CONNECTION(MySQL)清场。
结构化的每 SQL / 缓存事件日志由单独 writer 线程批量落盘,调用线程只入队;磁盘 I/O 不会阻塞查询热路径。
消除 ~40ms 的 Nagle + delayed-ACK 死等。这一个开关是把端到端从 40–80ms 拉回 1–2ms 的关键,不是噱头。
多副本部署时,本地 DML 引发的 invalidate_table 通过共享 broadcast-host WebSocket 中转扇出到同 group 的所有副本,对端就地清掉对应缓存项;中转不会回送给发送方,单节点保持关闭即可。
维持一条出站 wss:// 长连接。链路断开 + 重连连续 N 次失败 → 强制所有 SQL 绕过缓存直通后端(CACHE OFF),握手再次成功后自动恢复(CACHE ON)。
客户端 ⇄ 代理 ⇄ 后端三段,每段都按各自协议走原生 wire;代理在中间做缓存判定、表级失效与连接池路由。
同一个后端 DB 前部署多个代理副本时,让所有副本通过 broadcast-host 共享一份"哪些表刚被改"的实时视图。中转按 group 扇出,写副本的 invalidate_table 会把读副本的对应缓存项也清掉。详见 docs/broadcast-invalidation.md。
无论 MySQL 还是 PG,每条 SQL 都要走完这 6 步;缓存命中只走前 3 步,命中即回字节。
两条路径在缓存、连接池、鉴权三处保持完全等价的行为;差异仅在各自 wire 协议的细节里。
| 客户端鉴权插件 | mysql_clear_password |
| 客户端默认插件兜底 | AuthSwitchRequest |
| 后端鉴权方式 | native_password / caching_sha2 (fast) |
| 缓存命中包 | COM_QUERY 全响应字节 |
| 包重写 | sequence_id 重新打包 |
| 缓存条件 | 单语句 · 无 MORE_RESULTS · 无 ERR |
| 连接 release 命令 | COM_RESET_CONNECTION |
| 实现 | 自实现 wire 客户端 · 无 mysql_async |
| 客户端鉴权方式 | AuthenticationCleartextPassword |
| 支持模式 | simple query · extended query |
| 后端鉴权方式 | SCRAM-SHA-256 / MD5 / cleartext |
| 缓存命中包 (simple) | RowDescription + DataRow + CmdComplete |
| 缓存命中包 (extended) | 同上 + 重发 ReadyForQuery |
| 缓存条件 | 单语句 SELECT · ReadyForQuery 'I' |
| 连接 release 命令 | DISCARD ALL |
| 实现 | 自实现 wire 客户端 · zero-copy 批量转发 |
| 直连后端 | ~1–2 ms / query |
| 未优化的代理 | 40–80 ms / query |
| 开 TCP_NODELAY 后 | ~1–2 ms / query |
| 缓存命中 | < 1 ms / query |
| 命中 vs 后端 | ~10–40× 快 |
| TCP Nagle 移除 | 双向 setnodelay |
| PG 扩展查询缓冲 | 单 Vec · 0 alloc |
| 命中 CPU | 仅一次 write_all |
| 日志 | 异步 writer 线程 |
| 认证开销 | 池化后摊销为 0 |
凭据不进配置 — 代理用客户端发来的 user/password/database 直接打上游。
proxy:
listen: "0.0.0.0:3307"
backend:
host: "127.0.0.1"
port: 3306
protocol: "mysql" # or "postgresql"
# 共享池:MySQL / PG 路径都消费这份配置
pool:
enabled: true
max_per_key: 16
idle_ttl_secs: 300
reset_query: "DISCARD ALL" # MySQL 自动映射为 COM_RESET_CONNECTION
cache:
enabled: true
ttl_secs: 300
max_entries: 10000
# 可选:出站 WSS 长连接做心跳,断开 + 重连失败 N 次 → CACHE OFF
connectivity_probe:
enabled: false
url: "wss://myip.qq.com"
interval_secs: 5
reconnect_failures_for_cache_off: 3
# 可选:多副本部署时通过 broadcast-host 互通失效
broadcast:
enabled: false
url: "ws://gy.duguying.net:9940"
group: "default"
ping_interval_secs: 15
log:
level: "info"
stdout_level: "warn"
path: "log/database-cache-proxy.log"
file_async: true