Bài 18: Tối Ưu Vật Lý – Partitioning Và Parallel Query

"Khi dữ liệu vượt 100GB, đã đến lúc chia để trị và huy động binh đoàn CPU!"


1. Vấn Đề: Query Trên Bảng 100GB Chậm Không Thể Chấp Nhận

Scenario:
Bảng orders (100GB) chứa dữ liệu 10 năm (2014-2023) với cấu trúc:

  • order_id (PK)

  • customer_id

  • order_date

  • amount

Yêu cầu: Tính tổng doanh thu theo tháng năm 2023:

SELECT 
  EXTRACT(MONTH FROM order_date) AS month, 
  SUM(amount) 
FROM orders  
WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31'  
GROUP BY month;

Kết quả: Thời gian thực thi ~120 giây, Execution Plan hiển thị Seq Scan toàn bộ bảng.


2. Phân Tích Execution Plan

Bước 1: Hiểu Cơ Chế Full Scan
Gather  (cost=1,000,000.00..1,200,000.00 rows=12 width=8)  
  Workers Planned: 1  
  -> Parallel Seq Scan on orders  (cost=0.00..1,000,000.00 rows=12 width=8)  
        Filter: (order_date >= '2023-01-01' AND order_date <= '2023-12-31')
  • Vấn Đề Chính:

    1. Toàn bộ 100GB được quét dù chỉ cần dữ liệu 1 năm (10% tổng size).

    2. Parallel Workers chỉ 1 → Không tận dụng đa CPU.

    3. Không có index hỗ trợ order_date.


3. Giải Pháp: Kết Hợp Partitioning Và Parallel Query

Bước 1: Tạo Partitioned Table
-- Bảng master  
CREATE TABLE orders (  
  order_id BIGSERIAL,  
  customer_id INT,  
  order_date DATE NOT NULL,  
  amount NUMERIC  
) PARTITION BY RANGE (order_date);  

-- Partition cho năm 2023  
CREATE TABLE orders_2023_01 PARTITION OF orders  
  FOR VALUES FROM ('2023-01-01') TO ('2023-02-01');  
... (Tạo tương tự cho các tháng còn lại)  
CREATE TABLE orders_2023_12 PARTITION OF orders  
  FOR VALUES FROM ('2023-12-01') TO ('2024-01-01');
Bước 2: Cấu Hình Parallel Query (PostgreSQL)
ALTER SYSTEM SET max_parallel_workers_per_gather = 4;  -- Sử dụng 4 workers  
ALTER SYSTEM SET work_mem = '256MB';                   -- Tăng bộ nhớ cho sort  
SELECT pg_reload_conf();
Bước 3: Thêm Index Trên Partition
CREATE INDEX idx_orders_2023_date ON orders_2023_01 (order_date);  
... (Tạo index cho từng partition 2023)

4. Execution Plan Sau Khi Tối Ưu

Finalize GroupAggregate (cost=5000.00..6000.00 rows=12 width=8)  
  -> Gather Merge (cost=5000.00..5800.00 rows=48 width=8)  
    Workers Planned: 4  
    -> Partial GroupAggregate (cost=4000.00..4200.00 rows=12 width=8)  
      -> Parallel Index Scan on orders_2023_01  
        ... (Tương tự cho các partition 2023 khác)  
          Index Cond: (order_date >= '2023-01-01' AND order_date <= '2023-12-31')
Kết Quả:
  • Chỉ quét 12 partitions năm 2023 (10GB) thay vì 100GB → Giảm 90% I/O.

  • 4 workers xử lý song song → Tận dụng đa CPU.

  • Thời gian thực thi: Từ 120 giây8 giây (93% cải thiện).


5. Tổng Kết

Chỉ SốTrước Khi Tối ƯuSau Khi Tối Ưu
Dữ Liệu Quét100GB10GB
Thời Gian Thực Thi120,000 ms8,000 ms
Số Workers14
Phương Pháp ScanSeq ScanIndex Scan

Lý Do Hiệu Quả:

  • Partitioning chia dữ liệu thành các phần nhỏ → Giảm I/O và lock.

  • Parallel Query xử lý đồng thời → Tăng tốc độ tính toán.


6. Bài Tập Thực Hành

Dataset Mẫu:
CREATE TABLE sensor_logs (  
    log_id BIGSERIAL,  
    sensor_id INT,  
    log_time TIMESTAMP,  
    value FLOAT  
) PARTITION BY RANGE (log_time);
Yêu Cầu:
  1. Tạo partitions theo ngày cho tháng 1/2024.

  2. Chạy query tính giá trị trung bình mỗi giờ trong ngày 2024-01-15.

  3. So sánh hiệu suất khi dùng Parallel Query vs không.

Câu Hỏi:
  • Tại sao không nên tạo quá nhiều partitions (ví dụ: theo giây)?

7. Mở Rộng & Thảo Luận

Khi Nào Dùng Partitioning vs Sharding?
Tiêu ChíPartitioningSharding
Phạm ViTrong 1 databaseNhiều database/server
Độ Phức TạpTrung bìnhCao
Use CaseDữ liệu lớn nhưng ở 1 nodeDữ liệu cực lớn, cần scale ngang
Chiến Lược Partitioning Hiệu Quả
  • Range Partitioning: Dữ liệu theo thời gian (ngày/tháng/năm).

  • List Partitioning: Phân chia theo category (ví dụ: vùng địa lý).

  • Hash Partitioning: Phân tán đều dữ liệu (phù hợp cho cân bằng tải).

Cảnh Báo Khi Dùng Partitioning
  • Overhead Quản Lý: Mỗi partition có index riêng → Tăng chi phí maintain.

  • Giới Hạn Số Partition: PostgreSQL hỗ trợ tối đa ~32,000 partitions.

Case Study: Parallel Query Trong MySQL 8.0
-- Kích hoạt parallel query cho bảng lớn  
ALTER TABLE orders PARALLEL 4;  

-- Query tự động sử dụng parallel execution  
SELECT /*+ PARALLEL(orders, 4) */ ...  
FROM orders  
WHERE ...

Kết Luận

Partitioning và Parallel Query là vũ khí tối thượng để xử lý dữ liệu lớn. Trong ví dụ này, thời gian query giảm từ 2 phút xuống còn 8 giây - cải thiện 93%! Ở bài tiếp theo (Bài 19), chúng ta sẽ khám phá cách đọc hiểu Execution Plan dạng JSON và sử dụng các công cụ trực quan hóa mạnh mẽ.

👉 Bài Tập Về Nhà: Tạo partitioned table cho bảng sales theo quý và đo hiệu suất khi aggregate dữ liệu với max_parallel_workers_per_gather = 8!