Skip to content

MySQL 垂直拆分与水平拆分

垂直拆分

基本概念

垂直拆分是指将一个表按照列的方式拆分成多个表,每个表包含原表的一部分列。垂直拆分也可以指将一个数据库按照功能模块拆分成多个数据库。

拆分方式

垂直分表

功能:将一个表按照列的相关性拆分成多个表

拆分原则

  • 将经常一起查询的列放在同一个表中
  • 将不经常使用的列拆分到单独的表中
  • 将大字段(如TEXT、BLOB)拆分到单独的表中
  • 按照业务逻辑将列分组

使用示例

原表结构:

sql
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    password VARCHAR(255),
    bio TEXT,
    avatar BLOB,
    created_at DATETIME
);

拆分后:

sql
-- 核心信息表
CREATE TABLE users_core (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    password VARCHAR(255),
    created_at DATETIME
);

-- 扩展信息表
CREATE TABLE users_extra (
    user_id INT PRIMARY KEY,
    bio TEXT,
    avatar BLOB,
    FOREIGN KEY (user_id) REFERENCES users_core(id)
);

垂直分库

功能:将一个数据库按照功能模块拆分成多个数据库

拆分原则

  • 按照业务模块拆分
  • 将相关性高的表放在同一个数据库中
  • 考虑跨库 join 的影响
  • 考虑事务处理的复杂性

使用示例

  • 用户数据库:包含用户相关表
  • 订单数据库:包含订单相关表
  • 产品数据库:包含产品相关表
  • 日志数据库:包含日志相关表

优点

  • 降低表的复杂度,提高查询性能
  • 减少I/O操作,提高数据访问效率
  • 便于维护和管理
  • 支持不同业务模块的独立扩展
  • 提高数据安全性,不同模块数据隔离

缺点

  • 增加了系统复杂度
  • 可能需要跨表 join,影响查询性能
  • 事务处理变得复杂
  • 数据一致性维护难度增加

适用场景

  • 表中包含大字段,影响查询性能
  • 表中列的访问频率差异较大
  • 不同业务模块之间的耦合度较低
  • 系统需要独立扩展不同业务模块

水平拆分

基本概念

水平拆分是指将一个表按照行的方式拆分成多个表,每个表包含原表的一部分行。水平拆分也可以指将一个数据库拆分成多个数据库实例。

拆分方式

水平分表

功能:将一个表按照行的方式拆分成多个表,所有表的结构相同

拆分原则

  • 选择合适的分片键
  • 确保数据均匀分布
  • 考虑查询模式
  • 考虑扩容的便利性

分片键选择

  • 频繁用于查询条件的列
  • 具有高基数的列
  • 分布均匀的列
  • 便于范围查询的列

使用示例

按用户ID取模拆分:

sql
-- 用户表1:user_id % 4 = 0
CREATE TABLE users_0 (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    created_at DATETIME
);

-- 用户表2:user_id % 4 = 1
CREATE TABLE users_1 (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    created_at DATETIME
);

-- 用户表3:user_id % 4 = 2
CREATE TABLE users_2 (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    created_at DATETIME
);

-- 用户表4:user_id % 4 = 3
CREATE TABLE users_3 (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    created_at DATETIME
);

水平分库

功能:将一个数据库拆分成多个数据库实例,每个实例包含一部分数据

拆分原则

  • 与水平分表类似,但拆分粒度更大
  • 考虑数据库实例的负载均衡
  • 考虑跨库查询的复杂性
  • 考虑数据备份和恢复的便利性

使用示例

  • 数据库实例1:存储user_id % 4 = 0的数据
  • 数据库实例2:存储user_id % 4 = 1的数据
  • 数据库实例3:存储user_id % 4 = 2的数据
  • 数据库实例4:存储user_id % 4 = 3的数据

分片策略

范围分片

功能:按照分片键的范围将数据分配到不同的分片

优点

  • 便于范围查询
  • 便于扩容
  • 数据分布可控

缺点

  • 可能导致数据分布不均匀
  • 热点数据可能集中在某个分片

适用场景

  • 分片键是连续的(如时间、ID)
  • 范围查询频繁

使用示例

分片1:id < 1000000
分片2:1000000 <= id < 2000000
分片3:2000000 <= id < 3000000

哈希分片

功能:通过哈希函数将分片键转换为哈希值,根据哈希值分配到不同的分片

优点

  • 数据分布均匀
  • 避免热点数据

缺点

  • 范围查询性能较差
  • 扩容复杂

适用场景

  • 分片键分布均匀
  • 精确查询频繁
  • 无明显热点数据

使用示例

分片1:hash(user_id) % 4 = 0
分片2:hash(user_id) % 4 = 1
分片3:hash(user_id) % 4 = 2
分片4:hash(user_id) % 4 = 3

列表分片

功能:按照分片键的具体值列表将数据分配到不同的分片

优点

  • 数据分布可控
  • 便于业务逻辑关联

缺点

  • 可能导致数据分布不均匀
  • 扩容需要修改分片规则

