TÀI LIỆU KỸ THUẬT · DATABASE Indexing & Partitioning Từ bài toán đến giải pháp Index · B+Tree · Partitioning NỘI DUNG Bốn phần của bài trình bày 01 Đặt vấn đề cho Indexing Cơ chế lưu trữ và truy vấn khi không có index 02 Indexing B+Tree, thực thi truy vấn, kỹ thuật thiết kế index 03 Đặt vấn đề cho Partitioning Giới hạn của một bảng và một máy 04 Partitioning Cách chia, chiến lược, thách thức thực tế Indexing & Partitioning 02 / 23 PHẦN 1 · ĐẶT VẤN ĐỀ Nghịch lý tối ưu truy vấn “Cứ query chậm là thêm index” đúng, nhưng chỉ là phần nổi của tảng băng. 01 Query vẫn chậm Đã đánh index nhưng latency không cải thiện đáng kể. 02 Ghi chậm hơn Throughput ghi sụt giảm, tốn thêm RAM và dung lượng đĩa. 03 Optimizer bỏ qua index Database tự chuyển sang quét toàn bộ bảng dù index đã tồn tại. Indexing & Partitioning 03 / 23 PHẦN 1 · ĐẶT VẤN ĐỀ Dữ liệu được đọc ghi theo page PAGE · 8–16 KB dòng 1 dòng 2 dòng 3 dòng 4 dòng 5 dòng 6 ... Một page chứa nhiều dòng; DBMS đọc ghi cả page, không theo từng dòng. VỊ TRÍ VẬT LÝ CÁC PAGE TRÊN ĐĨA P7 P2 P9 P1 P5 P3 Thứ tự logic: P1 → P2 → P3 → ... Sau nhiều INSERT, UPDATE, DELETE, các page nằm rải rác trên đĩa và không theo thứ tự nào. Indexing & Partitioning 04 / 23 PHẦN 1 · ĐẶT VẤN ĐỀ Full table scan: đọc tuần tự mọi page Page chứa bản ghi name = X Page 1 Lan · Minh · Hà Page 2 Bình · An · Tú Page 3 Dũng · X · Hoa Page 4 Nam · Vy · Linh Page 5 Phúc · Mai · Sơn Page 6 Quân · X · Thảo Đọc từ page đầu tới page cuối, kể cả sau khi đã tìm thấy: chi phí O(n) Không biết vị trí Bản ghi nằm ở page nào? Phải đọc từng page vào bộ nhớ. Không biết điểm dừng Phía sau còn bản ghi trùng không? Phải quét tới page cuối. ≈ 10 s 1.000 page × 10 ms (ví dụ minh hoạ) Indexing & Partitioning 05 / 23 PHẦN 2 · INDEXING Index là mục lục của bảng INDEX · xếp theo name An → Page 2 Bình → Page 1 Cúc → Page 3 Dũng → Page 1 khóa đã sắp xếp + con trỏ row pointer Page 1 Dũng · Bình Page 2 An · Hoa Page 3 Cúc · Em bảng: dữ liệu rải rác, không theo thứ tự Tra An: đọc index rồi nhảy thẳng tới Page 2. Mục tiêu: O(n) → O(log n). Indexing & Partitioning 06 / 23 PHẦN 2 · INDEXING Các index page nối bằng danh sách liên kết đôi Page 1 khóa 1–9 đĩa #42 Page 2 khóa 10–19 đĩa #7 Page 3 khóa 20–29 đĩa #91 Page 4 khóa 30–39 đĩa #18 Page 5 khóa 40–49 đĩa #63 Muốn tới page thứ n phải đi qua từng page trước nó: O(n) CẤP 1 · SẮP XẾP Có thứ tự thì gặp giá trị khác là dừng, không cần quét hết bảng. CẤP 2 · LIÊN KẾT ĐÔI Mỗi page giữ con trỏ tới page liền trước và liền sau, dù nằm rải rác trên đĩa. Indexing & Partitioning 07 / 23 PHẦN 2 · INDEXING Xây cây B+Tree từ dưới lên 3 · NÚT GỐC 2 · NÚT NHÁNH 1 · NÚT LÁ 7 | 13 4 10 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 Khóa phân tách = low key: giá trị nhỏ nhất của nhánh bên phải. Chèn vào cuối lá không phải sửa nút nhánh. Indexing & Partitioning 08 / 23 PHẦN 2 · INDEXING Tìm ID = 14 chỉ đi qua 3 nút 10 | 20 4 | 7 13 | 16 23 | 27 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 20 21 22 23 24 25 27 28 30 Gốc [10 | 20]: 10 ≤ 14 < 20, đi theo con trỏ giữa tới nút nhánh. Nút nhánh [13 | 16]: 13 ≤ 14 < 16, đi xuống nút lá và tìm thấy khóa 14. Indexing & Partitioning 09 / 23 PHẦN 2 · INDEXING Chèn dữ liệu: lá đầy thì split 30 40 50 lá đã đủ 3 khóa + 45 30 40 45 50 vượt mức 3 khóa split ... | 45 | ... 30 40 45 50 khóa 45 được đưa lên nút cha GỐC TÁCH · CÂY CAO THÊM Split lan lên cha; chỉ khi gốc bị tách, cây mới cao thêm đúng một tầng, nên luôn cân bằng. RANGE · DELETE Range: tìm điểm đầu rồi đi ngang các lá nối nhau. Delete: lá thiếu một nửa thì mượn hoặc gộp (merge). Indexing & Partitioning 10 / 23 PHẦN 2 · INDEXING Độ phức tạp: đọc nhanh hơn, ghi tốn thêm Thao tác Không index B+Tree Tìm 1 khóa O(n) O(log n) Tìm khoảng, k kết quả O(n) O(log n + k) Chèn 1 dòng O(1) O(log n) mỗi index Xoá 1 dòng O(n) để tìm, rồi xoá O(log n) mỗi index Ví dụ 100 triệu khóa: cây nhị phân cao khoảng 27 tầng, B+Tree fan-out ~100 chỉ khoảng 4 tầng. Indexing & Partitioning 11 / 23 PHẦN 2 · INDEXING Ba giai đoạn của một truy vấn dùng index 1 · DUYỆT CÂY O(log n), luôn nhanh 2 · QUÉT LÁ LIÊN KẾT thủ phạm 1: tăng theo số mục khớp 3 · TRUY CẬP BẢNG thủ phạm 2: mỗi dòng một lần đọc Gốc Nút nhánh lá lá lá lá lá Page 1 Page 2 Page 3 Page 4 Page 5 Page 6 Chi phí hai bước sau tăng theo số dòng khớp: đây là nơi query thường chậm. Indexing & Partitioning 12 / 23 PHẦN 2 · INDEXING Hai thủ phạm làm index chậm THỦ PHẠM 1 · Index range scan active active active active ... duyệt ngang hàng nghìn lá Khi nào: truy vấn khoảng, hoặc cột ít giá trị (status, gender). Hậu quả: trả về cỡ 20–30% bảng thì optimizer bỏ index, full scan rẻ hơn. THỦ PHẠM 2 · Table access by row ID Index (name = X) Page 7 Page 2 Page 9 Page 4 mỗi dòng khớp: một lần đọc rải rác Khi nào: SELECT cột không có trong index. Hậu quả: 10.000 dòng là 10.000 lần đọc đĩa ngẫu nhiên. Indexing & Partitioning 13 / 23 PHẦN 2 · KỸ THUẬT THIẾT KẾ Covering index bỏ bước quay về bảng INDEX THƯỜNG Index (name) Bảng users · 10.000 lần quay về Kết quả COVERING INDEX Covering index (name, email, phone) Kết quả · Index Only Scan Tránh SELECT *: chọn dư cột làm mất khả năng dùng covering index. Indexing & Partitioning 14 / 23 PHẦN 2 · KỸ THUẬT THIẾT KẾ Composite index và leftmost prefix INDEX (customer_id, status) 1 · paid 1 · pending 2 · paid 2 · pending 2 · shipped 3 · paid 3 · pending 4 · paid WHERE customer_id = 2 3 mục liền kề: định vị nhanh bằng cây, đọc liền một đoạn. Có dùng index hiệu quả. WHERE status = 'paid' Các mục khớp (chấm cam) rải rác khắp index. Không hiệu quả: thiếu cột đầu. Khóa so sánh lần lượt từng cột: thiếu cột đầu thì không loại được nhánh nào của cây. Indexing & Partitioning 15 / 23 PHẦN 2 · KỸ THUẬT THIẾT KẾ Partial index và điều kiện dùng được index PARTIAL INDEX Chỉ index phần dòng cần Chỉ chứa các dòng thoả một điều kiện, nên nhỏ và nhanh. Hợp với giá trị hiếm, ví dụ status = 'pending'. ĐIỀU KIỆN Giữ cho index dùng được Không bọc hàm quanh cột: YEAR(created_at) = 2026 nên viết thành khoảng ngày. Tránh wildcard đầu: LIKE '%abc'. Khớp kiểu dữ liệu, tránh ép kiểu ngầm. Indexing & Partitioning 16 / 23 PHẦN 2 · KỸ THUẬT THIẾT KẾ Quy trình thiết kế index 1 Tìm truy vấn chậm 2 Đọc kế hoạch bằng EXPLAIN 3 Thiết kế theo WHERE, JOIN, ORDER BY 4 Đo lại kế hoạch, thời gian, chi phí ghi 5 Xoá index không dùng Index không miễn phí: mỗi index làm ghi chậm hơn và tốn dung lượng, nên phải đo được lợi ích. Indexing & Partitioning 17 / 23 PHẦN 3 · ĐẶT VẤN ĐỀ Khi bảng vượt giới hạn của một máy Index vượt ngưỡng RAM Tra cứu và cập nhật index phát sinh nhiều disk I/O. Bảo trì tê liệt VACUUM, backup, rebuild index mất hàng giờ đến hàng ngày. Xoá dữ liệu cũ tốn kém DELETE hàng triệu dòng sinh WAL lớn và phình bảng. Truy vấn khoảng rộng Báo cáo theo tháng, quý vẫn duyệt hàng triệu bản ghi. Chạm trần phần cứng Đĩa và thông lượng I/O của một máy có giới hạn. BÀI TOÁN CẦN GIẢI Chia nhỏ bảng để chỉ đọc đúng phân vùng và quản trị từng phần. Indexing & Partitioning 18 / 23 PHẦN 4 · PARTITIONING Chia bảng thành nhiều phần nhỏ HORIZONTAL · chia theo hàng id user_id type payload Partition 2026-09 các dòng có created_at trong tháng 9 Partition 2026-10 các dòng có created_at trong tháng 10 Schema giống nhau, mỗi partition giữ một nhóm dòng. VERTICAL · chia theo cột Bảng chính id, user_id, type cột gọn, truy vấn nhiều Bảng phụ id, payload cột lớn, ít dùng Tách cột sang bảng khác, nối lại bằng khóa id. Partitioning Các phần nằm trong một database instance. Sharding Các shard nằm trên nhiều máy, mỗi shard là một database độc lập. Indexing & Partitioning 19 / 23 PHẦN 4 · PARTITIONING Partition pruning loại bớt dữ liệu TRUY VẤN WHERE created_at trong tháng 10/2026 events_2026_09 bị loại: partition pruning events_2026_10 Index Scan trên (user_id, created_at) events_2026_11 bị loại: partition pruning events_2026_12 bị loại: partition pruning Chỉ một partition được quét. Thiếu điều kiện trên partition key thì phải quét mọi partition. Indexing & Partitioning 20 / 23