当前位置: 首页 > news >正文

MySQL 分库分表实战

# MySQL 分库分表实战

数据量到了千万级,单表扛不住了,就要分库分表。这篇说说怎么做。

## 什么时候需要分库分表?

```
单表数据量:
- < 500万:不用分,加索引、优化 SQL
- 500万~2000万:考虑分表
- > 2000万:建议分表

单库容量:
- < 50GB:不用分库
- 50GB~200GB:考虑分库
- > 200GB:建议分库
```

## 分库 vs 分表

| 维度 | 分库 | 分表 |
|------|------|------|
| 对象 | 数据库级别 | 表级别 |
| 解决 | 单库连接数、IO瓶颈 | 单表数据量瓶颈 |
| 代价 | 跨库事务、JOIN困难 | 路由复杂 |
| 建议 | 先分表,不够再分库 | 优先考虑 |

## 垂直拆分

### 垂直分库:按业务拆库

```
mydb(单库)
├── user、order、product、log 全在一个库

user_db (用户库)
order_db (订单库)
product_db (商品库)
log_db (日志库)
```

### 垂直分表:按字段拆表

```sql
-- user 表字段太多,拆成两张
-- user_base:常用字段
CREATE TABLE user_base (
id BIGINT PRIMARY KEY,
name VARCHAR(50),
phone VARCHAR(20),
status TINYINT
);

-- user_ext:不常用字段
CREATE TABLE user_ext (
user_id BIGINT PRIMARY KEY,
bio TEXT,
avatar_url VARCHAR(200),
created_at DATETIME
);
```

## 水平拆分

### 水平分表:数据按规则分布到多张表

```
order 表 1000万数据 → 按 user_id 分成 4 张表
order_0:user_id % 4 = 0
order_1:user_id % 4 = 1
order_2:user_id % 4 = 2
order_3:user_id % 4 = 3
```

### 分片策略

| 策略 | 规则 | 优点 | 缺点 |
|------|------|------|------|
| Hash | user_id % N | 数据均匀 | 扩容麻烦 |
| Range | 按时间/ID范围 | 范围查询方便 | 数据可能不均匀 |
| 一致性Hash | Hash环 | 扩容只迁移部分 | 实现复杂 |

## ShardingSphere 实战

### Maven 依赖

```xml
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.4.0</version>
</dependency>
```

### 配置水平分表

```yaml
spring:
shardingsphere:
datasource:
names: ds0
ds0:
type: com.zaxxer.hikari.HikariDataSource
jdbc-url: jdbc:mysql://localhost:3306/mydb
username: root
password: 123456
rules:
sharding:
tables:
order:
actual-data-nodes: ds0.order_$->{(0..3)}
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: order-mod
sharding-algorithms:
order-mod:
type: MOD
props:
sharding-count: 4
props:
sql-show: true
```

```java
// 使用和普通 MyBatis 一样
@Mapper
public interface OrderMapper {
@Insert("INSERT INTO `order` (user_id, amount) VALUES (#{userId}, #{amount})")
void insert(Order order);

@Select("SELECT * FROM `order` WHERE user_id = #{userId}")
List<Order> selectByUserId(Long userId);
}
// ShardingSphere 自动路由到 order_0~3
```

### 配置水平分库

```yaml
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
jdbc-url: jdbc:mysql://192.168.1.100:3306/mydb
ds1:
jdbc-url: jdbc:mysql://192.168.1.101:3306/mydb
rules:
sharding:
tables:
order:
actual-data-nodes: ds$->{0..1}.order_$->{(0..3)}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: db-mod
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: table-mod
sharding-algorithms:
db-mod:
type: MOD
props:
sharding-count: 2
table-mod:
type: MOD
props:
sharding-count: 4
```

## 分布式ID

分库分表后,不能用自增主键了。

### 1. 雪花算法(推荐)

```java
// ShardingSphere 内置
// 配置:
spring:
shardingsphere:
rules:
sharding:
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 1

// 使用:
public class Order {
@TableId(type = IdType.ASSIGN_ID)
private Long id; // 自动生成分布式ID
}
```

### 2. UUID

```sql
SELECT UUID(); -- 36 位字符串
-- 缺点:太长、无序、索引性能差
```

### 3. Leaf(美团)

```
基于数据库号段 + ZooKeeper 的分布式ID方案
优点:高性能、趋势递增
```

## 分库分表后的 JOIN 问题