适用场景

  • 分片键的值较少且固定
  • 业务逻辑与分片键强关联

使用示例

分片1:region = 'North'
分片2:region = 'South'
分片3:region = 'East'
分片4:region = 'West'

复合分片

功能:结合多种分片策略,如先按范围分片,再按哈希分片

优点

  • 结合多种策略的优点
  • 更灵活的分片方式

缺点

  • 增加了系统复杂度
  • 分片规则设计复杂

适用场景

  • 复杂的业务场景
  • 需要同时支持范围查询和精确查询

分库分表中间件

MySQL Proxy

功能:MySQL官方提供的中间件,用于实现读写分离和负载均衡

特点

  • 轻量级,部署简单
  • 支持读写分离
  • 支持负载均衡
  • 扩展性有限
  • 性能一般

MyCAT

功能:开源的分库分表中间件,支持MySQL、Oracle、DB2等多种数据库

特点

  • 支持垂直拆分和水平拆分
  • 支持多种分片策略
  • 支持读写分离
  • 支持事务处理
  • 社区活跃,文档丰富

ShardingSphere

功能:Apache开源的分布式数据库中间件,提供分库分表、读写分离、分布式事务等功能

特点

  • 模块化设计,灵活扩展
  • 支持多种分片策略
  • 支持分布式事务
  • 支持读写分离
  • 支持数据加密
  • 支持SQL审计

Vitess

功能:Google开源的分布式数据库中间件,用于大规模MySQL部署

特点

  • 支持水平分片
  • 支持自动扩容
  • 支持读写分离
  • 支持分布式事务
  • 适合大规模部署

Atlas

功能:奇虎360开源的分库分表中间件,基于MySQL Proxy开发

特点

  • 支持读写分离
  • 支持水平分片
  • 支持负载均衡
  • 部署简单
  • 性能较好

分库分表设计原则

1. 业务驱动原则

  • 根据业务需求设计分库分表策略
  • 考虑业务增长趋势
  • 考虑业务查询模式
  • 考虑业务数据特点

2. 分片键选择原则

  • 选择频繁用于查询条件的列
  • 选择高基数的列
  • 选择分布均匀的列
  • 考虑范围查询的需求
  • 考虑分片的扩展性

3. 数据分布原则

  • 确保数据均匀分布到各个分片
  • 避免热点数据集中在某个分片
  • 考虑分片的存储容量
  • 考虑分片的负载均衡

4. 可用性原则

  • 确保系统高可用
  • 考虑故障恢复机制
  • 考虑数据备份和恢复策略
  • 考虑分片的容错能力

5. 可扩展性原则

  • 支持平滑扩容
  • 支持在线分片调整
  • 支持新增分片而不影响现有业务
  • 考虑未来业务增长的需求

分库分表实现步骤

1. 需求分析

  • 分析业务需求和数据特点
  • 确定分库分表的目标
  • 评估当前系统的性能瓶颈
  • 预测未来业务增长趋势

2. 设计分片策略

  • 选择合适的分片键
  • 确定分片方式(垂直拆分或水平拆分)
  • 选择分片算法
  • 设计分片规则

3. 选择中间件

  • 评估各种中间件的特点和性能
  • 选择适合业务需求的中间件
  • 设计中间件的部署架构

4. 数据迁移

  • 设计数据迁移方案
  • 执行数据迁移
  • 验证数据一致性
  • 切换业务流量

5. 系统测试

  • 测试系统性能
  • 测试系统可用性
  • 测试故障恢复机制
  • 测试数据一致性

6. 上线部署

  • 灰度发布
  • 监控系统运行状态
  • 优化系统性能
  • 制定应急预案

分库分表常见问题

1. 跨分片查询

问题:需要查询多个分片的数据,导致查询性能下降

解决方案

  • 优化查询语句,减少跨分片查询
  • 使用中间件支持的全局索引
  • 考虑数据冗余,避免跨分片查询
  • 使用分布式查询引擎

2. 分布式事务

问题:需要保证多个分片上的数据一致性

解决方案

  • 使用中间件支持的分布式事务
  • 采用最终一致性方案
  • 使用消息队列保证数据一致性
  • 设计幂等操作,避免重复执行

3. 数据迁移

问题:数据迁移过程中可能影响业务运行

解决方案

  • 设计在线数据迁移方案
  • 使用双写策略,确保数据一致性
  • 进行充分的测试和验证
  • 选择业务低峰期进行迁移

4. 扩容问题

问题:扩容过程中需要重新分配数据

解决方案

  • 设计支持平滑扩容的分片策略
  • 使用中间件支持的在线扩容
  • 考虑预分片策略,提前规划分片数量
  • 采用一致性哈希算法,减少数据迁移量

5. 主键冲突

问题:多个分片可能产生相同的主键

解决方案

  • 使用全局唯一ID生成器(如Snowflake算法)
  • 使用UUID
  • 使用数据库自增主键 + 分片ID
  • 使用中间件支持的全局主键

最佳实践

1. 优先考虑垂直拆分

  • 垂直拆分相对简单,风险较低
  • 可以解决大部分性能问题
  • 便于后续水平拆分

