前言:什么时候你需要数据库中间件?
先问自己几个问题:
- 单表数据量突破千万甚至上亿,查询越来越慢?
- 主库写压力太大,想做读写分离但应用层改起来太麻烦?
- 数据库连接数经常打满,应用频繁报 "Too many connections"?
- 想做分库分表,但不想侵入业务代码?
如果你中了一条以上,那就该认真考虑数据库中间件了。
简单来说,数据库中间件就是横在应用和数据库之间的一层代理,它帮你处理分片路由、读写分离、连接复用、负载均衡这些脏活累活,让应用层可以像访问单库一样访问分布式数据库集群。
今天我们要聊的三款中间件,各有各的绝活:
| 中间件 | 一句话定位 |
|---|
| ShardingSphere | Java 生态的分库分表全家桶 |
| ProxySQL | MySQL 专属的轻量级智能代理 |
| Vitess | 云原生时代的水平扩展方案 |
接下来我们逐个拆解。
一、ShardingSphere:Java 生态的分库分表利器
1.1 架构概览
ShardingSphere 是 Apache 基金会的顶级项目,提供两种接入模式:
ShardingSphere-JDBC(嵌入式):
这种模式直接以 JAR 包形式嵌入 Java 应用,没有额外的网络开销,性能最好。但缺点是只能给 Java 用。
ShardingSphere-Proxy(独立代理):
Proxy 模式对外伪装成一个 MySQL 或 PostgreSQL 实例,任何语言的应用都可以直接连接,非常灵活。
1.2 核心功能
- 分库分表(Sharding): 支持按 hash、按范围、按时间等多种分片策略
- 读写分离: 自动识别读写 SQL,路由到主库或从库
- 分布式事务: 支持 XA 和 BASE(柔性事务)
- 数据加密: 透明化的列级加密,应用无感知
- 影子库: 生产环境压测利器,压测流量自动路由到影子库
- 数据迁移: 在线数据迁移,平滑扩缩容
1.3 Docker Compose 快速启动
下面我们用 Docker Compose 搭建一个 ShardingSphere-Proxy + 2 个 MySQL 实例的环境:
1# docker-compose.yml
2
3> **本章目标**:掌握本章核心知识点
4> **前置要求**:完成前序章节学习
5> **预计时长**:60 分钟
6
7version: '3.8'
8
9services:
10 mysql-ds0:
11 image: mysql:8.0
12 container_name: mysql-ds0
13 environment:
14 MYSQL_ROOT_PASSWORD: root123
15 MYSQL_DATABASE: ds_0
16 ports:
17 - "3307:3306"
18 command: --default-authentication-plugin=mysql_native_password
19
20 mysql-ds1:
21 image: mysql:8.0
22 container_name: mysql-ds1
23 environment:
24 MYSQL_ROOT_PASSWORD: root123
25 MYSQL_DATABASE: ds_1
26 ports:
27 - "3308:3306"
28 command: --default-authentication-plugin=mysql_native_password
29
30 shardingsphere-proxy:
31 image: apache/shardingsphere-proxy:5.5.0
32 container_name: ss-proxy
33 ports:
34 - "3309:3307"
35 volumes:
36 - ./conf:/opt/shardingsphere-proxy/conf
37 depends_on:
38 - mysql-ds0
39 - mysql-ds1
1.4 分片配置示例(按 user_id 哈希)
创建 conf/config-sharding.yaml:
1# conf/config-sharding.yaml
2databaseName: sharding_db
3
4dataSources:
5 ds_0:
6 url: jdbc:mysql://mysql-ds0:3306/ds_0?useSSL=false&allowPublicKeyRetrieval=true
7 username: root
8 password: root123
9 connectionTimeoutMilliseconds: 30000
10 idleTimeoutMilliseconds: 60000
11 maxLifetimeMilliseconds: 1800000
12 maxPoolSize: 50
13 minPoolSize: 1
14 ds_1:
15 url: jdbc:mysql://mysql-ds1:3306/ds_1?useSSL=false&allowPublicKeyRetrieval=true
16 username: root
17 password: root123
18 connectionTimeoutMilliseconds: 30000
19 idleTimeoutMilliseconds: 60000
20 maxLifetimeMilliseconds: 1800000
21 maxPoolSize: 50
22 minPoolSize: 1
23
24rules:
25 - !SHARDING
26 tables:
27 t_order:
28 actualDataNodes: ds_${0..1}.t_order_${0..3}
29 tableStrategy:
30 standard:
31 shardingColumn: order_id
32 shardingAlgorithmName: t_order_inline
33 keyGenerateStrategy:
34 column: order_id
35 keyGeneratorName: snowflake
36 t_user:
37 actualDataNodes: ds_${0..1}.t_user_${0..1}
38 databaseStrategy:
39 standard:
40 shardingColumn: user_id
41 shardingAlgorithmName: database_inline
42 tableStrategy:
43 standard:
44 shardingColumn: user_id
45 shardingAlgorithmName: t_user_inline
46
47 shardingAlgorithms:
48 database_inline:
49 type: INLINE
50 props:
51 algorithm-expression: ds_${user_id % 2}
52 t_order_inline:
53 type: INLINE
54 props:
55 algorithm-expression: t_order_${order_id % 4}
56 t_user_inline:
57 type: INLINE
58 props:
59 algorithm-expression: t_user_${user_id % 2}
60
61 keyGenerators:
62 snowflake:
63 type: SNOWFLAKE
配置解读:
t_user 表按 user_id % 2 分到 ds_0 和 ds_1 两个库
- 每个库里再按
user_id % 2 分成 t_user_0 和 t_user_1 两张表
t_order 表按 order_id % 4 分成 4 张子表
- 主键使用雪花算法自动生成,保证全局唯一
启动后连接代理试试:
1# 启动
2docker-compose up -d
3
4# 等待服务就绪后连接
5mysql -h 127.0.0.1 -P 3309 -u root -p
6
7# 连上后就像操作普通 MySQL 一样
8mysql> USE sharding_db;
9mysql> CREATE TABLE t_user (
10 user_id BIGINT PRIMARY KEY,
11 username VARCHAR(50),
12 email VARCHAR(100)
13);
14mysql> INSERT INTO t_user (user_id, username, email) VALUES (1, 'alice', 'alice@example.com');
15mysql> INSERT INTO t_user (user_id, username, email) VALUES (2, 'bob', 'bob@example.com');
16-- user_id=1 会被路由到 ds_1.t_user_1
17-- user_id=2 会被路由到 ds_0.t_user_0
1.5 优缺点
优点:
- 功能最全面:分片、读写分离、加密、影子库一应俱全
- 两种接入模式灵活选择
- 社区活跃,Apache 顶级项目背书
- 支持 MySQL 和 PostgreSQL
- 配置驱动,不侵入业务 SQL(大部分场景下)
缺点:
- 学习曲线较陡,配置项多且复杂
- Proxy 模式有额外网络开销
- 跨分片 JOIN 和聚合操作有限制
- 运维工具不够成熟,排查问题有时候比较痛苦
- 版本迭代快,API 变动频繁
二、ProxySQL:MySQL 的瑞士军刀
2.1 架构概览
ProxySQL 是一个用 C++ 写的高性能 MySQL 代理,它非常轻量——一个二进制文件就能跑起来。
ProxySQL 内部维护了连接池、查询路由引擎、查询缓存和查询防火墙,所有这些功能可以通过它独特的管理接口动态配置,不需要重启。
2.2 核心功能
- 连接池(Connection Multiplexing): 前端可能有上千个连接,后端只需要几十个,大幅降低数据库压力
- 查询路由(Query Routing): 基于正则表达式、用户、schema、端口等维度灵活路由
- 读写分离: 自动把 SELECT 发到从库,DML 发到主库
- 查询缓存: 在代理层缓存查询结果,减少数据库压力
- 查询防火墙: 拦截危险 SQL,比如不带 WHERE 的 DELETE
- 后端健康检查: 自动检测后端状态,故障节点自动摘除
- 查询镜像(Query Mirroring): 把查询复制到另一个后端,适合测试新版本
2.3 Docker 快速启动
1# 启动 ProxySQL
2docker run -d \
3 --name proxysql \
4 -p 6033:6033 \
5 -p 6032:6032 \
6 -v ./proxysql.cnf:/etc/proxysql.cnf \
7 proxysql/proxysql:2.6.3
8
9# 6033 = MySQL 协议端口(应用连这个)
10# 6032 = 管理端口(运维连这个)
或者用 Docker Compose 搭建完整的主从 + ProxySQL 环境:
1# docker-compose-proxysql.yml
2version: '3.8'
3
4services:
5 mysql-master:
6 image: mysql:8.0
7 container_name: mysql-master
8 environment:
9 MYSQL_ROOT_PASSWORD: root123
10 ports:
11 - "3310:3306"
12 command: >
13 --server-id=1
14 --log-bin=mysql-bin
15 --binlog-format=ROW
16 --gtid-mode=ON
17 --enforce-gtid-consistency=ON
18
19 mysql-slave:
20 image: mysql:8.0
21 container_name: mysql-slave
22 environment:
23 MYSQL_ROOT_PASSWORD: root123
24 ports:
25 - "3311:3306"
26 command: >
27 --server-id=2
28 --log-bin=mysql-bin
29 --binlog-format=ROW
30 --gtid-mode=ON
31 --enforce-gtid-consistency=ON
32 --read-only=ON
33 depends_on:
34 - mysql-master
35
36 proxysql:
37 image: proxysql/proxysql:2.6.3
38 container_name: proxysql
39 ports:
40 - "6033:6033"
41 - "6032:6032"
42 volumes:
43 - ./proxysql.cnf:/etc/proxysql.cnf
44 depends_on:
45 - mysql-master
46 - mysql-slave
2.4 读写分离配置
ProxySQL 最有意思的地方是它的管理接口——你通过 MySQL 客户端连上管理端口,直接用 SQL 来配置:
1# 连接管理接口
2mysql -h 127.0.0.1 -P 6032 -u admin -padmin
1-- 第一步:添加后端 MySQL 服务器
2-- hostgroup_id=10 是写组,hostgroup_id=20 是读组
3INSERT INTO mysql_servers (hostgroup_id, hostname, port, max_connections)
4VALUES
5 (10, 'mysql-master', 3306, 100),
6 (20, 'mysql-slave', 3306, 100);
7
8-- 第二步:配置读写分离规则
9INSERT INTO mysql_replication_hostgroups
10 (writer_hostgroup, reader_hostgroup, check_type)
11VALUES
12 (10, 20, 'read_only');
13
14-- 第三步:添加用户
15INSERT INTO mysql_users (username, password, default_hostgroup)
16VALUES ('app_user', 'app_password', 10);
17
18-- 第四步:加载配置并持久化
19LOAD MYSQL SERVERS TO RUNTIME;
20SAVE MYSQL SERVERS TO DISK;
21LOAD MYSQL USERS TO RUNTIME;
22SAVE MYSQL USERS TO DISK;
2.5 查询规则配置
ProxySQL 的查询规则非常强大,你可以用正则表达式把特定查询路由到指定的 hostgroup:
1-- 所有 SELECT 语句路由到读组(hostgroup 20)
2INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
3VALUES (1, 1, '^SELECT.*', 20, 1);
4
5-- 但是 SELECT ... FOR UPDATE 路由到写组(hostgroup 10)
6INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
7VALUES (2, 1, '^SELECT.*FOR UPDATE$', 10, 1);
8
9-- 拦截不带 WHERE 的 DELETE(查询防火墙)
10INSERT INTO mysql_query_rules (rule_id, active, match_pattern, error_msg, apply)
11VALUES (3, 1, '^DELETE\s+FROM\s+\w+\s*$', 'Dangerous! DELETE without WHERE is blocked.', 1);
12
13-- 缓存某些热点查询 10 秒
14INSERT INTO mysql_query_rules (rule_id, active, match_pattern, cache_ttl, destination_hostgroup, apply)
15VALUES (4, 1, '^SELECT.*FROM\s+hot_config', 10000, 20, 1);
16
17-- 加载生效
18LOAD MYSQL QUERY RULES TO RUNTIME;
19SAVE MYSQL QUERY RULES TO DISK;
2.6 监控
ProxySQL 内置了丰富的统计表,可以直接查询:
1-- 连上管理端口后
2
3-- 查看连接池状态
4SELECT hostgroup, srv_host, status, ConnUsed, ConnFree, ConnOK, ConnERR
5FROM stats_mysql_connection_pool;
6
7-- 查看查询统计(哪些查询最慢?)
8SELECT hostgroup, digest_text, count_star, sum_time_us,
9 ROUND(sum_time_us/count_star) AS avg_time_us
10FROM stats_mysql_query_digest
11ORDER BY sum_time_us DESC
12LIMIT 10;
13
14-- 查看查询规则命中情况
15SELECT rule_id, hits, match_pattern
16FROM stats_mysql_query_rules
17ORDER BY hits DESC;
这些统计数据对于性能调优非常有价值。你还可以接入 Prometheus + Grafana 实现可视化监控。
2.7 优缺点
优点:
- 极其轻量,C++ 编写,性能开销很小
- 动态配置,修改路由规则不需要重启
- 连接池效果显著,能大幅降低后端连接数
- 查询缓存和查询防火墙实用
- 运维友好,SQL 管理接口学习成本低
- 社区成熟,生产案例丰富
缺点:
- 只支持 MySQL(包括 Percona、MariaDB),不支持 PostgreSQL
- 没有分库分表能力,不做数据分片
- 需要手动配置主从关系(虽然支持自动发现)
- 官方 UI 比较简陋
- 复杂的查询路由规则调试起来比较麻烦
三、Vitess:为超大规模而生
3.1 架构概览
Vitess 是 YouTube 为了解决 MySQL 扩展性问题而开发的,现在是 CNCF 毕业项目。它的架构比前两个复杂得多:
三个核心组件:
- VTGate: 无状态的查询网关,应用连接它。它理解分片拓扑,把 SQL 拆分路由到正确的分片
- VTTablet: 每个 MySQL 实例前面的"小管家",管理连接池、执行查询、处理复制切换
- Topology Service: 使用 etcd、ZooKeeper 或 Consul 存储集群元数据
3.2 核心功能
- 水平分片(Horizontal Sharding): Vitess 的核心能力,支持基于 range 或 hash 的分片
- 在线 Resharding: 可以在不停机的情况下拆分或合并分片
- 连接池: VTTablet 在 MySQL 前做连接复用
- 查询服务: VTGate 自动做查询分析、改写、结果聚合
- Schema 管理: 在所有分片上统一执行 DDL
- 备份恢复: 内置备份到对象存储的能力
- VReplication: 强大的数据移动引擎,支持物化视图、Change Data Capture
3.3 Kubernetes 原生部署
Vitess 天然适合 Kubernetes,官方提供 Operator:
1# 安装 Vitess Operator
2kubectl apply -f https://github.com/planetscale/vitess-operator/releases/latest/download/operator.yaml
定义一个最简单的 Vitess 集群:
1# vitess-cluster.yaml
2apiVersion: planetscale.com/v2
3kind: VitessCluster
4metadata:
5 name: my-vitess
6spec:
7 images:
8 vtgate: vitess/lite:v19.0.0
9 vttablet: vitess/lite:v19.0.0
10 vtbackup: vitess/lite:v19.0.0
11 mysqld:
12 mysql80Compatible: vitess/lite:v19.0.0
13 vtctld: vitess/lite:v19.0.0
14
15 cells:
16 - name: zone1
17 gateway:
18 replicas: 2
19 resources:
20 requests:
21 cpu: 500m
22 memory: 512Mi
23
24 keyspaces:
25 - name: commerce
26 turndownPolicy: Immediate
27 partitionings:
28 - equal:
29 parts: 2
30 shardTemplate:
31 databaseInitScriptSecret:
32 name: commerce-schema
33 key: init_db.sql
34 tabletPools:
35 - cell: zone1
36 type: replica
37 replicas: 2
38 mysqld:
39 resources:
40 requests:
41 cpu: 500m
42 memory: 1Gi
43 dataVolumeClaimTemplate:
44 accessModes: ["ReadWriteOnce"]
45 resources:
46 requests:
47 storage: 10Gi
48
49 etcd:
50 createEtcdClusters:
51 zone1:
52 replicas: 3
53 resources:
54 requests:
55 cpu: 200m
56 memory: 256Mi
1kubectl apply -f vitess-cluster.yaml
对于本地测试,Vitess 也提供了 Docker Compose 方式:
1git clone https://github.com/vitessio/vitess.git
2cd vitess/examples/compose
3docker-compose up -d
3.4 VSchema 分片配置
Vitess 使用 VSchema 来定义分片策略:
1{
2 "sharded": true,
3 "vindexes": {
4 "hash": {
5 "type": "hash"
6 }
7 },
8 "tables": {
9 "users": {
10 "column_vindexes": [
11 {
12 "column": "user_id",
13 "name": "hash"
14 }
15 ]
16 },
17 "orders": {
18 "column_vindexes": [
19 {
20 "column": "user_id",
21 "name": "hash"
22 }
23 ]
24 }
25 }
26}
这里 users 和 orders 都按 user_id 的 hash 来分片,好处是同一个用户的订单和用户信息在同一个分片上,JOIN 查询不需要跨分片。
3.5 谁在用 Vitess?
Vitess 的用户名单非常亮眼:
- YouTube / Google: Vitess 的发源地,支撑着 YouTube 的全部 MySQL 流量
- Slack: 所有消息存储都跑在 Vitess 上
- GitHub: 用 Vitess 管理 MySQL 集群
- Square: 支付平台的核心数据库层
- PlanetScale: 基于 Vitess 的 DBaaS 产品
3.6 优缺点
优点:
- 经过超大规模生产验证(YouTube 级别)
- 原生支持 Kubernetes
- 在线 Resharding 能力强大
- VReplication 引擎灵活
- CNCF 毕业项目,社区活跃
缺点:
- 架构复杂,组件多,运维难度大
- 学习曲线最陡
- 只支持 MySQL
- 部分 MySQL 功能不支持(存储过程、触发器、外键等)
- 小规模场景下杀鸡用牛刀
- 本地开发环境搭建比较折腾
四、横向对比
来一张表格直观对比:
| 维度 | ShardingSphere | ProxySQL | Vitess |
|---|
| 开发语言 | Java | C++ | Go |
| 架构模式 | JDBC嵌入 / 独立代理 | 独立代理 | 分布式集群 |
| 支持数据库 | MySQL, PostgreSQL | MySQL (含Percona/MariaDB) | MySQL |
| 分库分表 | ✅ 核心功能 | ❌ 不支持 | ✅ 核心功能 |
| 读写分离 | ✅ 支持 | ✅ 核心功能 | ✅ 支持 |
| 连接池 | ✅ 基础 | ✅ 核心功能,效果最好 | ✅ VTTablet层 |
| 查询缓存 | ❌ | ✅ 支持 | ❌ |
| 查询防火墙 | ❌ | ✅ 支持 | ❌ |
| 分布式事务 | ✅ XA/BASE | ❌ | ✅ 两阶段提交 |
| 在线Resharding | ✅ 弹性扩缩容 | ❌ | ✅ 核心功能 |
| K8s原生 | 一般 | 一般 | ✅ 原生支持 |
| 学习曲线 | 中等偏高 | 低 | 高 |
| 社区活跃度 | 高 (Apache) | 高 (Percona) | 高 (CNCF) |
| 典型用户 | 京东、当当、转转 | 大量MySQL运维团队 | YouTube、Slack、GitHub |
| 最小部署规模 | 1个Proxy实例 | 1个ProxySQL实例 | VTGate+VTTablet+etcd |
五、选型指南:你应该选哪个?
场景一:Java 项目 + 需要分库分表
推荐:ShardingSphere
如果你的技术栈是 Java,而且主要需求是分库分表,ShardingSphere 是首选。用 JDBC 模式零额外开销,用 Proxy 模式也能服务非 Java 应用。
典型场景:电商订单表按用户 ID 分片,每个库放一部分用户的数据。
选择 ShardingSphere-JDBC 如果:
✓ 纯 Java 应用
✓ 对性能极致追求
✓ 不想多维护一个代理服务
选择 ShardingSphere-Proxy 如果:
✓ 多语言应用需要访问
✓ 想统一管理分片策略
✓ DBA 需要独立管理中间件
场景二:MySQL 主从 + 读写分离 + 连接管理
推荐:ProxySQL
如果你的 MySQL 已经搭好了主从,只需要做读写分离和连接池管理,ProxySQL 是最简单高效的方案。部署简单、配置灵活、资源占用少。
典型场景:Web 应用的数据库连接数暴涨,需要连接复用;读多写少的场景需要自动路由。
选择 ProxySQL 如果:
✓ 已有 MySQL 主从架构
✓ 不需要分库分表
✓ 想要最轻量的方案
✓ 需要查询级别的精细控制
✓ 运维团队偏好命令行管理
场景三:云原生 + 大规模水平扩展
推荐:Vitess
如果你的业务跑在 Kubernetes 上,数据量增长快,需要弹性的水平扩展能力,Vitess 是最佳选择。特别是当你的数据量和并发量达到一定规模后,Vitess 的优势会更加明显。
典型场景:SaaS 平台按租户分片,数据量从 TB 级增长到 PB 级。
选择 Vitess 如果:
✓ 跑在 Kubernetes 上
✓ 数据量大,需要水平扩展
✓ 需要在线 Resharding
✓ 团队有足够的技术实力
✓ 业务规模值得这个投入
决策流程图
六、能不能组合使用?
答案是:可以的。
一个常见的组合是 ProxySQL + ShardingSphere:
这个组合的优势:
- ProxySQL 负责前端连接管理、查询缓存和安全防护
- ShardingSphere 负责后端的分片路由和分布式事务
但要注意,组合使用也带来了额外的复杂度:
- 多了一跳网络延迟:每个请求要经过两层代理
- 故障排查更复杂:出问题时要排查两个组件
- 配置管理成本翻倍:需要维护两套配置
所以除非你确实同时需要两者的独有功能,否则不建议叠加使用。大多数场景下,选一个就够了。
另一个值得提的组合是 ProxySQL + Vitess,不过 Vitess 自带的 VTGate 已经覆盖了 ProxySQL 的大部分功能(连接池、查询路由),所以重叠度比较高,一般不推荐。
七、总结
最后用一句话概括每个工具的定位:
- ShardingSphere = 分库分表全家桶,Java 生态首选
- ProxySQL = MySQL 的轻量级智能管家,读写分离和连接池一把好手
- Vitess = 为超大规模而生的云原生方案,大厂标配
没有最好的中间件,只有最适合的。先搞清楚你的核心需求(分片?读写分离?连接管理?弹性扩展?),再结合团队技术栈和运维能力做决策。
如果你还是拿不定主意,我的建议是:
- 先从 ProxySQL 开始——它最简单,能解决 80% 的 MySQL 运维痛点
- 当你真的需要分库分表时,再考虑 ShardingSphere 或 Vitess
- 如果你的基础设施已经全面 Kubernetes 化,Vitess 值得认真评估
希望这篇文章能帮你在中间件选型上少走弯路。有问题欢迎留言讨论!
本章小结
本章介绍了以下核心内容:
- 前言:什么时候你需要数据库中间件?
- 一、ShardingSphere:Java 生态的分库分表利器
- 二、ProxySQL:MySQL 的瑞士军刀
- 三、Vitess:为超大规模而生
- 四、横向对比
- 五、选型指南:你应该选哪个?
- 六、能不能组合使用?
- 七、总结