正規化解決了一些問題,但有時候也會帶來其他麻煩,尤其是效能方面。今天我們就要帶你進入這個有點黑暗(有時候也很光明)的技術領域——反正規化。沒錯,你可以違反正規化的規則……但要有腦袋地用啦!
反正規化就是跟正規化相反的過程。如果正規化是把資料表拆成不同的邏輯實體,盡量減少重複,那反正規化就是把資料再合併回來,為了提升效能。當系統負載很大、複雜查詢很頻繁時,連接一堆資料表會讓系統變慢,這時候反正規化就很常被用到。
可以說,反正規化就是在資料純淨度和查詢速度之間做個妥協。
什麼時候該用反正規化?
跟所有工具一樣,重點是要知道什麼時候用反正規化才對。通常會在以下這些情況下用:
常用查詢變慢了。 如果你的系統負載很大,常常跑一樣的查詢(像是彙總報表或 aggregate),連接一堆資料表會花很多時間。反正規化可以減少這種 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— 門票銷售資料。
如果分析師要做一份每個活動平均票價的報表,正規化結構下你每次都得跑 aggregate 查詢:
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;
這個查詢如果每筆銷售都佔一行,幾百萬筆資料會跑超慢。
解法:反正規化。 我們可以建立一張 aggregate 資料表:
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 或 script 來確保所有資料都同步。這對開發者來說就是額外的工作。
總之,反正規化是一種工具,要懂得它的優缺點再用,才不會踩雷啦!
GO TO FULL VERSION