Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 8 — Indexes — Hiểu để viết spec đúng

Technical BA Masterclass Bài 8/60

Mở đầu — vì sao bài này quan trọng

Hãy hình dung bạn vừa viết xong một spec cho tính năng "Tra cứu lịch sử giao dịch" của một ví điện tử. Trên giấy, mọi thứ hoàn hảo: màn hình đẹp, luồng nghiệp vụ rõ ràng, acceptance criteria đầy đủ. Nhưng khi lên môi trường thật với 12 triệu người dùng và 800 triệu dòng giao dịch, người dùng bấm "Tìm" và màn hình quay vòng vòng mất 9 giây. Khách hàng phàn nàn, team dev đổ lỗi cho spec, còn bạn — Technical BA — đứng giữa và không hiểu chuyện gì đang xảy ra.

Rất nhiều trường hợp như vậy bắt nguồn từ một thứ nghe có vẻ "thuần kỹ thuật" mà BA hay né tránh: index. Sự thật là index không phải chuyện của riêng DBA. Một Technical BA hiểu index sẽ viết được những non-functional requirement về hiệu năng có căn cứ, đặt đúng câu hỏi khi review giải pháp, và quan trọng nhất — biết rằng "cho tìm kiếm theo bất kỳ trường nào" hay "cho lọc tự do mọi cột" là những yêu cầu nghe vô hại nhưng có thể giết chết hệ thống.

Bài này không biến bạn thành người tự tay tạo index. Mục tiêu khiêm tốn và thực tế hơn: bạn hiểu index đủ sâu để viết spec đúng, để đối thoại ngang hàng với dev và DBA, và để dự đoán được khi nào một yêu cầu nghiệp vụ sẽ gặp rào cản về hiệu năng.

Khái niệm cốt lõi

Index là gì — góc nhìn dễ hiểu nhất

Index là một cấu trúc dữ liệu phụ mà database tạo ra để tìm dòng dữ liệu nhanh hơn, thay vì phải quét toàn bộ bảng. Cách ẩn dụ kinh điển và vẫn hiệu quả nhất: index giống như mục lục cuối một cuốn sách.

Nếu bạn muốn tìm từ "lạm phát" trong một cuốn giáo trình 600 trang mà không có mục lục, bạn phải lật từng trang một — đó là full table scan (quét toàn bảng). Nhưng nếu có mục lục tra cứu ở cuối sách, bạn mở phần vần "L", thấy "lạm phát — trang 47, 213, 588" và nhảy thẳng tới đó. Index làm đúng việc này cho database.

Cấu trúc index phổ biến nhất là B-Tree (cây cân bằng). Bạn không cần nhớ thuật toán, chỉ cần nắm hai tính chất quan trọng với BA:

  • B-Tree giữ dữ liệu đã được sắp xếp, nên nó cực nhanh cho việc tìm chính xác (=), tìm theo khoảng (>, <, BETWEEN), và sắp xếp (ORDER BY).
  • B-Tree tra cứu theo kiểu "tìm kiếm nhị phân": với một bảng 1 tỷ dòng, nó chỉ cần khoảng 30 bước nhảy để tìm ra dòng cần thiết, thay vì duyệt 1 tỷ dòng.

Cái giá phải trả — không có bữa trưa miễn phí

Đây là phần BA hay bỏ sót. Index không miễn phí. Mỗi index bạn thêm vào sẽ:

  • Tốn dung lượng đĩa. Index là bản sao một phần dữ liệu được sắp xếp. Một bảng nặng có thể có phần index còn nặng hơn cả dữ liệu gốc.
  • Làm chậm ghi (INSERT, UPDATE, DELETE). Mỗi lần thêm hoặc sửa một dòng, database phải cập nhật lại tất cả index liên quan để giữ chúng đúng thứ tự. Bảng có 8 index thì mỗi lần ghi là 8 lần cập nhật phụ.
Đây chính là sự đánh đổi cốt lõi: index làm đọc nhanh nhưng làm ghi chậm và tốn chỗ. Một hệ thống đọc nhiều (báo cáo, tra cứu) sẽ hưởng lợi lớn từ index. Một hệ thống ghi cực nhiều (ví dụ ghi log IoT, ghi sự kiện realtime) phải cân nhắc kỹ vì index có thể trở thành nút thắt cổ chai.

Cardinality — khái niệm quyết định index có đáng hay không

