先把结论放在前面如果你的系统正在面临单库容量焦虑数据量眼看着要冲破几千万甚至上亿行同时你又不希望把每个业务的数据库访问代码全部翻出来改一遍那 ShardingSphere Proxy 这套东西值得花半天时间认真体验一下。这篇文章是我自己从零开始部署配置 ShardingSphere Proxy 5.4.1 的完整记录包括为什么选 Proxy 而不是 JDBC 形态、部署步骤、第一个分库分表配置怎么写、以及我在实际过程中踩过的坑和解决思路最后还有一段运维层面的个人建议。1. 为什么选 Proxy 而不是 JDBC1.1 两种形态的本质区别ShardingSphere 目前有两条主力产品线一条是 ShardingSphere-JDBC一条就是标题里这个 ShardingSphere-Proxy。这两者的定位差异很大很多第一次接触的人会纠结到底该学哪个其实不用纠结先看你的使用场景。JDBC 形态是一个轻量级的 Java 框架说白了就是给应用层提供一个特殊的 DataSource让你的业务代码在发起 SQL 时由这个 jar 包内的引擎完成分片、路由、改写、归并。它不部署独立服务性能开销相对小但有个天然限制它是 Java 语言绑定的只有 Java 项目能用而且每个应用都要各自引入、各自配置属于“应用内治理”。Proxy 形态则完全不同它是一个独立部署的数据库代理服务对外模拟出一个 MySQL/PostgreSQL 协议的数据库端口业务方完全不需要感知后端有几台数据库。你只要把数据库连接地址改成 Proxy 的地址SQL 到了 Proxy 之后由它根据规则把请求分发到真正的后端数据库节点上。我这次的场景就很典型一套老系统服务端有 Java 写的订单模块有 Python 写的报表模块还有一点 Go 的定时任务数据库是多个独立的 MySQL 实例。这种多语言环境根本没法用 JDBC 形态统一治理而 Proxy 只需要部署一个节点所有语言、所有服务都通过 MySQL 协议连过来天然解决了语言绑定问题。1.2 Proxy 适合什么样的团队从我实操的感受来说Proxy 更适合这几类人一是团队里有专职 DBA 或者基础设施组希望把分库分表规则集中管控不让业务开发各自为政二是系统从单库迁移到分库分表但不想大改应用代码三是后端语言比较杂没法统一引入某个 jar 包四是需要快速验证分库分表效果想先搭一个代理层试试水再决定要不要长期采用。它也有代价。最明显的是多一跳网络应用访问 Proxy 再访问后端 MySQL链路变长网络延迟会增加。如果业务 QPS 很高Proxy 本身会成为新的瓶颈需要额外的机器资源和监控手段。另外它的配置复杂度并不低虽然官方文档做得还算清晰但遇到边界场景比如子查询、多表关联、跨库事务还是要求使用方对分片原理有一定理解。1.3 版本选择与目录结构官方最新版本迭代很快但我个人建议首次体验优先选 5.4.x 这个稳定版本比如 5.4.1。这类中间件不一定越新越好新版本引入的新特性如果社区案例少出了冷门问题排查成本很高。5.4.x 的配置体系和 5.3、5.5 差别不大网上资料也相对充分适合入门。下载好官方发布包解压之后你会看到这样一个目录结构apache-shardingsphere-5.4.1-shardingsphere-proxy-bin/ ├── bin/ # 启停脚本 start.sh / stop.sh ├── conf/ # 全部配置文件所在地 ├── lib/ # Proxy 自身的依赖包 ├── ext-lib/ # 放后端驱动等扩展 jar 的位置 └── logs/ # 启动后生成的日志目录首次接触的人常犯一个错直接编辑 conf 目录下的文件却不知道 Proxy 启动时到底加载了哪些配置。记住一条主线server.yaml是它的基础配置定义了认证用户、模式、全局属性config-*.yaml是你的业务分片或读写分离规则。理解了这个分工后面改配置就不会一头雾水。2. 部署前先把这几件事说清楚2.1 环境准备与依赖部署 Proxy 前你需要先确认环境满足条件。Java 版本是硬性要求推荐 OpenJDK 118 也能跑但 11 的 GC 表现和兼容性更好。后端数据库我用的是两个 MySQL 5.7 实例Proxy 支持 5.7 和 8.0 都没问题但如果你的后端是 8.0建议把 MySQL 驱动也升级到 8.x并放到ext-lib目录里避免驱动版本过旧导致的通信协议问题。客户端方面本地准备好mysql命令行即可。注意 Proxy 默认监听 3307 端口如果你本机的 3307 被占用可以在启动脚本里通过环境变量改端口。2.2 server.yaml 的配置逻辑server.yaml是 Proxy 启动时首先读取的全局配置。我这次用的是 Standalone 模式也就是单机文件存储如果之后要搞集群部署就需要切到 Cluster 模式把注册中心信息塞进去。第一个核心配置是权限与用户5.4.x 默认开启了权限校验。这里有个容易踩坑的点5.1 之前的版本默认不带认证你拿旧经验去配新版本会发现怎么连都连不上。rules: - !AUTHORITY users: - root%:root privilege: type: ALL_PERMITTED它表示允许任何来源的用户root使用密码root连接 Proxy并且拥有全部权限。生产环境别这么干但本地体验就没问题。第二个核心配置是全局属性。我通常会调整三个参数props: kernel-executor-size: 20 sql-show: true proxy-frontend-database-protocol-type: MySQLkernel-executor-size决定后端 SQL 执行的线程池大小默认值偏保守压测时容易出现连接等待。sql-show是调试阶段的救星打开后 Proxy 会在日志里打印每条 SQL 最终路由到哪台库、哪张表的实际执行语句。proxy-frontend-database-protocol-type则声明代理层对外用哪个数据库协议这里填 MySQL。2.3 配置文件的关系梳理很多人刚开始看 conf 目录会懵因为里面有server.yaml、config-sharding.yaml、config-readwrite-splitting.yaml等一堆文件。其实 Proxy 的加载策略很清晰它启动时只读取server.yaml而业务规则文件是靠文件名前缀config-自动识别的。你可以在 conf 里放好几个config-*.yaml同时启用分片、读写分离、数据加密等不同功能Proxy 启动时会自动合并。但为了排查问题省心建议一个规则文件只干一件事。我这次的体验就只用了config-sharding.yaml专门做分库分表。3. 五步搭起第一个分库分表示例3.1 第一步先设计分片策略如果你的后端目前只有一个库想要体验分库分表最简单的是准备两个 MySQL 实例或一个实例建两个库。我这边用了两个独立实例分别叫sharding_ds0和sharding_ds1每个库里都建了t_order_0和t_order_1两张表。分片策略我采用了常见的双维度设计数据库分片键是user_id用它取模 2 决定数据落到哪个库表分片键是order_id用它取模 2 决定数据落到哪张表。这样设计的好处是查询时如果带上user_idProxy 能直接定位到某一个库只做单库路由效率最高。要注意的是表分片键用的是order_id为了演示两种不同的路由维度实际生产里也可以把两个维度都放在user_id上。3.2 第二步后端建库建表先在两个实例上执行同样的建库建表 SQLCREATE DATABASE sharding_ds0 DEFAULT CHARACTER SET utf8mb4; CREATE DATABASE sharding_ds1 DEFAULT CHARACTER SET utf8mb4; CREATE TABLE t_order_0 ( order_id BIGINT NOT NULL, user_id BIGINT NOT NULL, status VARCHAR(50) DEFAULT NULL, create_time DATETIME DEFAULT NULL, PRIMARY KEY (order_id) ); CREATE TABLE t_order_1 ( order_id BIGINT NOT NULL, user_id BIGINT NOT NULL, status VARCHAR(50) DEFAULT NULL, create_time DATETIME DEFAULT NULL, PRIMARY KEY (order_id) );这一步容易忽略一个细节t_order_0和t_order_1必须同时存在于sharding_ds0和sharding_ds1两个库里否则 Proxy 在路由时会因为找不到目标表直接报错。3.3 第三步编写分片配置文件在 conf 目录下新建或修改config-sharding.yamlschemaName: sharding_db dataSources: ds0: url: jdbc:mysql://127.0.0.1:3306/sharding_ds0?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root ds1: url: jdbc:mysql://127.0.0.1:3306/sharding_ds1?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root rules: - !SHARDING tables: t_order: actualDataNodes: ds$-{0..1}.t_order_$-{0..1} databaseStrategy: standard: shardingColumn: user_id shardingAlgorithmName: order_db_inline tableStrategy: standard: shardingColumn: order_id shardingAlgorithmName: order_table_inline keyGenerateStrategy: column: order_id keyGeneratorName: snowflake shardingAlgorithms: order_db_inline: type: INLINE props: algorithm-expression: ds$-{user_id % 2} order_table_inline: type: INLINE props: algorithm-expression: t_order_$-{order_id % 2} keyGenerators: snowflake: type: SNOWFLAKE重点解释几个关键配置项。actualDataNodes用了表达式ds$-{0..1}.t_order_$-{0..1}意思是后端共有ds0、ds1两个数据源每个数据源下各有t_order_0、t_order_1两张表。这个表达式很灵活后续如果你的库表扩展到ds0到ds9只需要把{0..1}改成{0..9}不需要写十行配置。algorithm-expression里的$-{}就是取模计算。ds$-{user_id % 2}表示对user_id取模 2结果 0 走 ds0结果 1 走 ds1。表策略同理对order_id取模。keyGenerateStrategy定义了分布式主键生成器这里用了 SNOWFLAKE 雪花算法。注意它的顺序问题我在表分片策略里同时用order_id做分片键而order_id又由雪花算法生成这在生产环境要特别留神。3.4 第四步启动 Proxy 并连接保存配置文件后进入 bin 目录启动# 如果不想用默认端口 3307可以显式指定 PORT3307 ./bin/start.sh第一次启动时先别急着测试盯着logs/stdout.log看日志。启动成功的标志是日志里出现类似“ShardingSphere-Proxy ... start successfully”的信息。然后本机用 MySQL 客户端连接mysql -h127.0.0.1 -P3307 -uroot -proot这里有一个小提示Proxy 和真实 MySQL 一样连接后需要切换到目标逻辑库才能操作业务表。Proxy 并没有真正创建物理库它只是在逻辑层面暴露了一个名为sharding_db的 schema。USE sharding_db; SHOW TABLES;如果一切正常你会在SHOW TABLES结果里看到t_order。注意它显示的是逻辑表名也就是你在配置里定义的表名而不是t_order_0、t_order_1这种物理表。3.5 第五步验证路由效果连接建立后插入几条数据验证分片是否生效INSERT INTO t_order (order_id, user_id, status) VALUES (1, 1, NEW); INSERT INTO t_order (order_id, user_id, status) VALUES (2, 2, PAID); INSERT INTO t_order (order_id, user_id, status) VALUES (3, 3, DONE);因为sql-show已经打开Proxy 日志里会打印出每条 SQL 的具体路由目标。比如第一条数据user_id1按user_id % 2会落到ds1order_id1按取模 2 会落到t_order_1。日志里实际执行的就是INSERT INTO t_order_1 (order_id, user_id, status) VALUES (1, 1, NEW)再去后端两个库分别查询你就能看到数据确实被分散到了不同的物理表和物理库中。这就是分库分表最直观的效果。4. 实际体验中的高频踩坑记录4.1 权限校验版本差异我在第一次启动 5.4.1 时配置里只写了rules: - !AUTHORITY但忘了给用户配置密码结果用客户端连接时一直报认证失败。这个问题的根源在于 ShardingSphere 在 5.1 之后默认开启了权限校验而网上大量旧教程都停留在“无需认证、直接连接”的阶段。解决方案就是按照 5.4.x 的规范在server.yaml里明确写上rules: - !AUTHORITY users: - root%:root privilege: type: ALL_PERMITTED生产环境强烈建议对用户做最小权限控制比如只允许访问特定 schema别用ALL_PERMITTED。4.2 jdbcUrl 里时区与 SSL 的坑配置后端数据源时URL 里如果不加serverTimezone连接 MySQL 5.7 时经常会因为时区问题抛异常。另外很多本地 MySQL 实例其实没配 SSL 证书但驱动默认会尝试 SSL 握手偶尔会拖慢连接甚至报错。我最终的连接串是这样的url: jdbc:mysql://127.0.0.1:3306/sharding_ds0?useSSLfalseserverTimezoneAsia/Shanghai这两个参数看起来不起眼但不加你很可能在启动阶段就卡住日志里全是通信链路相关的报错。如果你用的是 MySQL 8.0还可以加allowPublicKeyRetrievaltrue因为 8.0 默认用 caching_sha2_password 认证某些连接方式下需要显式允许公钥获取。4.3 执行线程池太小导致连接等待体验阶段数据量小没什么感觉。但我在压测时发现并发上到一定程度Proxy 的响应时间突然飙升。排查了半天最后定位到kernel-executor-size太低。这个参数控制着 Proxy 处理 SQL 的后端执行线程数。默认值很小如果你的应用并发较高线程池被打满后所有 SQL 都在排队表现就是“连上了但查询极慢”。我调整为 20 之后压测吞吐有明显提升。这里没有万能值你要根据实际并发情况动态调整但起步至少给到 16 或 20。4.4 分片键缺失会直接报错分片策略定义了分片键之后Proxy 对 SQL 的解析就会变得很严格。我第一次测试时写了一条没有带order_id的插入语句INSERT INTO t_order (user_id, status) VALUES (1, NEW);结果直接被拒报错信息大致是 Sharding column cant be neither null nor empty。原因很简单没有分片键Proxy 不知道这条数据该路由到哪张表。这说明一个问题应用层如果存在“批量插入但没带分片键”的代码接入 Proxy 后必须改造。想绕过这个限制要么在 SQL 里带上分片键要么使用强制路由功能也就是通过 Hint 指定目标库表。强制路由属于高级用法建议后面再深入学习。4.5 雪花 ID 取模分表的一个隐患我在配置表分片时用了order_id % 2而order_id用的是 SNOWFLAKE 生成。雪花算法的 ID 由时间戳、机器位、序列号组成它的低位并不是均匀随机的而是每毫秒递增或具周期性。如果你测试几条数据恰好都落在同一个表不要慌这不是 Bug是取模结果本来就不均匀。在生产环境如果表分片键是这种雪花 ID我更建议用哈希类型的分片算法或者把雪花 ID 再加工处理后再参与取模避免单表热点。这个知识点普通教程不会明说但在做容量规划时影响不小。4.6 单表、广播表和分片表要分开认知Proxy 里不是所有表都需要分片。比如配置字典表每台后端库里都存一份完全相同的数据这类表应该配置为广播表而某些不需要分片的大表可以配置为单表。我这次体验只创建了t_order的分片规则如果我再去后端某一张库里手动建了t_user表Proxy 能不能直接查询它答案是能Proxy 默认会对未配置分片规则的表做单表装载但要求所有数据源里的表结构保持一致。否则查询时容易出现表不存在的报错。如果你有“每个库表结构不同”的特殊情况最好明确配置单表规则把表归属到具体数据源避免路由混乱。4.7 子查询和跨库关联的限制分库分表之后SQL 能力是有损的。我在验证阶段跑过一条多表 JOIN 的 SQL虽然语法上没毛病但 Proxy 在后台需要对多个分片返回的数据做内存归并数据量一大性能就不可控。更复杂的子查询在某些版本里甚至直接解析失败。这不是 ShardingSphere 独有的问题任何分库分表中间件都对复杂 SQL 有限制。我的建议是接入 Proxy 之前先把应用的 SQL 全面体检一遍把关联查询改成单表查询加应用层拼接或者引入宽表、冗余字段方案。这项工作越早做越主动。5. 运维管理层面的实话与建议5.1 慢 SQL 定位思路接入 Proxy 后SQL 实际是在后端 MySQL 上执行的所以定位慢 SQL 的逻辑要转变一下。后端 MySQL 的慢查询日志与真实执行时间相关而 Proxy 日志则能显示路由过程。遇到慢查询我的排查路径是先在 Proxy 日志里找到这条 SQL 路由到了哪个数据源再去对应 MySQL 的慢日志或执行计划里分析。如果你不搞清楚“哪条 SQL 落在了哪台库”很可能会被分布式环境下的日志割裂搞得一头雾水。5.2 优雅关停与重启Proxy 的bin/stop.sh会做优雅退出尽量处理完正在执行的请求后再关闭。我看过有人在压测时直接kill -9Proxy 进程导致后端 MySQL 出现大量半连接和异常中断。实际运维中如果实在要强杀也要先摘流量再杀进程。重启时要确认配置文件没有语法错误。YAML 对缩进极其敏感一个缩进不一致轻则配置项被忽略重则启动直接失败。有个土办法改完配置后先启动立刻看logs/stdout.log如果出现解析异常基本都是缩进问题。5.3 监控与容量规划Proxy 本身是一个可水平扩展的无状态服务这算是它很大的优势。如果单节点性能不够可以横向再加一台 Proxy前面用负载均衡把流量分过去。但不要忽略后端连接数Proxy 每接收一个前端连接会在后端维持一个物理连接连接数多了对 MySQL 也有压力。建议上线前关注三块指标Proxy 节点 CPU、内存、线程池饱和度后端 MySQL 连接数变化路由到各个分片的数据量是否均匀。数据倾斜是分库分表后期最容易踩的雷刚开始看不出来运行一两个月分片差异会越来越大。5.4 上线前我的个人习惯以我这次体验的体会来说如果你打算在生产引入 ShardingSphere Proxy有几点基础工作值得认真做。第一抽出一周时间把核心业务 SQL 全部在 Proxy 上跑一遍回归不要只测开发自测的几条简单语句。第二把sql-show在生产环境关掉不然日志量会非常大磁盘容易被写爆。第三所有分片键列一定要加索引因为路由查询和归并排序都对索引敏感。第四先从小流量灰度开始不要一次性全量切过去Proxy 值得信任但任何中间件都经不起大流量突袭。我在实际体验中的核心体会是ShardingSphere Proxy 真正解决的问题是把分库分表从“代码改造问题”变成了“配置和运维问题”。它不能凭空消灭分片带来的 SQL 限制但它确实能帮多语言团队、存量系统低成本地迈过数据库容量这道坎。最后再分享一个实用的小技巧连接上 Proxy 之后如果你想临时切换逻辑库不用反复断开重连可以直接执行USE sharding_dbSha