Tinh chỉnh chỉ mục HNSW trên pgvector để tăng tốc độ tìm kiếm
Đánh giá hiệu năng hiện tại của chỉ mục
Trước khi tối ưu, cần đo lường thời gian truy vấn hiện tại để có số liệu so sánh.
Chạy câu lệnh SQL này để kiểm tra thời gian thực thi khi tìm kiếm vector gần nhất với 5 kết quả trên bảng dữ liệu hiện có.
EXPLAIN (ANALYZE, COSTS, TIMING) SELECT * FROM documents WHERE embedding '[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]' ORDER BY embedding '[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]' LIMIT 5;
Kết quả mong đợi: Bạn sẽ thấy thông báo "Sequential Scan" hoặc "Index Scan" với thời gian thực thi (actual time) có thể từ 100ms đến vài giây tùy số lượng dữ liệu. Nếu thấy Sequential Scan, hiệu năng đang rất thấp.
Xóa chỉ mục cũ và tạo chỉ mục HNSW tối ưu
Chỉ mục mặc định hoặc cũ có thể không phù hợp với tham số tìm kiếm nhanh. Cần xóa và tạo mới với cấu hình HNSW (Hierarchical Navigable Small World) phù hợp cho vector.
Thực hiện lệnh xóa chỉ mục cũ (nếu có) và tạo chỉ mục mới với tham số `m` và `ef_construction` để cân bằng giữa tốc độ build và độ chính xác.
DROP INDEX IF EXISTS idx_documents_embedding;
CREATE INDEX idx_documents_embedding_hnsw ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
Kết quả mong đợi: PostgreSQL trả về "INDEX CREATED". Quá trình này có thể mất vài giây đến vài phút tùy kích thước bảng.
Tinh chỉnh tham số tìm kiếm (ef_search)
Tham số `ef_search` trong HNSW quyết định độ chính xác và tốc độ khi tìm kiếm. Giá trị cao hơn = chính xác hơn nhưng chậm hơn.
Áp dụng tham số này trực tiếp vào câu lệnh tìm kiếm của ứng dụng hoặc chạy thử nghiệm trong SQL để tìm điểm cân bằng.
SET hnsw.ef_search = 40;
EXPLAIN (ANALYZE, COSTS, TIMING) SELECT * FROM documents WHERE embedding '[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]' ORDER BY embedding '[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]' LIMIT 5;
Kết quả mong đợi: Thông báo "Index Scan using idx_documents_embedding_hnsw" xuất hiện và "actual time" giảm đáng kể so với bước đánh giá ban đầu (ví dụ: xuống còn 5-10ms).
Verify kết quả tối ưu hóa
Để đảm bảo chỉ mục hoạt động ổn định, chạy lại câu lệnh tìm kiếm nhiều lần và kiểm tra consistency của kết quả.
SELECT count(*) FROM documents WHERE embedding '[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]' ORDER BY embedding '[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]' LIMIT 5;
Kết quả mong đợi: Query trả về đúng 5 dòng dữ liệu nhanh chóng, không có cảnh báo lỗi về chỉ mục.
Cấu hình Firewall (UFW) và chứng chỉ SSL cho kết nối an toàn
Cấu hình UFW để chặn truy cập trái phép
Mặc định PostgreSQL lắng nghe trên tất cả giao diện mạng (0.0.0.0), đây là lỗ hổng bảo mật nghiêm trọng. Cần dùng UFW để chỉ cho phép IP của máy chủ ứng dụng truy cập cổng 5432.
Đầu tiên, thiết lập chính sách mặc định là chặn tất cả và chỉ cho phép SSH để không bị khóa ngoài.
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow from 192.168.1.50 to any port 5432 proto tcp
ufw --force enable
Kết quả mong đợi: Firewall được bật thành công. Dòng lệnh cuối cùng sẽ in "Firewall is active and enabled on system startup". Lưu ý thay 192.168.1.50 bằng IP thực tế của máy chạy ứng dụng LangChain.
Tạo chứng chỉ SSL tự ký cho PostgreSQL
Để mã hóa dữ liệu truyền tải giữa ứng dụng và Database, cần tạo cặp chứng chỉ (certificate) và khóa riêng tư (private key).
Thực hiện tạo thư mục chứng chỉ và dùng OpenSSL để sinh chứng chỉ tự ký (self-signed) hợp lệ trong 10 năm.
sudo mkdir -p /var/lib/postgresql/16/main/ssl
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /var/lib/postgresql/16/main/ssl/postgresql.key -out /var/lib/postgresql/16/main/ssl/postgresql.crt -subj "/C=VN/ST=HCM/L=District 1/O=MyCompany/OU=Dev/CN=postgres-db"
Kết quả mong đợi: Hai file `postgresql.key` và `postgresql.crt` được tạo trong thư mục `/var/lib/postgresql/16/main/ssl/`. Không có lỗi về quyền hạn (permission).
Cấu hình PostgreSQL sử dụng SSL
Cần cập nhật file cấu hình `postgresql.conf` để kích hoạt SSL và chỉ định đường dẫn đến chứng chỉ vừa tạo.
Chỉnh sửa file `/etc/postgresql/16/main/postgresql.conf` với nội dung sau (dùng `sed` để đảm bảo chính xác).
sudo sed -i 's/#ssl = on/ssl = on/' /etc/postgresql/16/main/postgresql.conf
sudo sed -i 's/#ssl_cert_file =/ssl_cert_file = \/var\/lib\/postgresql\/16\/main\/ssl\/postgresql.crt/' /etc/postgresql/16/main/postgresql.conf
sudo sed -i 's/#ssl_key_file =/ssl_key_file = \/var\/lib\/postgresql\/16\/main\/ssl\/postgresql.key/' /etc/postgresql/16/main/postgresql.conf
sudo systemctl restart postgresql
Kết quả mong đợi: Dịch vụ PostgreSQL khởi động lại thành công. File cấu hình đã có các dòng `ssl = on` được bật.
Cấu hình quyền truy cập (pg_hba.conf) yêu cầu SSL
Để buộc mọi kết nối từ mạng phải sử dụng SSL, cần sửa file `pg_hba.conf` sang chế độ `cert` hoặc `md5` với loại `ssl`.
Chỉnh sửa file `/etc/postgresql/16/main/pg_hba.conf` để thay thế dòng `host` cũ bằng dòng yêu cầu SSL.
sudo nano /etc/postgresql/16/main/pg_hba.conf
Sửa nội dung file thành (thay `host all all 0.0.0.0/0 md5` bằng dòng dưới đây):
hostssl all all 0.0.0.0/0 md5
Sau khi lưu, chạy lệnh khởi động lại để áp dụng:
sudo systemctl restart postgresql
Kết quả mong đợi: PostgreSQL chấp nhận kết nối chỉ khi client gửi chứng chỉ hoặc yêu cầu SSL. Kết nối không SSL sẽ bị từ chối ngay lập tức.
Verify kết quả bảo mật
Thử kết nối từ xa không dùng SSL để xác nhận bị chặn, và kết nối có SSL để xác nhận thành công.
psql "host=192.168.1.1 user=postgres dbname=ai_rag sslmode=require" -c "SELECT version();"
psql "host=192.168.1.1 user=postgres dbname=ai_rag sslmode=disable" -c "SELECT version();"
Kết quả mong đợi: Dòng lệnh thứ nhất (sslmode=require) thành công và in ra version. Dòng lệnh thứ hai (sslmode=disable) bị lỗi "connection refused" hoặc "no pg_hba.conf entry for host...".
Hướng dẫn debug lỗi kết nối database và xử lý lỗi bộ nhớ trong LangChain
Debug lỗi kết nối Database (Connection Timeout/Refused)
Lỗi phổ biến nhất là ứng dụng LangChain không thể kết nối do firewall chặn hoặc cấu hình `pg_hba.conf` chưa đúng.
Sử dụng lệnh `psql` với tham số chi tiết để kiểm tra trạng thái kết nối từ bên ngoài trước khi debug code Python.
psql "host=192.168.1.1 user=postgres dbname=ai_rag sslmode=require" -c "\conninfo"
Kết quả mong đợi: Nếu thành công, bạn sẽ thấy thông tin kết nối bao gồm `SSL connection using certificate authentication`. Nếu lỗi, thông báo sẽ chỉ rõ nguyên nhân (ví dụ: `FATAL: no pg_hba.conf entry`).
Xử lý lỗi bộ nhớ (OOM) khi tạo Embeddings
Khi xử lý lượng lớn tài liệu, LangChain có thể cố gắng load toàn bộ embeddings vào RAM gây tràn bộ nhớ (Out Of Memory) và làm sập tiến trình Python.
Giải pháp là sử dụng cơ chế phân trang (pagination) hoặc giới hạn batch size trong quá trình tải dữ liệu vào Vector Store.
from langchain.vectorstores import PGVector
from langchain.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
# Giới hạn số lượng bản ghi được tải cùng lúc
vectorstore = PGVector.from_documents(
documents=documents,
embeddings=embeddings,
collection_name="my_collection",
connection_string="postgresql+psycopg2://user:pass@host:5432/dbname",
batch_size=100 # Thêm tham số này để giảm áp lực RAM
)
Kết quả mong đợi: Tiến trình Python chạy ổn định mà không bị kill bởi kernel OOM killer. Tốc độ có thể chậm hơn một chút nhưng đảm bảo tính ổn định.
Cấu hình giới hạn bộ nhớ cho PostgreSQL
Đôi khi lỗi bộ nhớ xảy ra ở phía Database do `shared_buffers` hoặc `work_mem` quá cao so với RAM vật lý của server.
Chỉnh sửa file `/etc/postgresql/16/main/postgresql.conf` để giới hạn bộ nhớ sử dụng cho các thao tác phức tạp như sắp xếp và join.
sudo nano /etc/postgresql/16/main/postgresql.conf
Đảm bảo các dòng sau được thiết lập phù hợp với 4GB RAM (ví dụ):
shared_buffers = 1GB
work_mem = 128MB
maintenance_work_mem = 512MB
effective_cache_size = 3GB
Khởi động lại dịch vụ sau khi chỉnh sửa:
sudo systemctl restart postgresql
Kết quả mong đợi: Database không bị crash khi thực hiện các truy vấn phức tạp. Kiểm tra lại bằng `ps aux` để thấy mức sử dụng RAM của tiến trình postgres ổn định.
Verify kết quả xử lý sự cố
Chạy lại script Python của bạn với dữ liệu lớn hơn để xác nhận lỗi đã được khắc phục.
python main.py
Kết quả mong đợi: Ứng dụng chạy hoàn tất, không xuất hiện lỗi `MemoryError` hay `ConnectionRefusedError`. Log hiển thị quá trình xử lý dữ liệu từng batch thành công.
Điều hướng series:
Mục lục: Series: Triển khai Database AI với LangChain, PostgreSQL và Ubuntu 24.04
« Phần 6: Đóng gói và triển khai ứng dụng lên Ubuntu Server