MySQL 分库分表:数据库的"豪宅改造计划"
MySQL 分库分表:数据库的"豪宅改造计划" ?️
当你的单身公寓住不下所有家具时,你会怎么做?搬到大房子或者拆分成多个小仓库!MySQL 的数据也有同样的烦恼...
什么是分库分表??
分库分表是指将原本存储在单个数据库和单张表中的数据,按照特定的规则切分到多个数据库或多个表中,以解决海量数据存储和访问的性能问题。简单来说:当数据量太大,一个"房子"住不下时,要么买更多房子(分库),要么在同一个房子里隔出更多房间(分表)。
为什么要"搬家"??
场景:拥挤的公寓
数据库:"我实在太拥挤了,每次查询都要翻箱倒柜!"
DBA:"是时候搬家或者做个大扩建了!"
开发者:"但搬家好麻烦啊..."
DBA:"比你的系统崩溃容易处理多了!"
主要原因:
- 单表数据量过大 - 一张表超过千万级别,查询性能急剧下降
- 并发访问压力大 - 一个 MySQL 实例扛不住高并发
- 存储容量受限 - 单机硬盘总有极限
- 备份恢复时间长 - 几百 G 的数据备份一次要到天亮
分库分表的两大流派 ?
1️. 垂直拆分 - "按功能区分不同的房子"
场景:家庭空间规划
家长:"厨房用品放厨房,卧室用品放卧室,不要混在一起!"
数据表:"我们按业务功能分开住,互不干扰!"
垂直分库
做法:按业务模块将不同表拆分到不同的数据库中
案例:用户库、订单库、商品库分开部署
搬家前:
┌────────────────────┐
│ 大杂烩数据库 │
│ ┌──────┐ ┌──────┐ │
│ │用户表 │ │订单表 │ │
│ └──────┘ └──────┘ │
│ ┌──────┐ ┌──────┐ │
│ │商品表 │ │评论表 │ │
│ └──────┘ └──────┘ │
└────────────────────┘
搬家后:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户库 │ │ 订单库 │ │ 商品库 │
│ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │
│ │用户表 │ │ │ │订单表 │ │ │ │商品表 │ │
│ └──────┘ │ │ └──────┘ │ │ └──────┘ │
└──────────┘ └──────────┘ └──────────┘
垂直分表
做法:将一张表中的列分拆到多张表,常用字段放一起,不常用字段另开表
案例:商品基本信息表、商品详细描述表、商品图片表
分表前:
┌─────────────────── 商品表 ───────────────────┐
│ ID │ 名称 │ 价格 │ 库存 │ 详细描述 │ 图片URL集 │
└─────────────────────────────────────────────┘
分表后:
┌─────── 商品基本表 ──────┐ ┌── 商品描述表 ──┐ ┌── 商品图片表 ───┐
│ ID │ 名称 │ 价格 │ 库存 │ │ ID │ 详细描述 │ │ ID │ 图片URL集 │
└───────────────────────┘ └───────────────┘ └───────────────┘
2️. 水平拆分 - "同样的房子造多个小区"
场景:小区扩建
开发商:"一个小区住不下这么多人,我们再建5个一模一样的!"
居民:"新小区和旧小区户型完全一样,只是地址不同!"
水平分库
做法:同一个表的数据拆分到不同数据库中
案例:订单库 1、订单库 2、订单库 3...
分库前:
┌────── MySQL实例 ──────┐
│ 订单库 │
│ ┌───────────┐ │
│ │ 订单表 │ │
│ │(1千万数据) │ │
│ └───────────┘ │
└───────────────────────┘
分库后:
┌─── 实例1 ───┐ ┌─── 实例2 ───┐ ┌─── 实例3 ──┐
│ 订单库1 │ │ 订单库2 │ │ 订单库3 │
│ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │
│ │订单表 │ │ │ │订单表 │ │ │ │订单表 │ │
│ │(300万)│ │ │ │(400万) │ │ │ │(300万)│ │
│ └───────┘ │ │ └───────┘ │ │ └───────┘ │
└────────────┘ └─────────────┘ └───────────┘
水平分表
做法:同一个数据库中的表拆分成多个表,结构完全相同
案例:订单表_0、订单表_1、...、订单表_9
分表前:
┌────────── 订单表 ─────────┐
│ order_id │ user_id │ ... │
│ 10001 │ 2103 │ ... │
│ 10002 │ 5832 │ ... │
│ ... │ ... │ ... │
└──────────────────────────┘
分表后:
┌── 订单表_0 ─┐ ┌── 订单表_1 ─┐ ┌── 订单表_9 ─┐
│id │user│...│ │id │user│...│ ... │id │user│...│
│...│ .. │...│ │...│ .. │...│ │...│ .. │...│
└────────────┘ └────────────┘ └────────────┘
分片策略:"如何决定谁住哪儿" ?️
1. 哈希分片 - "按门牌号分配"
应用:"订单1234应该放在哪个库?"
路由层:"1234 % 4 = 2,所以放在2号库!"
特点:
- 均匀分布数据
- 扩容麻烦,需要 rehash
- 不适合范围查询
2. 范围分片 - "按街区划分"
应用:"ID为80万的用户应该放哪里?"
路由层:"50万~100万的用户在2号库,所以放2号库!"
特点:
- 扩容简单
- 数据分布可能不均
- 适合范围查询
3. 时间分片 - "按入住日期划分"
应用:"2023年1月的订单要查哪里?"
路由层:"查订单_202301表就行了!"
特点:
- 历史数据归档方便
- 新数据集中在最新表
- 适合按时间查询
4. 列表分片 - "VIP 客户走专属通道"
应用:"北京的用户数据在哪?"
路由层:"查表_北京就行了!"
特点:
- 按特定业务属性分片
- 适合有明显区分的业务
- 数据分布不均
同构 VS 异构:房子风格不同了 ?️
1. 相同表结构(同构)
所有分片表结构完全相同,就像同一个小区的不同楼栋。
CREATE TABLE orders_0 (id INT, user_id INT, ...);
CREATE TABLE orders_1 (id INT, user_id INT, ...);
...
2. 不同表结构(异构)
分片后表结构有差异,就像别墅区和普通住宅区。
-- VIP用户表(多了信用额度)
CREATE TABLE vip_users (id INT, name VARCHAR(50), credit DECIMAL, ...);
-- 普通用户表
CREATE TABLE normal_users (id INT, name VARCHAR(50), ...);
分库分表的"搬家难题" ?
1. 分布式事务 - "多个房子的装修需要统一协调"
场景:跨库操作
应用:"我需要同时更新用户账户和订单状态,它们在不同的库!"
DBA:"这...要么妥协用最终一致性,要么引入分布式事务框架..."
解决方案:
- 柔性事务(最终一致性)
- XA 事务
- 消息队列
2. 跨库关联 - "朋友分别住在不同小区"
场景:跨库JOIN
应用:"我需要查用户购买的所有商品,但用户表和订单表不在一个库!"
DBA:"你只能在应用层先查一个库,再查另一个库,然后手动'JOIN'了..."
解决方案:
- 冗余字段(反范式化)
- 数据字典表
- 应用层组装
3. 全局唯一 ID - "不同小区门牌号不能重复"
场景:订单ID生成
应用:"不同的订单库,怎么保证订单ID不重复?"
DBA:"忘掉自增主键吧,我们需要其他方案..."
解决方案:
- UUID
- 雪花算法(Snowflake)
- 集中式 ID 生成器
- 号段模式
4. 动态扩容 - "小区不够住了还要加新楼"
场景:数据量增长
DBA:"现有的8个分库又满了,需要增加到16个..."
开发:"所有数据要重新分配位置?那路由规则不是全变了?"
DBA:"准备加班吧,朋友..."
解决方案:
- 一致性哈希
- 双写过渡
- 数据迁移工具
常见分库分表中间件 ?
就像搬家公司,它们帮你处理复杂的路由和迁移:
- Sharding-JDBC - 轻量级 Java 框架,像个小型搬家团队
- MyCat - 强大的中间代理,像个专业搬家公司
- DynamoDB - AWS 提供的全托管方案,像个帮你包办一切的豪华搬家服务
- Vitess - YouTube 开源的可扩展 MySQL 集群系统,像个大型物流集团
分库分表最佳实践 ?
1. 先分表后分库
阶段1:单库单表 → 单库多表
阶段2:单库多表 → 多库多表
就像先在自家隔出更多房间,实在不行再买新房子。
2. 避免过度设计
数据量:< 100万,别分
100万~1000万,考虑分表
> 1000万,考虑分库分表
小别墅住 3 个人,没必要隔出 10 个房间。
3. 尽量避免跨库 JOIN
-- 避免这样
SELECT u.name, o.order_id FROM users u
JOIN orders o ON u.id = o.user_id;
-- 改为两次查询
SELECT id, name FROM users WHERE id = ?;
SELECT order_id FROM orders WHERE user_id = ?;
朋友住不同小区,就别老想着办集体 party 了。
"数据库就像是你的房子,当东西太多时,你有两个选择:买更大的房子,或者更好地整理现有空间。分库分表是整理的艺术,而不仅仅是简单地买更多房子。"
—— 匿名架构师
下次面试官问你 MySQL 分库分表,别慌!告诉他:那不过是数据库世界的"乔迁新居"罢了!?