Cấu hình chỉ số HNSW để tăng tốc tìm kiếm
Để tìm kiếm vector nhanh trên dữ liệu lớn, ta không thể dùng quét toàn bộ bảng (linear scan). Cần tạo chỉ số HNSW (Hierarchical Navigable Small World) hỗ trợ truy vấn gần đúng.
HNSW xây dựng đồ thị nhiều lớp, giúp thuật toán nhảy từ vector này sang vector khác để tìm vùng lân cận nhanh hơn, hy sinh một chút độ chính xác để đổi lấy tốc độ.
Trước khi tạo index, cần đảm bảo bảng đã có cột vector và dữ liệu đã được tải vào. Giả sử ta có bảng documents với cột embedding kiểu vector(1536).
Tạo chỉ số HNSW sử dụng metric cosine distance, phù hợp nhất cho văn bản và embedding AI.
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
Kết quả: PostgreSQL trả về INDEX. Chỉ số đã được tạo và sẵn sàng phục vụ truy vấn.
Để kiểm tra chỉ số đã hoạt động, hãy chạy lệnh EXPLAIN trước khi thực thi truy vấn thực tế.
EXPLAIN (ANALYZE, COSTS OFF) SELECT * FROM documents ORDER BY embedding '[1,0,...]' LIMIT 5;
Kết quả mong đợi: Trong output của EXPLAIN, bạn phải thấy dòng Index Scan using hnsw hoặc Bitmap Index Scan. Nếu thấy Seq Scan thì index chưa được sử dụng.
Viết truy vấn SQL với các toán tử khoảng cách
pgvector cung cấp các toán tử khoảng cách khác nhau tùy thuộc vào loại dữ liệu và mục đích sử dụng.
L2 Distance (<->): Khoảng cách Euclid. Dùng khi vector nằm trong không gian vật lý hoặc khi độ lớn vector quan trọng.
Cosine Similarity (<=>): Độ tương đồng cosine. Dùng phổ biến nhất cho văn bản, hình ảnh, vì nó bỏ qua độ lớn vector, chỉ quan tâm hướng.
Inner Product (<#>): Tích vô hướng. Dùng khi vector đã được chuẩn hóa (normalized) về độ dài 1, tương đương cosine nhưng tính toán nhanh hơn.
Truy vấn tìm kiếm vector tương tự nhất với một query vector cụ thể bằng Cosine Similarity.
SELECT title, embedding '[0.1, 0.2, 0.3, ...]' AS distance FROM documents ORDER BY distance ASC LIMIT 5;
Kết quả: Trả về 5 bản ghi có vector gần nhất với vector mẫu, kèm theo điểm số khoảng cách (càng nhỏ càng giống).
Chuyển sang dùng L2 Distance nếu dữ liệu của bạn là tọa độ không gian hoặc chưa được chuẩn hóa.
SELECT title, embedding '[0.1, 0.2, 0.3, ...]' AS distance FROM documents ORDER BY distance ASC LIMIT 5;
Kết quả: Trả về 5 bản ghi gần nhất tính theo khoảng cách Euclid.
Chuyển sang dùng Inner Product nếu vector đã được chuẩn hóa (norm = 1) để tối ưu tốc độ.
SELECT title, embedding '[0.1, 0.2, 0.3, ...]' AS distance FROM documents ORDER BY distance DESC LIMIT 5;
Kết quả: Lưu ý với Inner Product, ta sắp xếp DESC vì giá trị càng lớn (gần 1) thì càng tương đồng.
Thực hiện tìm kiếm với tham số top-k
Trong thực tế, ứng dụng AI thường chỉ cần hiển thị top-k kết quả liên quan nhất để tránh nhiễu và giảm tải cho frontend.
Tham số top-k được điều khiển trực tiếp bằng mệnh đề LIMIT trong SQL kết hợp với ORDER BY khoảng cách.
Tìm kiếm top-10 tài liệu tương tự nhất cho một câu hỏi (query vector) đã được tính toán sẵn.
SELECT id, title, embedding '[0.5, 0.5, 0.0, ...]' AS similarity FROM documents ORDER BY similarity ASC LIMIT 10;
Kết quả: Trả về chính xác 10 dòng dữ liệu có điểm tương đồng cao nhất (khoảng cách nhỏ nhất).
Để đảm bảo hiệu năng, luôn đặt LIMIT trước khi thực thi trên bảng lớn. Không bao giờ chạy ORDER BY toàn bộ bảng mà không có LIMIT.
SELECT * FROM (SELECT id, title, embedding '[0.5, 0.5, 0.0, ...]' AS similarity FROM documents) AS subq ORDER BY similarity ASC LIMIT 10;
Kết quả: Cách viết này giúp optimizer PostgreSQL hiểu rõ ý định giới hạn số lượng và tận dụng chỉ số HNSW tốt hơn trong một số trường hợp phức tạp.
Tối ưu hóa bằng ngưỡng điểm tương đồng (Threshold)
Đôi khi top-k vẫn trả về các kết quả "không liên quan" nếu dữ liệu quá thưa thớt. Cần đặt ngưỡng (threshold) để lọc bỏ các kết quả quá xa.
Ngưỡng này là giá trị tối đa của khoảng cách (distance) được chấp nhận. Nếu khoảng cách lớn hơn ngưỡng, bỏ qua kết quả đó.
Sử dụng mệnh đề WHERE để giới hạn khoảng cách Cosine. Chỉ lấy những kết quả có khoảng cách nhỏ hơn 0.5.
SELECT id, title, embedding '[0.5, 0.5, 0.0, ...]' AS distance FROM documents WHERE embedding '[0.5, 0.5, 0.0, ...]' < 0.5 ORDER BY distance ASC LIMIT 10;
Kết quả: Có thể trả về ít hơn 10 dòng nếu không đủ dữ liệu đạt ngưỡng. Đảm bảo chất lượng kết quả cao hơn số lượng.
Để tối ưu hơn nữa, tránh tính toán khoảng cách hai lần trong WHERE và ORDER BY, hãy dùng CTE (Common Table Expression).
WITH ranked AS (SELECT id, title, embedding '[0.5, 0.5, 0.0, ...]' AS distance FROM documents) SELECT * FROM ranked WHERE distance < 0.5 ORDER BY distance ASC LIMIT 10;
Kết quả: PostgreSQL chỉ tính toán toán tử khoảng cách một lần, giảm chi phí CPU và tăng tốc độ truy vấn đáng kể.
Kết hợp lọc dữ liệu (Filtering) cùng tìm kiếm vector
Trong RAG (Retrieval-Augmented Generation), ta thường cần tìm kiếm vector nhưng chỉ trong một phạm vi con (ví dụ: chỉ tài liệu của năm 2024, hoặc chỉ của user ID 123).
pgvector hỗ trợ lọc dữ liệu (filtering) trước khi thực hiện tìm kiếm vector nhờ vào chỉ số HNSW, gọi là Index Condition Pushdown.
Tìm kiếm top-5 tài liệu tương tự, nhưng chỉ trong phạm vi category = 'technology'.
SELECT id, title, embedding '[0.5, 0.5, 0.0, ...]' AS distance FROM documents WHERE category = 'technology' ORDER BY distance ASC LIMIT 5;
Kết quả: Chỉ trả về các tài liệu thuộc danh mục công nghệ và có vector gần nhất. Chỉ số HNSW sẽ tự động lọc bớt các node không thuộc danh mục này trước khi so sánh vector.
Kết hợp nhiều điều kiện lọc: Lọc theo user_id và status cùng lúc với tìm kiếm vector.
SELECT id, content, embedding '[0.1, 0.2, ...]' AS score FROM documents WHERE user_id = 123 AND status = 'published' ORDER BY score ASC LIMIT 5;
Kết quả: Truy vấn trả về 5 kết quả liên quan nhất chỉ trong phạm vi dữ liệu của user 123 đã được xuất bản.
Verify rằng chỉ số HNSW vẫn được sử dụng khi có điều kiện lọc bằng lệnh EXPLAIN.
EXPLAIN (ANALYZE, COSTS OFF) SELECT id, title FROM documents WHERE category = 'technology' ORDER BY embedding '[0.5, 0.5, 0.0, ...]' LIMIT 5;
Kết quả mong đợi: Output phải hiển thị Index Scan using hnsw với dòng Index Cond chứa điều kiện lọc vector, chứng tỏ PostgreSQL đã tối ưu hóa truy vấn thành công.
Điều hướng series:
Mục lục: Series: Triển khai Database AI với PostgreSQL, pgvector và Ubuntu 24.04
« Phần 3: Tạo pipeline xử lý dữ liệu và chuyển đổi sang vector
Phần 5: Tích hợp PostgreSQL AI với ứng dụng Backend (Node.js/Python) »