2. 合理选择分片键

  • 分片键的选择直接影响系统性能
  • 考虑查询模式和数据分布
  • 避免频繁修改分片键
  • 考虑分片的扩展性

3. 控制分片数量

  • 分片数量不宜过多,否则增加系统复杂度
  • 根据业务需求和硬件资源调整分片数量
  • 考虑分片的管理和维护成本

4. 避免跨分片事务

  • 跨分片事务增加系统复杂度
  • 影响系统性能
  • 考虑业务设计,减少跨分片事务
  • 使用最终一致性方案

5. 监控和优化

  • 监控分片的性能和负载
  • 监控数据分布情况
  • 定期优化分片策略
  • 定期清理无用数据

垂直拆分与水平拆分对比

特性垂直拆分水平拆分
拆分方式按列拆分按行拆分
表结构不同表结构相同表结构
适用场景表包含大字段、列访问频率差异大表数据量大、查询性能下降
复杂度相对较低相对较高
扩容方式独立扩容横向扩容
跨分片查询可能需要跨表join可能需要跨分片查询
事务处理相对简单相对复杂
数据一致性容易维护维护难度大

分库分表案例分析

案例1:电商系统订单表设计

问题:订单表数据量过大,查询性能下降

解决方案

  1. 垂直拆分:将订单表拆分为订单核心表和订单扩展表
  2. 水平拆分:按订单创建时间范围进行分片
  3. 分片策略
    • 按年分片:每年创建一个订单表
    • 热点数据:最近3个月的数据放在一个分片,便于快速访问
  4. 中间件选择:使用ShardingSphere实现分库分表

效果

  • 查询性能提升300%
  • 支持系统的快速扩展
  • 便于维护和管理

案例2:社交系统用户表设计

问题:用户表数据量超过1亿,插入和查询性能下降

解决方案

  1. 垂直拆分:将用户表拆分为用户核心表和用户详细信息表
  2. 水平拆分:按用户ID哈希分片
  3. 分片策略
    • 使用一致性哈希算法,减少扩容时的数据迁移
    • 分片数量:64个分片
  4. 中间件选择:使用MyCAT实现分库分表

效果

  • 插入性能提升200%
  • 查询性能提升500%
  • 支持系统的线性扩展

常见问题(FAQ)

Q1: 什么时候需要考虑分库分表?

A1: 以下情况需要考虑分库分表:

  • 单表数据量超过1000万行
  • 查询性能明显下降
  • 写入性能明显下降
  • 数据库存储空间不足
  • 系统需要支持更高的并发

Q2: 垂直拆分和水平拆分的区别是什么?

A2: 垂直拆分和水平拆分的主要区别:

  • 垂直拆分按列拆分,水平拆分按行拆分
  • 垂直拆分后表结构不同,水平拆分后表结构相同
  • 垂直拆分解决表复杂度问题,水平拆分解决数据量问题
  • 垂直拆分相对简单,水平拆分相对复杂

Q3: 如何选择合适的分片键?

A3: 选择合适的分片键需要考虑以下因素:

  • 频繁用于查询条件的列
  • 高基数的列
  • 分布均匀的列
  • 便于范围查询的列
  • 考虑分片的扩展性

Q4: 分库分表后如何处理跨分片查询?

A4: 处理跨分片查询的方法:

  • 优化查询语句,减少跨分片查询
  • 使用中间件支持的全局索引
  • 考虑数据冗余,避免跨分片查询
  • 使用分布式查询引擎

Q5: 分库分表后如何保证数据一致性?

A5: 保证数据一致性的方法:

  • 使用中间件支持的分布式事务
  • 采用最终一致性方案
  • 使用消息队列保证数据一致性
  • 设计幂等操作,避免重复执行

Q6: 如何选择合适的分库分表中间件?

A6: 选择合适的分库分表中间件需要考虑以下因素:

  • 业务需求和场景
  • 中间件的功能和特性
  • 中间件的性能和稳定性
  • 中间件的社区活跃度和文档支持
  • 团队的技术栈和经验

Q7: 分库分表后如何进行数据迁移?

A7: 数据迁移的步骤:

  • 设计数据迁移方案
  • 执行数据迁移
  • 验证数据一致性
  • 切换业务流量

Q8: 分库分表后如何进行扩容?

A8: 扩容的方法:

  • 设计支持平滑扩容的分片策略
  • 使用中间件支持的在线扩容
  • 考虑预分片策略,提前规划分片数量
  • 采用一致性哈希算法,减少数据迁移量

Q9: 分库分表会影响系统的可用性吗?

A9: 分库分表可能会影响系统的可用性,因此需要:

  • 确保系统高可用
  • 考虑故障恢复机制
  • 考虑数据备份和恢复策略
  • 考虑分片的容错能力

Q10: 分库分表后如何监控系统性能?

A10: 监控系统性能的方法:

  • 监控分片的性能和负载
  • 监控数据分布情况
  • 监控查询响应时间
  • 监控系统资源使用情况
  • 定期进行性能测试和优化