CodeGym /课程 /SQL SELF /规范化和性能之间的平衡

规范化和性能之间的平衡

SQL SELF
第 26 级 , 课程 3
可用

当我们把数据规范化做到极致时,每张表都变得超级精简,里面的信息只遵循一个原则。但实际写查询(比如“哪些学生报名了 SQL 课程?”)时,可能得 JOIN 一堆表。表越多,SQL 越复杂,数据库就得“搬砖”搬得更狠。

你们应该已经在前面的课里见过 JOIN 了。下面是个例子,展示在一个规范化设计的数据库里可能需要的查询:

SELECT students.name, courses.title
FROM students
JOIN enrollments ON students.id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.id
WHERE courses.title = 'SQL';

看起来挺简单,但实际上服务器在背后做了很多事:读每张表、拼数据、过滤……要是表特别大,性能肯定会掉下来。

大战:规范化 vs 速度

幸好(也可能不幸?),现实中的数据库都是妥协的产物。完全规范化能保证数据一致性,但复杂查询会变慢。如果数据库主要用来做分析和报表,有时候反规范化更划算。就像把 10 个小盒子换成一个大箱子:取数据快了,但想再分开就麻烦了。

什么时候可以“松口气”不用太规范?

有些场景下,反规范化更合适:

经常用到的聚合

比如,假设系统每天都要查每门课有多少学生。规范化结构下得一直 JOINCOUNT()。不如直接在 "Courses" 表里加个 student_count 字段,每次增删记录时自动更新。

-- 反规范化的字段
UPDATE courses
SET student_count = (
    SELECT COUNT(*)
    FROM enrollments
    WHERE enrollments.course_id = courses.id
);

经常要的报表

如果客户天天要“谁、在哪、啥时候买了啥?”的报表,直接存一张反规范化的表,里面就是“客户名、商品、日期”这种现成的行。表会变大,但查数据飞快。

读多写少的场景

如果数据库主要是读(比如做分析),可以牺牲规范化换速度。

复杂关系下减少 JOIN

如果表之间关系很复杂(嵌套多层), JOIN 写起来很痛苦,可以适当减少规范化层级。

例子:反规范化怎么加速?

假设有个电商的规范化表:

products orders order_items
id id id
name date order_id
price customer_id product_id
quantity

每个订单(orders)包含多条订单项(order_items)。我们来算下商店赚了多少钱:

SELECT SUM(order_items.quantity * products.price) AS total_revenue
FROM order_items
JOIN products ON order_items.product_id = products.id;

在数据量很大的时候,order_itemsproducts 的 JOIN 会拖慢查询。

反规范化结构

现在假设 order_items 表里多了个 total_price 字段(反规范化):

order_items
id
order_id
product_id
quantity
total_price

现在查询就很简单了:

SELECT SUM(total_price) AS total_revenue
FROM order_items;

这样就不用 JOIN,速度直接起飞。

实战练习:优化“销售”数据库

已知:规范化表

products sales
id id
name product_id
price date
quantity

目标:加速类似“每个产品赚了多少钱?”的高频查询。

第 1 步:sales 表里加个 total_price 字段:

ALTER TABLE sales ADD COLUMN total_price NUMERIC;

第 2 步: 用已有数据填充这个字段:

UPDATE sales
SET total_price = quantity * (
    SELECT price
    FROM products
    WHERE products.id = sales.product_id
);

第 3 步: 查询更快了:

SELECT product_id, SUM(total_price) AS total_revenue
FROM sales
GROUP BY product_id;

但是!反规范化是有代价的

你也知道,“更快”不等于“更好”。反规范化会带来这些问题:

存储冗余

total_price 字段其实是数据的副本,占用更多空间。

更新麻烦

如果 products 表里的价格变了,得手动同步 total_price 字段。不然就会不一致。

插入、更新、删除时的异常

如果忘了同步反规范化的数据,信息很容易“不同步”。比如产品价格变了,但不会自动反映到 total_price

平衡:怎么找到最佳方案?

先想清楚什么更重要:性能还是结构? 如果数据库主要是读,优先考虑查询效率。

只在关键地方反规范化。 比如只针对核心统计和报表。

自动化反规范化数据的更新。 用触发器或者定时任务,避免数据不一致。

2
任务
SQL SELF, 第 26 级, 课程 3
已锁定
为加速统计进行反规范化
为加速统计进行反规范化
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION