
Giới thiệu về Materialized View#
Trong thế giới cơ sở dữ liệu, View là một công cụ hữu ích giúp đơn giản hóa các truy vấn phức tạp bằng cách tạo ra một "lớp ảo" dựa trên kết quả của câu lệnh SELECT. Tuy nhiên, mỗi lần truy vấn View, hệ thống phải thực thi lại câu lệnh gốc, điều này có thể tốn kém với dữ liệu lớn, đặc biệt là khi gặp các truy vấn phức tạp với nhiều phép JOIN và hàm tổng hợp.
Materialized View xuất hiện như một giải pháp tối ưu, cho phép lưu trữ kết quả truy vấn xuống đĩa cứng để tái sử dụng.
Khác với View thông thường (chỉ là định nghĩa truy vấn), Materialized View thực sự lưu trữ dữ liệu như một bảng vật lý. Điều này mang lại lợi ích đáng kể về tốc độ truy vấn, đặc biệt với dữ liệu lớn và các phép tính phức tạp.
Materialized View vs View: So sánh chi tiết#
| Đặc điểm | View thông thường | Materialized View |
| Lưu trữ | Chỉ lưu định nghĩa truy vấn | Lưu cả định nghĩa và kết quả truy vấn |
| Tốc độ | Chậm (phải thực thi lại mỗi lần) | Cực nhanh (đọc trực tiếp kết quả đã tính) |
| Cập nhật | Luôn phản ánh dữ liệu mới nhất | Cần refresh để cập nhật |
| Tài nguyên | Tốn CPU khi truy vấn | Tốn dung lượng lưu trữ |
Cách hoạt động của Materialized View#
Ta hoàn toàn có thể sử dụng Materialized View được với hầu hết các cơ sở dữ liệu quan hệ: nó có thể đã được hỗ trợ sẵn hàm như trong PostgreSQL, MicrosoftSQL server, IBM DB2,… hoặc cũng có thể qua rất nhiều bước khá phức tạp như MySQL,… Với mục đích minh họa đơn giản, nên trong bài này chúng ta sẽ thử thực hành với PostgreSQL.
Chuẩn bị dữ liệu mẫu#
Giả sử chúng ta có hai bảng cơ bản:
CREATE TABLE "public"."products" (
product_id SERIAL PRIMARY KEY,
product_name TEXT,
brand TEXT
);
CREATE TABLE "public"."orders" (
"id" SERIAL PRIMARY KEY,
"product_id" INTEGER NOT NULL,
"customer_id" INTEGER NOT NULL,
"date_key" INTEGER NOT NULL,
"price" BIGINT NOT NULL,
CONSTRAINT "orders_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "public"."products"("product_id")
);
Nếu không có sẵn PostgreSQL trong máy, bạn có thể cài bằng lệnh:
docker run -it --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres
Download sample data tại đây nhé.
Tạo Materialized View#
Để tạo Materialized View, ta dụng lệnh CREATE MATERIALIZED VIEW:
CREATE MATERIALIZED VIEW view_name
AS
query
WITH [NO] DATA;
Chỉ định tên của Materialized View (
view_name) ngay sau mệnh đềCREATE MATERIALIZED VIEW.Chỉ định câu truy vấn sử dụng để load dữ liệu ở ngay sau từ khóa
AS.Nếu bạn muốn load sẵn dữ liệu vào Materialized View ngay từ lúc khởi tạo, thì đặt thêm
WITH DATAở cuối. Ngược lại thì đặtWITH NO DATA.- Trong trường hợp
WITH NO DATA: Materialized View sẽ được đánh dấu là không thể đọc được, ta chỉ có thể truy vấn được từ nó chỉ khi nó đã được load dữ liệu vào thôi.
- Trong trường hợp
Để tạo Materialized View tổng hợp đơn hàng theo thương hiệu và ngày:
CREATE MATERIALIZED VIEW orders_mat_view
AS
SELECT
brand,
date_key,
COUNT(*) AS amount,
SUM(price) AS total_price
FROM
orders
JOIN products ON products.product_id = orders.product_id
GROUP BY
brand,
date_key
WITH DATA;
Sử dụng Materialized View#
Sau khi tạo, bạn có thể sử dụng Materialized View như một bảng thông thường:
-- Truy vấn toàn bộ dữ liệu
SELECT * FROM orders_mat_view;
-- Sắp xếp theo tổng doanh thu giảm dần
SELECT * FROM orders_mat_view ORDER BY total_price DESC;
-- Lọc theo thương hiệu và ngày cụ thể
SELECT * FROM orders_mat_view
WHERE brand = 'Omeprazole' AND date_key = 140101;
-- Kết quả: amount=2, total_price=1555
Ưu điểm của Materialized View so với View đó là thời gian response rất nhanh, gần như tức thì, vì dữ liệu đã được tính toán và lưu sẵn trong disk. DB sẽ chỉ cần lôi kết quả ra và truy vấn đơn giản hơn (kiểu như lọc theo brand, lọc theo ngày, hay sort theo amount, total_price,…).
Kiến trúc lưu trữ vật lý#
Mô hình lưu trữ hai lớp#
Materialized View thực chất là một đối tượng lưu trữ kép:
Lớp 1: Định nghĩa logic (query definition)
┌─────────────────────────────────┐
│ SELECT brand, date_key, │
│ COUNT(*), SUM(price) │
│ FROM orders JOIN products │
│ GROUP BY brand, date_key │
└─────────────────────────────────┘
Lớp 2: Dữ liệu vật lý (physical storage)
┌─────────┬──────────┬────────┬─────────────┐
│ brand │ date_key │ amount │ total_price │
├─────────┼──────────┼────────┼─────────────┤
│ Brand A │ 20240101 │ 150 │ 75000 │
│ Brand B │ 20240101 │ 200 │ 120000 │
└─────────┴──────────┴────────┴─────────────┘
Cấu trúc bảng ẩn#
Khi tạo Materialized View, hệ quản trị cơ sở dữ liệu (DBMS) thực hiện:
Tạo bảng ẩn với schema tương ứng kết quả truy vấn
Gắn metadata để theo dõi mối quan hệ với bảng nguồn
Quản lý lifecycle gồm tạo, refresh, và xóa
Cập nhật dữ liệu trong Materialized View#
Vấn đề dữ liệu cũ#
Khi dữ liệu trong bảng gốc thay đổi, Materialized View không tự động cập nhật. Ví dụ:
-- Thêm đơn hàng mới
INSERT INTO "public"."orders"
("id", "product_id", "customer_id", "date_key", "price")
VALUES ('201', '803', '5301', '140101', '400');
-- Truy vấn Materialized View (vẫn hiển thị dữ liệu cũ)
SELECT * FROM orders_mat_view
WHERE brand = 'Omeprazole' AND date_key = 140101;
-- Kết quả: amount=2, total_price=1555 (trong khi thực tế đã có 3 đơn hàng)
Refresh dữ liệu thông thường (Full Refresh)#
Để cập nhật Materialized View với dữ liệu mới nhất:
REFRESH MATERIALIZED VIEW orders_mat_view;
-- Kiểm tra lại kết quả
SELECT * FROM orders_mat_view
WHERE brand = 'Omeprazole' AND date_key = 140101;
-- Kết quả: amount=3, total_price=1955 (đã được cập nhật)
Lưu ý quan trọng: Khi refresh thông thường, PostgreSQL sẽ khóa (lock) Materialized View, ngăn không cho truy vấn trong quá trình refresh.
Refresh đồng thời (Concurrent Refresh)#
Để tránh tình trạng khóa và cho phép truy vấn trong khi refresh:
-- Bước 1: Tạo unique index (bắt buộc cho concurrent refresh)
CREATE UNIQUE INDEX idx_brand_date_key
ON orders_mat_view (brand, date_key);
-- Bước 2: Refresh đồng thời
REFRESH MATERIALIZED VIEW CONCURRENTLY orders_mat_view;
Cơ chế hoạt động của CONCURRENTLY:
Tạo một bảng tạm chứa dữ liệu mới
So sánh với dữ liệu hiện có
Chỉ cập nhật những dòng thay đổi
Hoán đổi bảng tạm với Materialized View gốc
Ưu điểm nổi bật của Materialized View#
1. Hiệu suất vượt trội#
Truy vấn trả kết quả gần như tức thì
Giảm tải đáng kể cho server cơ sở dữ liệu
Tối ưu cho các hệ thống cần phản hồi nhanh
2. Đơn giản hóa phát triển ứng dụng#
Che giấu độ phức tạp của truy vấn gốc
Cung cấp interface đơn giản cho ứng dụng
Dễ dàng bảo trì và tối ưu
3. Linh hoạt trong thiết kế kiến trúc#
Có thể đặt trên server riêng cho hệ thống báo cáo
Hỗ trợ cả OLTP và OLAP trong cùng hệ thống
Dễ dàng mở rộng theo nhu cầu
Khi nào nên sử dụng Materialized View?#
Materialized View phù hợp nhất cho các trường hợp:
Báo cáo và dashboard cần tốc độ phản hồi nhanh
Dữ liệu thống kê được sử dụng thường xuyên
Truy vấn phức tạp với nhiều JOIN và hàm tổng hợp
Dữ liệu ít thay đổi hoặc chỉ cần cập nhật định kỳ
Kết luận#
Materialized View không phải là giải pháp phù hợp cho mọi trường hợp, nhưng khi được áp dụng đúng chỗ, nó mang lại lợi ích vượt trội về hiệu suất và khả năng mở rộng.
Các hệ quản trị cơ sở dữ liệu hiện đại tiếp tục phát triển các kỹ thuật mới như incremental maintenance với machine learning, auto-tuning materialized views, và integration với streaming data platforms, mở ra khả năng ứng dụng ngày càng rộng rãi và hiệu quả hơn.