MySQL中对表进行垂直拆分和水平拆分是为了应对数据量的增长和访问压力增大带来的性能瓶颈,这两种拆分方法分别适用于不同的场景和需求。
###
1. 垂直拆分(Vertical Partitioning)
垂直拆分
是对表结构进行重组,将一个大表按照列的相关性分割成两个或多个表,通常是将那些不太常用或数据类型差异较大的字段放到另外的表中。这样做的目的是减少单表的宽度,提升查询性能,尤其是对于那些不需要经常一起使用的列。
例如,假设有一个包含用户信息的大表`users`,其中包含用户的姓名、联系方式、登录历史、个人简介等多个字段。如果发现登录历史和个人简介字段并不频繁查询,且数据量较大,则可以通过垂直拆分将其分离到不同的表中:
-- 原始大表
CREATE TABLE users (
id INT PRIMARY KEY,
username VARCHAR(50),
password VARCHAR(50),
email VARCHAR(50),
address TEXT,
bio TEXT,
login_history JSON // 不常用字段,可拆分
);
-- 垂直拆分后的表
CREATE TABLE user_basic_info (
id INT PRIMARY KEY,
username VARCHAR(50),
password VARCHAR(50),
email VARCHAR(50),
address TEXT
);
CREATE TABLE user_profile (
id INT PRIMARY KEY,
bio TEXT,
login_history JSON
);
###
2. 水平拆分(Horizontal Partitioning)
水平拆分
则是将同一个表的行数据按照一定的规则分布在不同的表或数据库中,每个表的结构完全相同,只是存储的数据集合不同。这样做主要是为了分散数据量,提高并行处理能力和降低单表数据过大导致的查询性能下降。
水平拆分通常依据某个特定字段的值(如用户ID取模、时间范围或其他业务逻辑相关的属性)来决定数据存储的位置。例如,一个大型的订单表可以根据订单创建时间,将每年的数据存放在不同的表中:
-- 假设原始订单表
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
order_date DATE,
product_name VARCHAR(100),
price DECIMAL(10,2)
);
-- 水平拆分后的表
CREATE TABLE orders_2020 (
order_id INT PRIMARY KEY,
user_id INT,
order_date DATE,
product_name VARCHAR(100),
price DECIMAL(10,2)
);
CREATE TABLE orders_2021 (
order_id INT PRIMARY KEY,
user_id INT,
order_date DATE,
product_name VARCHAR(100),
price DECIMAL(10,2)
);
-- 这样每年的数据都在单独的表中
更复杂的水平拆分还可以结合分区表(Partitioning)或分布式数据库中间件(如ShardingSphere、Cobar等)实现自动化管理和路由,以便透明地将数据分布到多个物理节点上,同时保持应用程序看起来像在操作单个逻辑表。
总结来说,垂直拆分关注的是减少单表字段数量,优化查询性能;而水平拆分则关注的是将数据行分摊到不同表中,以应对海量数据存储和高并发访问的需求。当然,为了更直观地理解MySQL查询优化器的工作原理,我们再来一个贴近生活的例子:
想象一下光头强是一家快递公司的智能调度员,他每天要面对众多的包裹配送任务(类比SQL查询)。有一天,他接到了一个特殊的任务,需要尽快将一批包裹送到森林各处的小动物手中(获取特定数据)。
有两种配送策略供光头强选择:
策略1(索引扫描):光头强有一份详尽的地址簿,上面标记了每个动物家的位置以及它们附近的地标(类似索引结构)。他可以根据这份地址簿快速定位到每个收件人家附近,然后逐一派送。
策略2(全表扫描):没有地址簿的情况下,光头强只能逐个街区、逐栋房子去找,确保不遗漏任何一家(相当于对所有数据行进行遍历)。
在决定采取哪种策略之前,光头强要考虑以下几个因素:
- 地址簿是否最新且准确,能否有效减少寻找时间?
- 包裹目的地分布是否有规律,使用地址簿能否大大减少走冤枉路的概率?
- 如果不用地址簿,逐户查找的总距离有多长?
基于这些信息,光头强(查询优化器)会选择预期耗时最少、效率最高的配送策略。同样地,在MySQL中,查询优化器也会根据表的统计信息、索引的存在与否及有效性等因素,来判断采用索引扫描还是全表扫描,或者其他更复杂的查询执行路径,以达到最快的查询速度和最少的系统资源消耗。