数据库垂直拆分和水平拆分
目录
- 一、 垂直拆分 (Vertical Sharding/Partitioning)
- 二、 水平拆分 (Horizontal Sharding/Partitioning)
- 总结与对比
为了应对单实例数据库存储空间不足或写入/查询性能瓶颈(TPS/QPS 接近上限),采用的核心扩展策略是数据库垂直拆分(Vertical Sharding/Partitioning)和水平拆分(Horizontal Sharding/Partitioning)。
核心区别:
- 垂直拆分: 按 列/功能/业务模块 切分数据。将一个“宽”表拆成多个“窄”表,或者将一个大库中的不同业务表拆分到不同的物理数据库实例中。
- 水平拆分: 按 行/数据记录 切分数据。将一个大表中的数据行分散存储到多个结构相同的表或数据库实例中。
一、 垂直拆分 (Vertical Sharding/Partitioning)
-
定义:
- 基于列拆分表: 将一个包含大量字段的表,根据字段的访问频率、业务相关性或大小(如大文本、BLOB),拆分成多个更小的表。这些表通常通过主键关联(一对一或一对多关系)。
- 基于功能/业务模块拆分库: 将一个大型单体数据库,根据其承载的不同业务功能(例如用户管理、订单管理、商品管理、日志记录等),拆分成多个独立的、更小型的数据库实例。每个数据库实例只负责特定业务领域的数据。
-
主要目的:
- 减少单表宽度,提高查询效率(减少 I/O,只读取需要的列)。
- 将访问热点分散到不同的表或数据库实例,降低锁竞争和资源争用。
- 隔离不同业务模块,提高系统可维护性、可扩展性和容错性(一个业务出问题不影响另一个)。
- 针对性地为不同业务模块优化硬件资源(如为订单库配置更强的 CPU/磁盘,为日志库配置更大存储)。
- 将大字段(如 TEXT, BLOB)分离到单独的表中,避免它们拖慢核心数据的查询。
-
举例说明:
- 例1:拆分用户表 (基于列)
- 原始表
users
:CREATE TABLE users (user_id INT PRIMARY KEY,username VARCHAR(50) NOT NULL,password_hash VARCHAR(255) NOT NULL,email VARCHAR(100) NOT NULL,phone VARCHAR(20),registration_date DATETIME NOT NULL,last_login DATETIME,profile_picture BLOB, -- 大字段bio TEXT -- 大字段 );
- 垂直拆分后:
- 核心表
users_core
:CREATE TABLE users_core (user_id INT PRIMARY KEY,username VARCHAR(50) NOT NULL,password_hash VARCHAR(255) NOT NULL,email VARCHAR(100) NOT NULL,phone VARCHAR(20),registration_date DATETIME NOT NULL,last_login DATETIME );
- 详情表
users_profile
:CREATE TABLE users_profile (user_id INT PRIMARY KEY, -- 与 users_core.user_id 关联profile_picture BLOB,bio TEXT );
- 核心表
- 效果: 频繁查询用户名、邮箱、登录时间的操作只在较小的
users_core
表上进行,效率更高。只有需要头像或简介时才去查users_profile
。BLOB/TEXT 字段的 I/O 不再影响核心用户信息的访问。
- 原始表
- 例2:拆分电商数据库 (基于业务模块)
- 原始单体数据库
ecommerce_db
包含:users
表 (用户信息)products
表 (商品信息)orders
表 (订单头信息)order_items
表 (订单明细)payments
表 (支付记录)inventory
表 (库存)logs
表 (操作日志)
- 垂直拆分后 (分库):
user_db
实例: 包含users
表 (可能还会包含用户地址簿、积分等用户相关表)。product_db
实例: 包含products
表、inventory
表(库存)、categories
表(分类)、reviews
表(评价)。order_db
实例: 包含orders
表、order_items
表、payments
表。log_db
实例: 包含logs
表(可能还有其他审计、监控表)。
- 效果:
- 用户注册、登录的流量压力集中在
user_db
,不影响商品浏览或下单。 - 促销时订单系统的巨大压力由
order_db
独自承担,不影响用户正常浏览商品 (product_db
) 或查看个人资料 (user_db
)。 - 日志记录的高频写入操作由专门的
log_db
处理,不会占用核心业务数据库的宝贵 I/O 和 CPU。 - 可以根据每个库的特点独立优化和扩展(如
order_db
需要高性能 SSD 和更多 CPU,log_db
需要大容量机械硬盘)。
- 用户注册、登录的流量压力集中在
- 原始单体数据库
- 例1:拆分用户表 (基于列)
二、 水平拆分 (Horizontal Sharding/Partitioning)
-
定义:
- 将 同一个表 中的 数据行,按照某种规则(称为分片键/分区键 Sharding Key),分散存储到 多个结构完全相同的表或数据库实例 中。
- 每个拆分出来的部分称为一个 分片 (Shard) 或 分区 (Partition)。每个分片存储数据的一个子集,所有分片的数据加起来就是全量数据。每个分片可以部署在独立的物理服务器上。
-
主要目的:
- 解决单表数据量过大问题: 突破单机存储容量限制。
- 解决单表写入/读取性能瓶颈: 将负载(TPS/QPS)分散到多个物理节点上并行处理,显著提升整体吞吐量。
- 提高查询性能(针对特定分片键的查询可以快速定位到单个分片)。
-
关键概念:
- 分片键 (Sharding Key): 决定数据行分配到哪个分片的依据字段(如用户ID、订单ID、时间戳、地理区域等)。选择合适的分片键至关重要,它直接影响数据分布的均匀性和查询效率。
- 分片策略:
- 范围分片 (Range Sharding): 按分片键的范围划分(如
user_id
1-1000万在分片1,1000万-2000万在分片2)。 - 哈希分片 (Hash Sharding): 对分片键计算哈希值,根据哈希值取模或一致性哈希分配到分片。通常能获得更均匀的数据分布。
- 列表分片 (List Sharding): 根据分片键的枚举值直接映射到分片(如
region
字段:’华北’ -> 分片1, ’华东’ -> 分片2)。 - 地理分片 (Geo Sharding): 按地理位置划分,将用户的数据存储在靠近用户的物理位置(可视为列表分片的特殊形式)。
- 范围分片 (Range Sharding): 按分片键的范围划分(如
-
举例说明:
- 例:拆分订单表 (水平分表/分库)
- 问题: 单表
orders
数据量达到数十亿,写入 TPS 极高,查询和存储都成为瓶颈。 - 水平拆分方案 (假设使用
user_id
作为分片键,采用哈希分片,分成4个分片):orders_db_shard1
: 包含表orders
(存储user_id
哈希后取模hash(user_id) % 4 = 0
的订单数据)orders_db_shard2
: 包含表orders
(存储user_id
哈希后取模hash(user_id) % 4 = 1
的订单数据)orders_db_shard3
: 包含表orders
(存储user_id
哈希后取模hash(user_id) % 4 = 2
的订单数据)orders_db_shard4
: 包含表orders
(存储user_id
哈希后取模hash(user_id) % 4 = 3
的订单数据)
- 效果:
- 存储扩展: 订单数据总量可以突破单机磁盘限制,分散在4台机器上。
- 写入扩展: 新的订单写入请求会根据
user_id
的哈希值被路由到对应的分片数据库。理论上写入能力(TPS)接近原来单机的4倍(忽略路由和协调开销)。 - 读取扩展 (针对分片键): 查询某个特定用户 (
user_id=xxx
) 的所有订单时,可以直接路由到该用户所在的分片,只查询一个较小的数据集,速度更快。 - 读取挑战 (非分片键): 如果查询条件是“查询今天所有订单”,则需要向 所有4个分片 发送查询 (
SELECT * FROM orders WHERE order_date = TODAY
),然后在应用层或中间件层合并结果(称为 Scatter-Gather)。这种查询性能较差,代价高。这是水平拆分的主要缺点之一。
- 问题: 单表
- 例:拆分订单表 (水平分表/分库)
总结与对比
特性 | 垂直拆分 (Vertical Sharding) | 水平拆分 (Horizontal Sharding) |
---|---|---|
拆分依据 | 列 (Column) / 业务功能 (Business Function) | 行 (Row) / 数据记录 (Data Record) |
目标 | 减少单表宽度、分散热点、业务解耦、优化资源 | 解决海量数据存储、分散读写负载 (TPS/QPS)、并行处理 |
影响范围 | 表结构改变 / 数据库实例分离 | 表结构不变,数据分布到多个相同结构的表/实例 |
优点 | 提升热点查询性能、业务清晰、资源隔离、易于优化大字段 | 突破单机存储和性能极限、线性扩展能力、点查快 (分片键) |
缺点 | 无法解决单表数据量过大问题;跨库 JOIN 复杂 | 分片键选择至关重要;跨分片查询复杂低效;分布式事务难 |
适用场景 | 表过宽、大字段影响性能、业务模块耦合度高、资源争用 | 单表数据量巨大、写入/读取 TPS/QPS 接近单机瓶颈 |
举例 | 用户表拆核心信息/详情;电商库拆用户库/商品库/订单库 | 订单表按用户ID哈希分到4个库;用户表按注册时间范围分表 |
实际应用:
大型系统通常会 组合使用 垂直拆分和水平拆分:
- 先垂直拆分: 将庞大的单体数据库按业务模块拆分成多个专门的数据库(如
user_db
,product_db
,order_db
)。 - 再水平拆分: 当某个业务库(如
order_db
)中的核心表(如orders
表)数据量或负载达到瓶颈时,对该表进行水平拆分(如按user_id
哈希分片到order_db_shard1
到order_db_shard4
)。
这种组合策略能够有效应对复杂的扩展需求,实现存储容量和性能的线性增长。不过,拆分也引入了分布式系统的复杂性(跨库事务、跨片查询、数据一致性、运维复杂度等),需要借助数据库中间件(如 MyCat, ShardingSphere, Vitess)或云数据库服务来简化管理。