规范化能解决一些问题,但有时候也会带来别的问题,尤其是性能方面。今天我们就来聊聊这个有点“黑魔法”色彩的东西——反规范化。没错,你可以打破规范化的规则……不过要用点脑子!
反规范化其实就是和规范化相反的过程。如果说规范化是把表拆成独立的逻辑实体,尽量减少冗余,那反规范化就是把数据合并回来,为了提升性能。反规范化经常用在高并发、复杂查询很多的时候,因为这时候多表 join 会让系统变慢。
可以说,反规范化就是在数据纯净和查询速度之间做个妥协。
什么时候用反规范化?
就像所有工具一样,关键是知道什么时候用反规范化。一般在这些场景下会用:
经常用的查询变慢了。 如果你的系统经常跑同样的查询(比如各种报表、聚合),多表 join 可能会很慢。反规范化可以减少 join 的数量。
分析任务和统计。 在分析型系统(比如 BI — Business Intelligence)里,经常需要大批量分析数据。这种情况下,反规范化能让处理速度更快,因为数据是“预处理”好的。
复杂查询。 如果一个查询要 join 五张、十张甚至更多表,数据库就会很慢。反规范化可以让查询结构简单点。
join 数量多到离谱。 如果你有 25 张表一起
JOIN,那可能是时候重新考虑下你的设计了。
反规范化的例子
例子 1:电商网站。 在一个规范化的电商数据库里,可能有这些表:
customers— 客户信息。orders— 订单信息。products— 商品信息。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:分析系统。 假设你在一个卖活动门票的公司做数据库。有这些表:
events— 活动信息。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 或脚本,保证所有副本都同步。这对开发者来说就是额外的工作量。
总之,反规范化是个工具,用的时候一定要清楚它的优缺点,别乱用。
GO TO FULL VERSION