
分库后 JOIN 查不到数据,是因为 SQL 路由失效
分库之后,跨库 JOIN 天然不支持——中间件(如 ShardingSphere、MyCat)或自研路由层通常只按分片键路由单表查询,遇到多表 JOIN 且涉及不同库时,要么报错,要么只查一个库、结果为空或错乱。
常见错误现象:ShardingSphere-JDBC 报 UnsupportedOperationException: Cannot support multiple tables with different data sources in JOIN;MyCat 返回空结果但无报错;应用层查出的数据明显缺失关联字段。
- 优先改写业务逻辑:用「两次查询 + 应用层组装」替代
JOIN,比如先查订单列表(带user_id),再批量查用户信息 - 若必须
JOIN,确保所有参与表都按同一分片键(如user_id)分库,且路由规则一致,否则中间件无法对齐数据源 - 避免在分库场景下使用全局表(如
dict_type)与分片表做JOIN,部分中间件不自动广播查询,需显式配置broadcast-tables
sharding-jdbc 的 standard 和 complex 分片策略区别在哪
standard 只支持单分片键(如 order_id),对应一个 sharding-column 和一个分片算法;complex 支持多列组合(如 user_id + create_time),但要求所有查询必须包含全部分片列,否则路由失败或全库扫描。
使用场景:standard 适合用户中心类系统(按 user_id 分);complex 常用于订单归档(按 user_id 分库 + create_time 分表),但代价是 WHERE create_time > ? 单独出现时,会遍历所有库。
-
standard策略下,select * FROM order WHERE user_id = 123路由精准;但WHERE order_id = 456会全库扫 -
complex策略下,WHERE user_id = 123 AND create_time BETWEEN '2023-01' AND '2023-06'才能准确定位,缺一不可 - 性能影响:复杂策略提升写入局部性,但大幅增加查询条件约束,线上误写
WHERE条件极易引发慢查询
分表后 MAX(id) 或 ORDER BY id DESC LIMIT 1 拿不到最新记录
分表后 id 不全局唯一,各表独立自增,MAX(id) 返回的是当前表最大值,不是整个逻辑表最新写入记录;ORDER BY id DESC LIMIT 1 同理,只查单表,可能漏掉其他分表里更晚插入的高 id 记录。
典型错误:用 id 当时间序标识做同步、轮询或翻页,导致漏数据或重复处理。
- 正确做法是引入逻辑时间字段,如
create_time(精度到毫秒),并确保写入时严格单调;查询用ORDER BY create_time DESC LIMIT 1 - 如果必须依赖递增编号,改用分布式 ID 生成器(如
snowflake、leaf),确保全局有序,再按该字段分片 - 避免在分表场景下用
SELECT LAST_INSERT_ID()获取刚插入记录的完整状态——它只返回当前物理表的 ID,不反映路由后的实际归属
跨分片 count(*)、GROUP BY 性能差,根本原因不是 SQL 写法
这类聚合操作慢,不是因为没加索引或没写对 SQL,而是中间件必须把请求下发到所有分片,拉回全部原始数据,在内存中合并计算。数据量大时,网络传输和应用层 CPU 成瓶颈,比单库慢数倍甚至十倍。
兼容性影响明显:ShardingSphere 5.x 默认开启 merge 引擎,但超过 10 个分片 + 百万级行时,jvm 常因 OOM 中断;MyCat 则可能直接超时断连。
- 能预计算就预计算:比如用定时任务更新
daily_order_count表,而不是每次查SELECT COUNT(*) FROM order WHERE create_time > ? - 接受近似结果:用 HyperLogLog 或采样(如
LIMIT 10000后统计)替代精确聚合,尤其用于监控看板 - 强制下推失败时,检查是否用了不支持下推的函数,如
DATE_FORMAT(create_time, '%Y-%m')会导致整个GROUP BY无法下推,改用create_time >= ? AND create_time 区间过滤
分库分表不是银弹,真正难的从来不是怎么切,而是切完之后,那些你以为“还是原来那样查”的地方,全都悄悄变了规则。