1. Tối ưu hóa tham số HNSW và cân bằng tốc độ/độ chính xác
HNSW (Hierarchical Navigable Small World) là thuật toán index mặc định của pgvector, nhưng cấu hình mặc định thường không tối ưu cho mọi workload.
Tham số m (số lượng cạnh trong mỗi node) và ef_construction (số lượng candidate khi xây dựng index) quyết định độ chính xác và tốc độ build. Tham số ef_search quyết định độ chính xác khi query.
Tăng m và ef_construction giúp index chính xác hơn nhưng tốn RAM và thời gian build lâu hơn. Tăng ef_search giúp query chính xác hơn nhưng chậm hơn.
1.1. Rebuild Index với tham số tối ưu cho dataset lớn
Giả sử bạn đang có bảng vectors với column vector embedding và đã tạo index HNSW mặc định. Bạn cần xóa index cũ và tạo lại với tham số tùy chỉnh.
Tham số gợi ý cho dataset 100k - 1M vectors: m=16, ef_construction=200.
Chạy lệnh sau trong psql để xóa index cũ:
ALTER TABLE vectors DROP INDEX IF EXISTS vectors_embedding_idx;
Kết quả mong đợi: Lệnh chạy thành công, không báo lỗi, index cũ bị xóa.
Tạo index mới với tham số tối ưu:
CREATE INDEX vectors_embedding_idx ON vectors USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 200);
Kết quả mong đợi: PostgreSQL bắt đầu build index. Thời gian build sẽ lâu hơn index mặc định nhưng chất lượng index tốt hơn.
1.2. Điều chỉnh ef_search cho truy vấn
Để cân bằng tốc độ và độ chính xác khi query, bạn có thể set tham số ef trực tiếp trong câu lệnh SQL hoặc trong code Python của LlamaIndex.
Trong SQL, set ef thấp (ví dụ 50) để tăng tốc, cao (ví dụ 200) để tăng độ chính xác.
Ví dụ query với ef=100:
SET hnsw.ef_search = 100;
SELECT id, embedding '[0.1, 0.2, ...]' as distance FROM vectors ORDER BY distance LIMIT 10;
Kết quả mong đợi: Query trả về 10 kết quả gần nhất với độ chính xác cao hơn so với mặc định (thường là 40).
Trong LlamaIndex (Python), bạn truyền tham số similarity_top_k và cấu hình index settings:
from llama_index.core import VectorStoreIndex, Settings
from llama_index.vector_stores.postgres import PGVectorStore
Settings.similarity_top_k = 10
# Cấu hình ef_search cho index
index_settings = {
"hnsw_ef_search": 100,
"hnsw_m": 16
}
vector_store = PGVectorStore(
connection_string="postgresql://user:pass@host:5432/dbname",
table_name="vectors",
index_settings=index_settings
)
index = VectorStoreIndex.from_vector_store(vector_store)
Kết quả mong đợi: LlamaIndex tự động áp dụng tham số ef_search=100 khi thực hiện query, cải thiện độ chính xác kết quả RAG.
1.3. Verify hiệu năng sau khi tối ưu
Sử dụng lệnh EXPLAIN (ANALYZE, BUFFERS) để đo lường thời gian thực thi và bộ nhớ dùng cho query.
EXPLAIN (ANALYZE, BUFFERS)
SELECT id FROM vectors
WHERE embedding '[0.1, 0.2, 0.3, ...]' < 0.5
ORDER BY embedding '[0.1, 0.2, 0.3, ...]'
LIMIT 10;
Kết quả mong đợi: Xem phần Execution Time (ms) và Planning Time. Nếu thời gian giảm và số buffer hit tăng, index đang hoạt động tốt.
2. Xử lý lỗi kết nối và giới hạn bộ nhớ PostgreSQL
PostgreSQL thường gặp lỗi FATAL: too many connections hoặc out of memory khi xử lý vector search quy mô lớn.
Cần điều chỉnh file cấu hình postgresql.conf và pg_hba.conf để giải quyết.
2.1. Tăng số lượng kết nối tối đa (max_connections)
Mặc định max_connections là 100. Với ứng dụng RAG có nhiều worker hoặc user đồng thời, con số này không đủ.
Tham số max_connections nằm trong file /etc/postgresql/16/main/postgresql.conf (tùy phiên bản PostgreSQL, 16 cho Ubuntu 24.04).
Chỉnh sửa file cấu hình:
sudo nano /etc/postgresql/16/main/postgresql.conf
Tìm dòng #max_connections = 100 và thay đổi thành:
max_connections = 500
Tăng shared_buffers để hỗ trợ nhiều connection (thường set bằng 25% RAM, tối đa 4GB cho vector search):
shared_buffers = 2GB
Khởi động lại dịch vụ PostgreSQL để áp dụng:
sudo systemctl restart postgresql
Kết quả mong đợi: PostgreSQL khởi động lại thành công. Bạn có thể tạo 500 kết nối đồng thời mà không bị lỗi.
2.2. Xử lý lỗi Out of Memory (OOM) trong Vector Search
Lỗi out of memory thường xảy ra khi work_mem hoặc effective_cache_size không đủ cho các phép toán vector phức tạp.
Trong postgresql.conf, tăng work_mem (bộ nhớ cho mỗi thao tác sort/hash) và maintenance_work_mem (cho build index).
Cấu hình gợi ý cho server 16GB RAM:
work_mem = 128MB
maintenance_work_mem = 2GB
effective_cache_size = 12GB
Khởi động lại dịch vụ:
sudo systemctl restart postgresql
Kết quả mong đợi: Query vector và build index không còn bị kill bởi OOM killer của Linux.
2.3. Sử dụng Connection Pooling với PgBouncer (Khuyến nghị)
Thay vì tăng max_connections quá cao (tốn RAM), cách tốt nhất là dùng PgBouncer để pool các kết nối.
Cài đặt PgBouncer:
sudo apt update
sudo apt install pgbouncer
Cấu hình file /etc/pgbouncer/pgbouncer.ini:
[databases]
mydb = host=localhost port=5432 dbname=ai_db
[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
Tạo file /etc/pgbouncer/userlist.txt với nội dung (user:password):
myuser:mysecurepassword
Khởi động PgBouncer:
sudo systemctl enable pgbouncer
sudo systemctl start pgbouncer
Trong ứng dụng Python, đổi connection string sang port 6432:
connection_string = "postgresql://myuser:mysecurepassword@localhost:6432/ai_db"
Kết quả mong đợi: Ứng dụng có thể mở 1000 kết nối logic nhưng chỉ tiêu tốn 20 kết nối vật lý thực tế vào PostgreSQL, giảm tải RAM đáng kể.
3. Giải quyết vấn đề xung đột phiên bản thư viện
Xung đột thường xảy ra giữa pgvector, psycopg2, numpy và llama-index.
Đặc biệt, psycopg2 cần thư viện C libpq-dev và phải được compile đúng version với PostgreSQL đang chạy.
3.1. Cài đặt psycopg2-binary vs psycopg2
psycopg2-binary dễ cài nhưng không khuyến khích cho production vì có thể bị lỗi runtime. psycopg2 cần compile từ source.
Đảm bảo cài đặt dependencies C trước:
sudo apt install python3-dev libpq-dev build-essential
Xóa các thư viện psycopg2 cũ trong virtualenv:
pip uninstall -y psycopg2 psycopg2-binary
Cài đặt version ổn định nhất cho PostgreSQL 16 (thường là 2.9.x trở lên):
pip install psycopg2==2.9.9
Kết quả mong đợi: Lệnh pip install chạy thành công với thông báo Building wheel... và Successfully installed.
3.2. Xung đột giữa numpy và llama-index
LlamaIndex có thể yêu cầu numpy version cao hơn hoặc thấp hơn so với các thư viện AI khác.
Kiểm tra version numpy hiện tại:
python -c "import numpy; print(numpy.__version__)"
Nếu gặp lỗi AttributeError: module 'numpy' has no attribute 'int' (thường gặp khi numpy >= 1.24 và code cũ), hãy downgrade numpy:
pip install "numpy
Hoặc upgrade LlamaIndex để hỗ trợ numpy mới:
pip install --upgrade llama-index llama-index-vector-stores-postgres
Kết quả mong đợi: Import thư viện llama_index không còn báo lỗi về numpy.
3.3. Verify môi trường Python
Chạy script kiểm tra nhanh:
python -c "import psycopg2; import numpy; import llama_index; print('All imports successful')"
Kết quả mong đợi: In ra dòng All imports successful mà không có lỗi.
4. Tips nâng cao: Chia nhỏ index và caching kết quả
Khi dataset vector quá lớn (trên 5 triệu), hiệu năng HNSW sẽ giảm. Cần chia nhỏ index hoặc dùng caching.
4.1. Chia nhỏ Index theo ngữ cảnh (Sharding)
Thay vì một index khổng lồ, hãy chia dữ liệu theo domain hoặc date vào các bảng con khác nhau, mỗi bảng có index riêng.
Tạo bảng con cho dữ liệu cũ và mới:
CREATE TABLE vectors_2023 (LIKE vectors INCLUDING ALL);
CREATE TABLE vectors_2024 (LIKE vectors INCLUDING ALL);
Chuyển dữ liệu vào bảng con:
INSERT INTO vectors_2023 SELECT * FROM vectors WHERE created_at < '2024-01-01';
INSERT INTO vectors_2024 SELECT * FROM vectors WHERE created_at >= '2024-01-01';
Tạo index riêng cho từng bảng:
CREATE INDEX idx_2023 ON vectors_2023 USING hnsw (embedding vector_cosine_ops);
CREATE INDEX idx_2024 ON vectors_2024 USING hnsw (embedding vector_cosine_ops);
Trong query, kết hợp kết quả từ các bảng con (Union) hoặc chỉ query bảng liên quan:
SELECT * FROM (
SELECT id, embedding '[...]' as dist FROM vectors_2024 WHERE embedding '[...]' < 0.5 LIMIT 20
) t
ORDER BY dist LIMIT 10;
Kết quả mong đợi: Query chỉ quét trên index nhỏ hơn, tốc độ tăng đáng kể (có thể nhanh gấp 2-3 lần).
4.2. Caching kết quả truy vấn với Redis
Nhiều query RAG thường lặp lại cùng một câu hỏi. Cache kết quả vector search vào Redis để tránh query DB.
Cài đặt Redis:
sudo apt install redis-server
sudo systemctl start redis-server
Trong code Python, sử dụng Redis client để check cache trước khi query DB:
import redis
import hashlib
import json
redis_client = redis.Redis(host='localhost', port=6379, db=0)
def get_cached_results(query_embedding, ttl=3600):
# Tạo key hash từ embedding
query_hash = hashlib.sha256(str(query_embedding).encode()).hexdigest()
cache_key = f"rag_result:{query_hash}"
cached = redis_client.get(cache_key)
if cached:
return json.loads(cached)
return None
# Logic trong LlamaIndex
def query_rag(query_text):
embedding = embed_model.get_text_embedding(query_text)
cache_key = hashlib.sha256(str(embedding).encode()).hexdigest()
# Check cache
result = redis_client.get(f"rag_result:{cache_key}")
if result:
return json.loads(result)
# Query DB nếu không có cache
db_results = index.query(query_text)
# Lưu vào cache
redis_client.setex(f"rag_result:{cache_key}", 3600, json.dumps(str(db_results)))
return db_results
Kết quả mong đợi: Các query lặp lại trả về kết quả trong < 1ms từ Redis thay vì < 200ms từ PostgreSQL.
4.3. Verify hiệu năng tổng thể
Chạy benchmark với script Python để đo tổng thời gian phản hồi (latency) và throughput (requests/sec).
import time
import requests
start = time.time()
for i in range(100):
requests.get("http://localhost:8000/query?q=hello")
end = time.time()
print(f"Total time: {end - start:.2f}s, Avg latency: {(end - start)/100*1000:.2f}ms")
Kết quả mong đợi: Thời gian trung bình (Avg latency) giảm xuống dưới 200ms với cache và index tối ưu.
Điều hướng series:
Mục lục: Series: Triển khai Database AI với LlamaIndex và PostgreSQL trên Ubuntu 24.04
« Phần 5: Triển khai hệ thống truy vấn RAG với PostgreSQL