CodeGym /课程 /SQL SELF /数据反规范化的例子及其后果

数据反规范化的例子及其后果

SQL SELF
第 25 级, 课程 4
可用

规范化能解决一些问题,但有时候也会带来别的问题,尤其是性能方面。今天我们就来聊聊这个有点“黑魔法”色彩的东西——反规范化。没错,你可以打破规范化的规则……不过要用点脑子!

反规范化其实就是和规范化相反的过程。如果说规范化是把表拆成独立的逻辑实体,尽量减少冗余,那反规范化就是把数据合并回来,为了提升性能。反规范化经常用在高并发、复杂查询很多的时候,因为这时候多表 join 会让系统变慢。

可以说,反规范化就是在数据纯净和查询速度之间做个妥协。

什么时候用反规范化?

就像所有工具一样,关键是知道什么时候用反规范化。一般在这些场景下会用:

  1. 经常用的查询变慢了。 如果你的系统经常跑同样的查询(比如各种报表、聚合),多表 join 可能会很慢。反规范化可以减少 join 的数量。

  2. 分析任务和统计。 在分析型系统(比如 BI — Business Intelligence)里,经常需要大批量分析数据。这种情况下,反规范化能让处理速度更快,因为数据是“预处理”好的。

  3. 复杂查询。 如果一个查询要 join 五张、十张甚至更多表,数据库就会很慢。反规范化可以让查询结构简单点。

  4. join 数量多到离谱。 如果你有 25 张表一起 JOIN,那可能是时候重新考虑下你的设计了。

反规范化的例子

例子 1:电商网站。 在一个规范化的电商数据库里,可能有这些表:

  1. customers — 客户信息。
  2. orders — 订单信息。
  3. products — 商品信息。
  4. order_items — 订单里的商品。

获取信息的查询可能像这样:

SELECT
    c.customer_name,
    o.order_date,
    p.product_name,
    oi.quantity
FROM 
    customers c
JOIN 
    orders o ON c.customer_id = o.customer_id
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id
WHERE 
    c.customer_id = 42;

但如果你的电商一天要处理几十万订单,这个查询因为 join 太多就会变得很慢。

解决方法:反规范化。

我们可以建一张表,专门存常用信息:

CREATE TABLE order_summary AS
SELECT 
    c.customer_id,
    c.customer_name,
    o.order_id,
    o.order_date,
    p.product_id,
    p.product_name,
    oi.quantity
FROM 
    customers c
JOIN 
    orders o ON c.customer_id = o.customer_id
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id;

现在要查数据,只需要查 order_summary:

SELECT * FROM order_summary WHERE customer_id = 42;

例子 2:分析系统。 假设你在一个卖活动门票的公司做数据库。有这些表:

  1. events — 活动信息。
  2. sales — 门票销售数据。

如果分析师要做一个每个活动平均票价的报表,规范化结构下你每次都得跑聚合查询:

SELECT
    e.event_name,
    AVG(s.price) AS avg_ticket_price
FROM 
    events e
JOIN 
    sales s ON e.event_id = s.event_id
GROUP BY 
    e.event_name;

这个查询如果销售记录有几百万行,可能会很慢。

解决方法:反规范化。 建一张聚合数据的表:

CREATE TABLE event_summary AS
SELECT 
    e.event_id,
    e.event_name,
    COUNT(s.sale_id) AS ticket_count,
    SUM(s.price) AS total_revenue,
    AVG(s.price) AS avg_ticket_price
FROM 
    events e
JOIN 
    sales s ON e.event_id = s.event_id
GROUP BY 
    e.event_id, e.event_name;

现在报表可以直接查聚合表,速度就快多了:

SELECT
    event_name, 
    avg_ticket_price 
FROM 
    event_summary;

反规范化的后果

反规范化确实能让查询快不少,但它可不是万能钥匙。你可能会遇到这些坑:

第一,数据会重复。一样的信息存好几份,数据库体积一下就大了,管理起来也麻烦。

第二,更新数据变难了。比如客户信息在 customers 表有一份,order_summary 表又有一份。如果客户改了名字或者地址,你得记得两边都改。要是漏了,就会出错,因为数据不一致了。

第三,冗余太多容易搞混,也容易出错。就像一个文档有好几个版本,有时候都不知道哪个才是对的。

最后,这种库维护和扩展都更麻烦。你得写各种 trigger 或脚本,保证所有副本都同步。这对开发者来说就是额外的工作量。

总之,反规范化是个工具,用的时候一定要清楚它的优缺点,别乱用。

2
任务
SQL SELF, 第 25 级, 课程 4
已锁定
基于现有数据创建一个非规范化表
基于现有数据创建一个非规范化表
1
调查/小测验
数据规范化第 25 级,课程 4
不可用
数据规范化
数据规范化
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION