SQL ShardingSphere 的 shadow 库流量染色测试实践

1次阅读

shadow路由不生效需排查四点:一是确认sql是否经分片引擎(开启sql-show日志验证);二是检查表名大小写及别名导致匹配失败;三是确保shadow-Property由业务显式注入(如setshadow(true));四是写操作需显式染色且配置shadow-insert-allowed=true。

SQL ShardingSphere 的 shadow 库流量染色测试实践

shadow 表路由不生效?先确认 shadow-rule 是否命中真实 SQL 路由路径

ShardingSphere 的 shadow 功能本质是「路由拦截」,不是请求代理。它只在 SQL 解析后、实际分片路由前介入,如果 SQL 没走分片引擎(比如直连单库、用了 default-data-source、或被 sql-comment-parser 误判为非标准 SQL),shadow 规则压根不会触发。

实操建议:

  • 开启 props.sql-show=trueprops.shadow-sql-show=true,观察日志里是否出现 [Shadow] Route to shadow data source 这类标记
  • 确保目标表名在 shadow-table-rules 中显式声明,且表名大小写与 SQL 中完全一致(ShardingSphere 默认区分大小写)
  • 避免在 SQL 中用别名覆盖原表名,例如 select * FROM t_order AS o —— 此时路由依据的是 o,而非 t_ordershadow 规则无法匹配

流量染色靠 shadow-property,但 header 透传失败很常见

ShardingSphere 不主动读取 http Header 或 rpc 上下文,shadow-property 的值必须由业务代码显式注入到 ThreadLocal 或通过 SPI 注入的 ShadowDataSource 上下文。常见错误是以为加个 X-Shadow:true 就能自动生效。

实操建议:

  • 使用 ShadowConnection.setShadow(true) 是最直接的方式,适合单元测试或脚本调用场景
  • Web 层需手动提取 header 并调用 ShadowProperty.setShadow(true),注意清理 ThreadLocal 防止污染下游线程
  • spring Cloud 微服务中,若用 OpenFeign,需配合 RequestInterceptor 把 shadow 标识透传到下游,否则染色只在第一跳有效

shadow-datasource 和真实数据源共存时,事务和连接泄漏风险高

ShardingSphere 的 shadow 数据源默认不参与分布式事务管理(如 Seata、XA),也不受 Spring @Transactional 的连接复用机制控制。一旦在同一个事务里混用 shadow 和非 shadow 数据源,极易出现连接未关闭、事务回滚不一致、甚至影子库写入真实库。

实操建议:

  • 禁止在 @Transactional 方法内同时操作 shadow 表和普通表;如需比对,改用「先查主库 → 再查影子库」的非事务方式
  • shadow-datasource 单独配置最小空闲连接数为 0,防止连接长期滞留(影子库通常只读/低频)
  • 监控 ShadowDataSourceactiveCount,异常升高往往意味着连接未释放,根源常是 try-with-resources 缺失或异步线程未正确传递上下文

测试验证阶段最容易忽略 shadow 的「只读豁免」逻辑

ShardingSphere 默认对 SELECT 自动启用 shadow 路由,但 INSERT/UPDATE/delete 必须显式染色才走影子库——这是为了防止脏写。很多团队测试时只跑查询,误以为 shadow 已全量生效,上线后才发现写流量没进影子库,比对失效。

实操建议:

  • 测试用例必须覆盖 INSERT INTO ... SELECT 类混合语句,这类语句会被判定为写操作,不染色就不进 shadow 库
  • SHOW SHADOW RULES 命令(ShardingSphere-Proxy)或 ShadowRuleConfiguration 的 API 检查当前规则是否启用了 shadow-insert-allowed=true(5.3.0+ 可配)
  • 影子库 DDL 同步不能依赖 ShardingSphere,必须单独维护;若影子库缺字段,INSERT 会直接报 column not found 错误,而非静默降级

事情说清了就结束。

text=ZqhQzanResources