Cardinality là độ "đa dạng" của giá trị trong một cột. Cột email có cardinality rất cao (gần như mỗi dòng một giá trị khác nhau). Cột gioi_tinh (Nam/Nữ/Khác) có cardinality cực thấp — chỉ 3 giá trị.

Quy tắc vàng cho BA: index hiệu quả khi cột có cardinality cao. Index trên email giúp database nhảy thẳng tới đúng 1 dòng. Còn index trên gioi_tinh thường vô dụng — nếu một nửa bảng là "Nam", database đọc index rồi vẫn phải lấy 50% số dòng, chẳng nhanh hơn quét cả bảng là bao. Khi bạn yêu cầu "cho tìm kiếm nhanh theo trạng thái" mà cột trạng thái chỉ có 2-3 giá trị, hãy dừng lại suy nghĩ.

Composite index và quy tắc "trái sang phải"

Index có thể đặt trên nhiều cột cùng lúc (composite index), ví dụ index trên (chi_nhanh, ngay_giao_dich). Điều BA cần nhớ: composite index chỉ phát huy tác dụng khi truy vấn dùng các cột từ trái sang phải, không bỏ cột đầu.

Index (chi_nhanh, ngay_giao_dich) sẽ tăng tốc cho:

  • Lọc theo chi_nhanh
  • Lọc theo chi_nhanh AND ngay_giao_dich
Nhưng không giúp ích nếu chỉ lọc theo ngay_giao_dich mà bỏ qua chi_nhanh. Giống như danh bạ điện thoại sắp theo (Họ, Tên): bạn tra theo Họ thì nhanh, nhưng tra theo Tên mà không biết Họ thì vẫn phải lật cả cuốn. Thứ tự cột trong index là một quyết định thiết kế quan trọng mà BA nên hiểu để không viết spec mâu thuẫn.

Tình huống thực tế

Tình huống 1 — Ví điện tử và màn hình tra cứu giao dịch chậm 9 giây

Một fintech tại TP.HCM (gọi là VietPay) ra mắt tính năng cho người dùng tra cứu lịch sử giao dịch theo khoảng thời gian. Spec viết: "Người dùng chọn khoảng ngày, hệ thống trả về danh sách giao dịch của họ." Bảng transactions có 800 triệu dòng.

Khi lên production, truy vấn lọc theo user_id AND created_at BETWEEN ... AND ... mất 9 giây. Lý do: bảng chỉ có index trên user_id. Database tìm đúng các giao dịch của user đó (giả sử 4.000 dòng), nhưng sau đó phải tự lọc tiếp theo ngày trên cả 4.000 dòng đó, và còn phải sắp xếp lại.

DBA tạo một composite index (user_id, created_at). Truy vấn rớt xuống còn 40 mili-giây — nhanh gấp 225 lần. Database giờ nhảy thẳng tới đúng đoạn dữ liệu của user trong đúng khoảng ngày, đã được sắp sẵn.

Bài học cho BA: khi viết spec có cả lọc lẫn sắp xếp ("hiển thị giao dịch của tôi từ mới đến cũ trong khoảng ngày X"), bạn nên ghi rõ pattern truy vấn này vào tài liệu để dev/DBA chọn đúng composite index. Một dòng ghi chú "truy vấn chính: lọc theo user_id + khoảng created_at, sắp xếp giảm dần theo created_at" có giá trị hơn nhiều so với việc để dev tự đoán.

Tình huống 2 — Sàn thương mại điện tử và yêu cầu "lọc theo mọi thuộc tính"

Một sàn TMĐT khu vực Đông Nam Á thiết kế trang danh sách sản phẩm. Product Owner yêu cầu: "Cho phép khách lọc sản phẩm theo bất kỳ tổ hợp nào của: danh mục, thương hiệu, khoảng giá, màu sắc, đánh giá, tình trạng còn hàng, có khuyến mãi hay không." Nghe rất hợp lý về nghiệp vụ.

Vấn đề: với 7 tiêu chí lọc tự do, số tổ hợp truy vấn có thể lên tới hàng trăm. Không thể tạo composite index cho mọi tổ hợp — và nếu cố làm, bảng sản phẩm sẽ có hàng chục index khiến mỗi lần cập nhật tồn kho (vốn diễn ra liên tục) trở nên chậm chạp. Hơn nữa nhiều cột như "màu sắc", "có khuyến mãi" có cardinality thấp, index gần như vô dụng.