```sql
-- ❌ 跨表 JOIN 在 ShardingSphere 中有限制
SELECT * FROM order o JOIN user u ON o.user_id = u.id;
-- 如果 order 和 user 不在同一个库/表,无法直接 JOIN

-- ✅ 方案1:绑定表(分片键相同)
spring:
shardingsphere:
rules:
sharding:
binding-tables:
- order, order_item # 同一个 user_id 分片

-- ✅ 方案2:应用层组装
List<Order> orders = orderMapper.selectByUserId(userId);
List<Long> productIds = orders.stream().map(Order::getProductId).collect(Collectors.toList());
List<Product> products = productMapper.selectByIds(productIds);
// 内存中关联

-- ✅ 方案3:全局表(字典表广播到每个库)
spring:
shardingsphere:
rules:
sharding:
broadcast-tables:
- dict, config # 每个库都有完整数据
```

## 分页问题

```sql
-- ❌ LIMIT 在分表场景下有问题
SELECT * FROM order ORDER BY id LIMIT 1000000, 10;
-- 需要查询所有分片取 Top N,再合并,性能很差

-- ✅ 方案1:用上次的最大 ID
SELECT * FROM order WHERE id > #{lastId} ORDER BY id LIMIT 10;

-- ✅ 方案2:禁止深度分页
-- 业务层面限制只能翻前 100 页
```

## 扩容问题

```
从 4 张表扩到 8 张表怎么办?
- Hash 取模扩容:数据需要全量迁移
- 一致性Hash扩容:只迁移部分数据

推荐:初始分片数留余量(比如初始 16 张表,先只用 4 张)
```

## 小结

| 问题 | 解决方案 |
|------|---------|
| 拆分方式 | 先垂直再水平 |
| 分片策略 | Hash取模(简单) |
| 分布式ID | 雪花算法 |
| 跨表JOIN | 绑定表/应用层/全局表 |
| 分页 | 避免深度分页,用游标 |
| 扩容 | 初始留余量 |

---

**相关阅读:**
- [MySQL 读写分离实战]
- [MySQL 集群方案对比]
- [MySQL 一千万级数据分页查询]

http://www.jsqmd.com/news/876245/

相关文章:

  • MALA框架:机器学习加速大规模材料电子结构计算实战与优化
  • 医疗文本数据噪声对模型性能的影响:从错误率量化到模型选型实战
  • Frida Swift动态分析实战:突破iOS限制的可观测性方案
  • 小红书数据采集实战指南:Python自动化工具快速上手
  • OpenLDAP密码修改原理与实战:EXOP协议、ACL权限与ppolicy策略
  • Warcraft Helper终极指南:让魔兽争霸3在现代系统焕发新生
  • LLM在芯片设计优化中的应用与ORFS-agent创新架构
  • 分期乐京东e卡高价回收:2026年最新攻略! - 团团收购物卡回收
  • MySQL JOIN 优化详解
  • Frida Hook Java层还原Android客户端签名算法
  • Spectre与Meltdown漏洞:原理、影响与防护措施
  • Mermaid Live Editor:为什么每个开发者都需要这个实时图表编辑神器?
  • 分期乐京东e卡回收安全吗?三分钟了解回收全流程 - 团团收购物卡回收
  • 2026年亲测必备:10个论文降AI工具,免费将AI率降至5%以下(附避坑教程) - 降AI实验室
  • E7Helper第七史诗自动化助手:新手也能轻松上手的终极游戏解放方案
  • MySQL 子查询优化:从慢查询到飞起的实战之路
  • 长沙手表变现不被坑的密码,合扬本地老店实测封神 - 李宏哲1
  • PotPlayer字幕翻译插件:5分钟实现外语影视无障碍观看的终极免费方案
  • 专业级AMD Ryzen调试工具SMUDebugTool:深度解析与实战应用指南
  • 深入解析大模型架构之争:全能通用模型 vs 领域专精模型
  • WechatDecrypt终极指南:3步快速解密你的微信聊天数据库
  • CentOS 7上编译安装glibc 2.28,我踩过的那些坑(附完整排错流程)
  • 基于ASAR文件系统解析的WeMod客户端增强框架技术实现
  • Docker .dockerignore 完全指南
  • 教你在分期乐京东e卡回收平台上快速提现的秘诀 - 团团收购物卡回收
  • 揭秘分期乐京东e卡回收平台:快速变现的最佳选择 - 团团收购物卡回收
  • 安卓逆向实战:用Frida Hook Java层还原API-Sign签名算法
  • RDPWrap配置踩坑实录:更新rdpwrap.ini文件解决Listener state不支持问题
  • 【最新 v 2.7.5】从“手动搬砖“到“AI 代劳“:Windows 一键部署 Open Claw,效率差距就是这么拉开的
  • TeamSpeak 3权限与防火墙配置深度解析