当前位置:首页>排行榜>什么时候需要分库分表?分片键怎么选?分完之后跨库查询怎么办?

什么时候需要分库分表?分片键怎么选?分完之后跨库查询怎么办?

  • 更新时间 2026-10-09 03:54:47
什么时候需要分库分表?分片键怎么选?分完之后跨库查询怎么办?

聊到架构,分库分表是必答题。常见问法:什么时候需要分库分表?分片键怎么选?分完之后跨库查询怎么办?

今天把这几件事讲透。

一、先别急着分:能不分就不分

分库分表是最后手段,因为它会带来一大堆新问题。在此之前,有几件事更便宜:

  • 加索引、优化慢 SQL
    。很多时候慢是因为没走索引,不是数据量太大。
  • 读写分离
    。读压力大就加从库,写入没到瓶颈之前不用分。
  • 冷热分离 / 归档
    。把历史数据挪到归档表或离线库,主表只留热数据。
  • 上缓存
    。热点数据挡在 Redis 前面,数据库压力能降一个量级。

判断信号也很明确:单表数据量过大导致查询明显变慢,或者单库的写入/QPS 顶到硬件上限,这时候再考虑分。

二、两种拆法:垂直和水平

垂直拆分是按业务或字段拆:

  • 垂直分库:按业务域拆成用户库、订单库、商品库,互不干扰。
  • 垂直分表:把大字段(text、blob)或低频字段拆到扩展表,主表更“瘦”,查询更快。

水平拆分是按数据行拆:

  • 水平分表:同一个库内,把一张表按规则拆成 user_0、user_1……
  • 水平分库:再进一步,拆到不同数据库实例上。

实践中通常是先垂直(按业务拆库)→ 再水平(大表拆小表)。

三、分片键怎么选,这是最关键的一步

分片键决定了数据落在哪个分片,选错了后面全是麻烦。三条经验:

1. 高基数、分布均匀。用性别、状态这种只有几个值的字段,数据会全挤在一个分片里。

2. 大多数查询都能带上它。

分片键最好就是业务查询的必经条件,比如订单按 user_id 分,那“查我的订单”就只打一个分片。

3. 尽量避免跨分片。如果按 user_id 分片,却经常要按 order_id 查,那每次都得扫全部分片。

常见的替代方案是基因法:把分片键的信息“嵌入”到另一个 ID 里,让两个维度都能定位到同一个分片。

// 水平分表路由:按 user_id 取模定位表
int idx = Math.abs(userId.hashCode()) % 64;
String table = "orders_" + idx;
String sql = "select * from " + table + " where user_id = ?";

四、分片算法

  • 范围分片
    :按 ID 或时间区间分。扩容方便,但容易热点(比如按时间分,写入全压在新分片上)。
  • 哈希取模
    :hash(key) % 分片数。分布均匀,但扩容时要重新分布数据,非常痛。
  • 一致性哈希
    :减少扩容时的数据迁移量,配合虚拟节点解决分布不均。

所以很多团队宁可预留足够多的分片(比如一开始就分 64 张表),避免后期扩容。

五、分完之后必须面对的五个麻烦

1. 跨库 JOIN。分片后 JOIN 基本不可用,只能在应用层组装,或者做冗余字段。

2. 跨库事务。本地事务管不了多个库,需要分布式事务或最终一致方案。

3. 分页与排序。limit 100, 10 这种要各个分片都查一遍再归并,越翻到后面越慢。

4. 全局唯一 ID。自增主键不再全局唯一,得换成雪花算法、号段模式或者 Redis 自增。

5. 扩容与数据迁移。怎么平滑地把数据搬到新分片,是个大工程。

六、中间件还是自己写

  • 客户端分片
    :应用里自己做路由(ShardingSphere-JDBC 这类)。少了网络一跳,但和代码耦合。
  • 代理层分片
    :ShardingSphere-Proxy、MyCat 这类独立服务。应用无感,但多一层转发和运维成本。

选哪个看团队:要简单可控就自己写,要透明通用就上中间件。

一句话记忆

1. 分库分表是最后手段,先试索引优化、读写分离、冷热分离、缓存。

2. 先垂直拆业务,再水平拆大表。

3. 分片键要高基数、分布均匀、且是查询必经条件。

4. 范围分片易热点,取模分布均匀但扩容痛,一致性哈希折中。

5. 分完要面对:跨库 JOIN、跨库事务、分页排序、全局 ID、数据迁移。

分库分表难的不是“怎么分”,而是“分完之后的那堆问题你打算怎么接”。面试时能主动说出这些坑,比只会背拆法的人靠谱得多。

随机文章