Giải pháp mà kiến trúc sư đề xuất: chuyển phần tìm kiếm/lọc đa tiêu chí sang một search engine chuyên dụng (như Elasticsearch/OpenSearch), còn database quan hệ chỉ giữ vai trò "nguồn sự thật". BA trong dự án này, sau khi hiểu giới hạn của index, đã viết lại NFR: "Tìm kiếm và lọc đa tiêu chí được phục vụ qua tầng tìm kiếm riêng; dữ liệu đồng bộ từ DB chính với độ trễ chấp nhận được dưới 5 giây."

Bài học cho BA: câu "cho lọc theo bất kỳ trường nào" là một cái bẫy spec kinh điển. Hiểu index giúp bạn nhận ra giới hạn này sớm — ngay từ giai đoạn phân tích — và đề xuất kiến trúc phù hợp thay vì để vỡ trận lúc test hiệu năng.

Tình huống 3 — Ngân hàng và bảng ghi log audit bị index quá đà

Một ngân hàng tại Hà Nội có bảng audit_log ghi lại mọi thao tác của nhân viên — khoảng 15 triệu dòng mỗi ngày. Để chiều lòng đội kiểm soát nội bộ vốn hay tra cứu theo đủ kiểu, team dev đã tạo tới 11 index trên bảng này.

Hậu quả: tốc độ ghi log bắt đầu lag vào giờ cao điểm, đôi khi gây nghẽn cả luồng nghiệp vụ chính vì việc ghi audit nằm trong cùng transaction. Mỗi giao dịch nghiệp vụ phải chờ cập nhật 11 index của bảng log.

Sau khi rà soát, đội chỉ giữ lại 3 index thực sự dùng thường xuyên (theo nhan_vien_id, theo thoi_gian, và composite (loai_thao_tac, thoi_gian)), bỏ 8 index ít dùng. Tốc độ ghi phục hồi. Phần tra cứu hiếm gặp được chuyển sang chạy trên bản sao dữ liệu (replica) hoặc data warehouse, không đụng tới hệ thống giao dịch chính.

Bài học cho BA: với bảng ghi nhiều (log, sự kiện, audit), "thêm index cho chắc" là phản tác dụng. Khi viết spec cho các bảng dạng này, hãy phân biệt rõ nhu cầu ghi (cần nhanh, ít index) và nhu cầu đọc/báo cáo (có thể tách sang hệ thống khác).

Hướng dẫn từng bước

Đây là quy trình một Technical BA nên áp dụng khi xử lý phần index trong spec:

  • Liệt kê các truy vấn nghiệp vụ chính. Với mỗi màn hình hay API, viết ra: người dùng tìm/lọc theo cột nào, sắp xếp theo cột nào. Ví dụ: "Tra cứu đơn hàng: lọc theo khach_hang_id + trang_thai, sắp theo ngay_tao giảm dần."
  • Ước lượng quy mô dữ liệu. Bảng này dự kiến bao nhiêu dòng sau 1 năm? Index chỉ thực sự quan trọng khi bảng lớn (từ vài trăm nghìn dòng trở lên). Với bảng nhỏ vài nghìn dòng, full scan vẫn nhanh và bàn về index là phí thời gian.
  • Đánh giá cardinality của cột lọc. Cột bạn định cho lọc có nhiều giá trị khác nhau không? Nếu là cột Yes/No hay trạng thái 2-3 giá trị, đừng kỳ vọng index một mình giải quyết.
  • Xác định tỷ lệ đọc/ghi. Bảng này đọc nhiều hay ghi nhiều? Đây là dữ kiện quyết định mức độ "hào phóng" với index.
  • Ghi pattern truy vấn vào tài liệu kỹ thuật, đừng tự kê đơn index. Bạn không cần viết câu lệnh CREATE INDEX. Việc của BA là mô tả rõ pattern truy cập dữ liệu để DBA/dev chọn index tối ưu. Một mục "Data Access Patterns" trong tech spec là nơi lý tưởng.
  • Đặt câu hỏi xác nhận với dev/DBA. Sau khi giải pháp được đề xuất, hỏi: "Truy vấn chính này có index phù hợp chưa? Đã chạy thử với khối lượng dữ liệu thực tế chưa?"

