Administrator
发布于 2022-07-02 / 4903 阅读
135

数据库分库分表后的跨库查询方案

分库分表后,运营要查"跨商家跨库的订单"

我们订单库按 user_id 哈希分了 16 个库、每库 64 张表。单用户的订单查询很顺,因为都在同一个分片。直到运营提了个需求:"给我看最近 30 天、金额大于 5000、状态是退款中的所有订单"——这个查询跨了所有库所有表,没有分片键可路由。组员在周会上问:"这种查询到底怎么搞?"我把四种常见方案摊开讲了讲。

方案一一:绑定表(Binding Table)

如果跨库查询发生在两表 Join 且分片规则一致的场景,可以用绑定表。比如订单表 t_order 和订单明细 t_order_item 都按 order_id 分片,它们必然落在同一分片,Join 不会被打散。

# ShardingSphere 配置:两表按同一分片键绑定
rules:
  - !SHARDING
    bindingTables:
      - t_order, t_order_item
    shardingAlgorithms:
      order-alg:
        type: INLINE
        props:
          algorithm-expression: t_order_${order_id % 64}

适用面窄:只解决"同分片键的 Join",对开头那个"无分片键的全局检索"没用。

方案二:广播表(Broadcast Table)

有些小表(字典表、地区表、商品类目)在每个分片都要用到,且数据量小、变更少。把它们设成广播表,每个库都存全量,Join 时本地就有:

broadcastTables:
  - t_region        # 地区字典,所有分片各存一份
  - t_category      # 类目字典

我们订单库里的"地区字典"就是广播表,运营按地区筛选时能本地 Join。但要注意:广播表一旦数据量大或变更频繁,同步成本很高,只适合小且稳的表

方案三:异构索引(冗余分片键)

开头的全局检索,本质是"没有分片键也要查"。异构索引的思路是:再建一张按查询维度分片的索引表。比如为"按商家查订单"建一张 t_order_merchant_index,以 merchant_id 分片,只存 (merchant_id, order_id)。查某商家订单时,先走索引表拿到 order_id 列表,再用 order_id 回原表取详情。

-- 先按 merchant_id 分片路由,拿到订单主键
SELECT order_id FROM t_order_merchant_index
WHERE merchant_id = 5521 AND amount > 500000;  -- 金额存索引里可过滤

-- 再用 order_id 回原分片取完整订单
SELECT * FROM t_order_${order_id % 64} WHERE order_id IN (...);

代价是写的时候要双写:订单落库同时写索引表。我们用了 ShardingSphere 的阴影库加本地事务表保证最终一致,多一次写约增加 3~5 ms。适合"读远多于写"的查询。

方案四:ES 二级索引

运营那类"多条件组合 + 范围 + 排序"的检索,最对路的还是扔给 Elasticsearch 8.x 做二级索引。订单写入 MySQL 的同时,通过 Canal 订阅 binlog 异步同步到 ES,ES 按业务查询维度建索引:

{
  "mappings": {
    "properties": {
      "order_id":   { "type": "keyword" },
      "merchant_id":{ "type": "keyword" },
      "amount":     { "type": "scaled_float", "scaling_factor": 100 },
      "status":     { "type": "keyword" },
      "create_time":{ "type": "date" }
    }
  }
}

查询直接打 ES,毫秒级返回订单主键,再回 MySQL 取详情。我们运营后台现在 90% 的复杂检索都走 ES,MySQL 只承担点查和写入。

四种方案怎么选

方案适用场景代价
绑定表同分片键两表 Join几乎无
广播表小且稳的字典表写时多库同步
异构索引单一新维度检索双写 + 一致性
ES 二级索引多条件组合检索引入 ES + 同步链路

小结

  • 分库分表解决了单机容量,但把"跨分片查询"变成了新难题。
  • 绑定表/广播表只解决特定 Join,救不了无分片键的全局检索。
  • 异构索引适合单一新维度;ES 二级索引适合灵活组合查询,是目前运营类需求的主力。
  • 任何"冗余一份"的方案都要想清楚写放大和一致性边界。

那次周会之后,我们定的原则是:点查走分片,组合检索走 ES,二者都不行的特殊 Join 才考虑异构索引。没有银弹,只有按查询模式选对路。

参考