# MySQL分库分表怎么分,这篇讲透了

3 阅读3分钟

单表数据量到了千万级,查询开始变慢,索引也救不了。这时候就得考虑分库分表了。

但分库分表不是拍脑袋就能干的,分不好反而更慢。今天把核心思路讲清楚。

什么时候需要分

先明确一个前提:能用索引优化解决的,别急着分库分表。

单表数据量建议
< 500万加索引、优化SQL
500万 - 2000万考虑归档历史数据、读写分离
> 2000万分库分表

这个数字不是绝对的,和字段数量、行大小、查询模式都有关系。但超过2000万行就该认真评估了。

垂直拆分

垂直分库:按业务拆,订单库、用户库、商品库分开。

单库
├── user表
├── order表
└── product表

拆成
├── 用户库:user表
├── 订单库:order表
└── 商品库:product表

垂直分表:把字段多的大表拆成主表和扩展表。

user表(20个字段)
↓
user_base(id, name, phone, status)
user_ext(id, avatar, bio, settings)

把不常查的大字段(text、blob)拆出去,主表更小,查询更快。

水平拆分

水平分表:同一个表的数据按规则分散到多张表。

order表(1亿行)
↓
order_0、order_1、order_2、order_3

水平分库:分到不同数据库实例,进一步分散压力。

分片键怎么选

这是分库分表最关键的一步,选错了后面很难改。

分片键优点缺点
用户ID查询天然带用户维度按订单号查要走全库
订单ID按订单查快查用户所有订单要扫全库
时间便于归档热点集中在新数据
哈希取模数据均匀扩容需要迁移

实际项目里常用基因法:订单号里嵌入用户ID的后几位,这样按订单号也能定位到分片。

常见分片算法

取模:

int shard = userId.hashCode() % 4;
// 简单,但扩容时几乎所有数据都要迁移

范围:

// 按ID区间分
if (id < 10000000) return "db0";
else if (id < 20000000) return "db1";

一致性哈希:扩容时只迁移部分数据,但实现复杂。

雪花算法+基因位:ID本身携带分片信息,最常用。

分库分表带来的问题

问题说明方案
跨库JOIN不能直接JOIN冗余字段、应用层组装
分布式事务跨库写入要保证一致Seata、最终一致性
分页跨库分页很麻烦二次查询法、禁止深分页
全局ID自增ID会冲突雪花算法、号段模式
聚合统计count/sum要汇总汇总表、离线计算

什么时候用中间件

手写分片逻辑维护成本高,一般用中间件:

  • ShardingSphere:生态好,支持分库分表、读写分离、数据加密
  • MyCat:老牌代理式中间件
# ShardingSphere配置示例
spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..3}
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: order-mod

实施顺序建议

  1. 先做读写分离,缓解读压力
  2. 再做垂直拆分,按业务解耦
  3. 最后做水平分表,解决单表过大
  4. 历史数据归档,优先于分表

别一上来就上分库分表,架构复杂度会陡增。能用简单方案解决的,就别给自己找麻烦。