Lỗi thường gặp & mẹo

  • Tưởng index là chuyện của DBA, BA không cần biết. Sai. BA viết spec sai pattern truy vấn thì index dù tốt cũng không cứu được. Hiểu index là một phần của "viết spec đúng".
  • Viết yêu cầu "tìm kiếm nhanh theo mọi trường". Đây là cái bẫy lớn nhất. Hãy luôn hỏi: "Trường nào thực sự cần tìm nhanh? Tần suất bao nhiêu?" và phân biệt nhu cầu tìm kiếm chính xác với tìm kiếm văn bản tự do (loại sau thường cần search engine riêng).
  • Quên rằng index làm chậm ghi. Khi đề xuất thêm tính năng tra cứu trên một bảng ghi nhiều, hãy nêu rõ đánh đổi để team cân nhắc.
  • Nhầm index với mọi loại tối ưu. Index giải quyết vấn đề "tìm dòng nào", không giải quyết được truy vấn tính toán nặng trên toàn bộ dữ liệu. Báo cáo tổng hợp toàn hệ thống thường cần giải pháp khác (báo cáo này thuộc phạm vi các bài về data warehouse — ở đây chỉ cần biết index không phải thuốc tiên cho mọi thứ).
  • Mẹo đối thoại: khi nghi ngờ truy vấn chậm vì thiếu index, bạn có thể hỏi dev một câu rất "pro": "Truy vấn này đang dùng index nào, hay đang full scan? Cho mình xem EXPLAIN được không?" EXPLAIN là lệnh cho biết database thực thi truy vấn ra sao — BA không cần đọc thành thạo, nhưng biết hỏi đúng chỗ đã tạo khác biệt.
  • Mẹo về soft delete: nếu spec dùng cờ is_deleted để xóa mềm và mọi truy vấn đều kèm WHERE is_deleted = 0, hãy lưu ý điều này với DBA — đôi khi cần partial index để truy vấn không bị chậm bởi đống dữ liệu đã xóa.

Bài tập thực hành

  • Phân tích pattern truy vấn. Lấy một màn hình tra cứu bất kỳ trong sản phẩm bạn đang làm (hoặc tự nghĩ ra, ví dụ "danh sách đơn hàng của khách"). Viết ra: lọc theo cột nào, sắp xếp theo cột nào, ước lượng quy mô bảng. Từ đó đề xuất pattern index nên có và viết một đoạn "Data Access Patterns" cho tech spec.
  • Soi cái bẫy. Đọc lại yêu cầu sau và chỉ ra vấn đề về index: "Hệ thống cho phép admin tìm kiếm người dùng theo tên, email, số điện thoại, trạng thái tài khoản, ngày đăng ký, và lọc tự do theo bất kỳ tổ hợp nào." Bạn sẽ viết lại yêu cầu này thế nào để khả thi về hiệu năng?
  • Đánh giá cardinality. Cho danh sách cột của bảng nhân viên: ma_nhan_vien, phong_ban, gioi_tinh, email, dang_lam_viec (true/false). Cột nào đáng đánh index, cột nào không, và vì sao?
  • Đối thoại với DBA. Viết 3 câu hỏi bạn sẽ đặt cho DBA khi review giải pháp cho một bảng giao dịch dự kiến 500 triệu dòng.

Tóm tắt

Index là cấu trúc dữ liệu phụ giúp database tìm dòng nhanh hơn rất nhiều thay vì quét toàn bảng — đổi lại nó tốn dung lượng và làm chậm thao tác ghi. Là một Technical BA, bạn không cần tự tạo index, nhưng bạn bắt buộc phải hiểu nó để viết spec đúng: liệt kê pattern truy vấn chính, đánh giá cardinality của cột lọc, ước lượng quy mô và tỷ lệ đọc/ghi của bảng, rồi ghi rõ những điều này vào tài liệu kỹ thuật cho dev và DBA.

Ba điều cần khắc cốt ghi tâm: (1) câu "cho tìm kiếm/lọc theo bất kỳ trường nào" là cái bẫy spec — hãy hỏi rõ trường nào thực sự cần và với tần suất nào; (2) index trên cột cardinality thấp thường vô dụng; (3) thêm index vô tội vạ lên bảng ghi nhiều sẽ phản tác dụng. Khi bạn nắm được những nguyên tắc này, bạn không còn đứng giữa lằn ranh "BA viết spec đẹp nhưng hệ thống chạy chậm" nữa — bạn trở thành cầu nối thực sự giữa nghiệp vụ và kỹ thuật.