SSD Nodes Learn 🎉 VPS $5.50/மாதம் முதல்
கல்வி வழிகாட்டிகள் Matt Connorஆல் Matt Connor · புதுப்பிக்கப்பட்டது 2026-08-16

VPS-ல் vector database இயக்குவது எப்படி?

VPS-ல் pgvector, Qdrant மற்றும் Chroma ஆகியவற்றை ஒப்பிட்டுப் பாருங்கள். RAM பயன்பாடு மற்றும் index அளவை எவ்வாறு கணக்கிடுவது என்பதை அறிந்து, உங்கள் தேடல் செயல்திறனை மேம்படுத்துங்கள்.

VPS-ல் vector database-ஐ இயக்குவதற்கான உண்மையான செலவு

ஒரு VPS-ல் (virtual private server) vector database-ஐ இயக்குவது, நிர்வகிக்கப்படும் (managed) விற்பனையாளர்கள் தீர்வு என்று விற்கும் சிக்கலை நீக்குகிறது. உங்கள் application மற்றும் index ஆகிய இரண்டும் ஒரே machine-ல் இருப்பதால், search request ஒரு network-க்கு பதிலாக loopback socket வழியாகவே பயணிக்கிறது. எஞ்சியிருப்பது எப்போதும் உண்மையான செலவாக இருந்த ஒன்றுதான்: text-ஐ vector-களாக மாற்றுவது. இதற்குப் பின்னால் மேலும் இரண்டு செலவுகள் உள்ளன: index-ஐ உருவாக்குவதற்கான நேரம் மற்றும் அது இயங்கும் காலம் முழுவதும் தேவைப்படும் RAM.

இது எந்த முடிவுகள் முக்கியம் என்பதை மாற்றுகிறது. Region மற்றும் endpoint round trip ஆகியவை உங்கள் கவலையாக இருக்காது. Vector-களின் எண்ணிக்கை, பரிமாணங்கள் (dimensions) மற்றும் நான்கு bytes ஆகியவற்றின் பெருக்கற்பலனே உங்கள் கவலையாக மாறும், ஏனெனில் இதுதான் நீங்கள் மாதந்தோறும் வாடகைக்கு எடுக்கும் memory-ல் index பொருந்துமா என்பதைத் தீர்மானிக்கிறது.

ஒரு server-ல் மில்லி விநாடிகள் எங்கே செலவாகின்றன

சுயமாக ஹோஸ்ட் செய்யப்பட்ட stack-ல் ஒரு similarity query எவ்வாறு செயல்படுகிறது என்பதைப் பார்ப்போம்.

  1. query உரை, embedding model மூலம் vector-ஆக மாற்றப்படுகிறது. CPU-வில் ஒரு சிறிய string-க்கு இது பத்து முதல் நூறு மில்லி விநாடிகள் வரை எடுக்கும். GPU-வில் இது ஒற்றை இலக்க மில்லி விநாடிகளில் முடிந்துவிடும்.
  2. vector, store-க்கு அனுப்பப்படுகிறது. loopback TCP அல்லது Unix domain socket வழியாக இது ஒரு மில்லி விநாடியின் ஒரு பகுதியிலேயே முடிந்துவிடும்.
  3. store அதன் index-ஐ ஆய்வு செய்து, அருகிலுள்ள வரிசைகளை (nearest rows) திருப்பித் தருகிறது.
  4. உங்கள் code, பொருந்தும் உரையை வாசித்து ஒரு prompt-ஐ உருவாக்குகிறது.

மேலே உள்ள பட்டியலில் படி 1 தான் பொதுவாக அதிக நேரத்தை எடுத்துக்கொள்கிறது. படி 2-ல் தான் ஹோஸ்ட் செய்யப்பட்ட vendors தங்களுக்குள் போட்டியிடுகிறார்கள், ஆனால் ஒரே server-ல் இது மிகக் குறைவான நேரமே எடுக்கும். நேரத்தை ஊகிக்க வேண்டாம். உங்கள் server-ல் இரண்டு முனைகளிலும் நேரத்தை அளவிடுங்கள்.

curl http://127.0.0.1:11434/api/embed -s -o /dev/null \
  -w 'embed: %{time_total}s\n' \
  -d '{"model": "nomic-embed-text", "input": "how do I rotate the api key"}'

பின்பு, search query-க்கு முன்னதாக \timing on-ஐ psql-ல் இயக்கவும். முதல் command embed: 0.184312s என்று அச்சிட்டு, psql-க்கு Time: 4.201 ms என்று பதில் கிடைத்தால், index-ஐ tuning செய்வது தவறான வேலை: உங்கள் latency-க்குக் காரணம் embedding model தான். Ollama மூலம் embedding model-ஐ உள்ளூர் அளவில் இயக்குதல் படி 1-ஐ, படி 2 மற்றும் 3 இயங்கும் அதே CPU-வில் வைக்கிறது, எனவே இரண்டு பகுதிகளும் ஒரே cores மற்றும் ஒரே RAM-க்காகப் போட்டியிடுகின்றன. இந்த store-ஐச் சுற்றியுள்ள ingest மற்றும் retrieval loop, சுயமாக ஹோஸ்ட் செய்யப்பட்ட RAG pipeline வழிகாட்டி-ல் விவரிக்கப்பட்டுள்ளது. RAG என்பது retrieval augmented generation ஆகும்: இதில் நீங்கள் உங்கள் சொந்த ஆவணங்களைத் தேடி, சிறந்த பொருத்தங்களை ஒரு prompt-ல் இணைக்கிறீர்கள்.

சுமார் ஒரு லட்சம் vectors-க்கு உட்பட்ட தரவுகளுக்கு, அனைத்தையும் scan செய்யவும்

முழுமையான scan என்பது, நீங்கள் தேடும் query-ஐ சேமிக்கப்பட்டுள்ள ஒவ்வொரு vector-உடனும் ஒப்பிடுவதாகும். இதன் மூலம் கிடைக்கும் recall துல்லியமானது. இதற்கு எந்த index-உம் அல்லது build செய்யும் நிலையும் தேவையில்லை, மேலும் இது தரவுகளுடன் ஒப்பிடும்போது காலாவதியாவதும் இல்லை.

எந்த நிலையில் இது செயல்திறனை இழக்கும் என்பதை கணக்கீடு மூலம் அறியலாம். ஒரு scan, ஒவ்வொரு query-க்கும் n * d * 4 bytes-ஐ வாசிக்கும்; இதில் n என்பது vector-களின் எண்ணிக்கை மற்றும் d என்பது பரிமாணம் (dimension) ஆகும். 100,000 vectors மற்றும் 768 பரிமாணங்கள் கொண்ட தரவுகளுக்கு, ஒரு query-க்கு 307 MB தேவைப்படும். நவீன CPU-க்கள் இதனைச் சில மில்லி விநாடிகளில் கையாளும். ஆனால் 5 மில்லியன் vectors எனும்போது, ஒரு query-க்கு 15 GB தேவைப்படும்; இது நடைமுறைக்குச் சாத்தியமற்றது.

எனவே, vectors-ஐ SQLite-ல் சேமித்து, NumPy மூலம் ஒப்பீட்டைச் செய்யவும்.

import sqlite3, numpy as np

db = sqlite3.connect("docs.db")
db.execute("CREATE TABLE IF NOT EXISTS docs (id INTEGER PRIMARY KEY, body TEXT, vec BLOB)")

def add(body, vec):
    v = np.asarray(vec, dtype=np.float32)
    v /= np.linalg.norm(v)
    db.execute("INSERT INTO docs (body, vec) VALUES (?, ?)", (body, v.tobytes()))
    db.commit()

def search(query_vec, k=5):
    rows = db.execute("SELECT id, body, vec FROM docs").fetchall()
    mat = np.frombuffer(b"".join(r[2] for r in rows), dtype=np.float32).reshape(len(rows), -1)
    q = np.asarray(query_vec, dtype=np.float32)
    q /= np.linalg.norm(q)
    scores = mat @ q
    return [(rows[i][0], rows[i][1], float(scores[i])) for i in np.argsort(-scores)[:k]]

இரு பக்கங்களும் unit length-க்கு மாற்றப்படுவதால், dot product என்பது cosine similarity-க்கு சமமாகும்; அதிக மதிப்பெண் என்பது நெருக்கமான பொருத்தத்தைக் குறிக்கும். ஒவ்வொரு query-க்கும் பதிலாக, startup-ன் போது mat-ஐ ஒருமுறை மட்டும் load செய்யவும்; இது SQLite வாசிப்பை hot path-லிருந்து முழுமையாக நீக்கிவிடும்.

இதை நிராகரிக்கும் முன், உங்கள் கணினியில் நேரத்தைச் சரிபார்த்துப் பார்க்கவும்.

import time
t = time.perf_counter()
search(q)
print(f"{(time.perf_counter() - t) * 1000:.1f} ms")

இதன் உண்மையான வரம்புகள்: இது முழு matrix-ஐயும் RAM-ல் வைத்திருக்கும் ஒரு process ஆகும். இதில் metadata filtering வசதியோ அல்லது ஒரே நேரத்தில் பல writers-ஐக் கையாளும் வசதியோ இல்லை. இவை உங்களுக்குத் தேவைப்படும்போது, வேறு தீர்வுக்கு மாறவும். SQLite என்பது ஒரு வலுவான server-side store ஆகும்; இது குறித்து the SQLite in production guide-ல் விரிவாகக் காணலாம். உங்கள் பணி வரிசைகளை (rows) வழங்குவதை விட, நெடுவரிசைகளை (columns) scan செய்வதாக இருந்தால், the DuckDB and SQLite comparison உங்களுக்கு மிகவும் பயனுள்ளதாக இருக்கும்.

ஏற்கனவே Postgres இயங்கிக்கொண்டிருக்கும்போது pgvector-ஐப் பயன்படுத்துதல்

உங்கள் application-ல் ஏற்கனவே Postgres database இருந்தால், pgvector மிகக்குறைந்த கூடுதல் கட்டமைப்பையே உருவாக்குகிறது. இது ஒரு service அல்ல, ஒரு extension மட்டுமே. Vectors ஒரு சாதாரண table-ல், அவை விவரிக்கும் row-க்கு அருகிலேயே சேமிக்கப்படுகின்றன. எனவே, ஒரு filtered search என்பது ஒரு WHERE clause மட்டுமே; இரண்டாவதாக ஒரு system-ஐ synchronize செய்ய வேண்டிய அவசியமில்லை.

Ubuntu 24.04-ன் universe component-ல் இது கிடைக்கிறது.

sudo apt update
sudo apt install -y postgresql-16-pgvector
sudo -u postgres psql -d yourdb -c 'CREATE EXTENSION vector;'

ஆகஸ்ட் 2026 நிலவரப்படி, அந்த package-ல் pgvector 0.6.0 உள்ளது, இது upstream-ஐ விட மிகவும் பின்தங்கியுள்ளது. குறிப்பாக, iterative index scans-க்கு 0.8 பதிப்பு தேவைப்படுகிறது, எனவே அவற்றை PostgreSQL project-ன் சொந்த repository-லிருந்து பதிவிறக்கவும்.

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-17-pgvector

17 என்பதற்குப் பதிலாக உங்கள் server-ன் major version-ஐக் குறிப்பிடவும்; இதை sudo -u postgres psql -tAc 'SHOW server_version' கட்டளை காட்டும். தவறான major version-க்காக உருவாக்கப்பட்ட extension package-ஐ நிறுவினால் CREATE EXTENSION தோல்வியடையும், ஏனெனில் இயங்கிக்கொண்டிருக்கும் version-ன் share directory-ல் மட்டுமே Postgres தேடும்:

ERROR:  could not open extension control file "/usr/share/postgresql/16/extension/vector.control": No such file or directory

இதன் schema ஒரு புதிய type-ஐக் கொண்ட சாதாரண SQL ஆகும்.

CREATE TABLE chunks (
  id        bigserial PRIMARY KEY,
  doc_id    bigint NOT NULL,
  body      text   NOT NULL,
  embedding vector(768)
);

SELECT id, body FROM chunks
ORDER BY embedding <=> '[0.013, -0.021, 0.004]'
LIMIT 5;

<=> என்பது cosine distance, <-> என்பது L2 (Euclidean) distance, மற்றும் <#> என்பது negative inner product ஆகும். உங்கள் embedding model எதற்காகப் பயிற்சியளிக்கப்பட்டதோ அதைப் பயன்படுத்தவும். தவறானதைத் தேர்ந்தெடுத்தால் பிழை ஏற்படாது, ஆனால் உங்கள் முடிவுகளின் தரம் தானாகவே குறைந்துவிடும்.

Index இல்லாத நிலையில், அந்த query ஒவ்வொரு row-விலும் துல்லியமான தேடலை (exact search) மேற்கொள்ளும். இது மேலே குறிப்பிட்ட brute force முறையின் Postgres பதிப்பாகும், இது அதே துல்லியமான முடிவுகளைத் தரும். max_parallel_workers_per_gather-ஐ அதிகரிப்பது கூடுதல் cores-ஐப் பயன்படுத்த உதவும். இதை முதலில் செய்துவிட்டு, பின்னர் index உருவாக்கவும்; அப்போதுதான் index-ன் செயல்திறனை அளவிட உங்களிடம் ஒரு recall baseline இருக்கும்.

Qdrant, index தரவுத்தளத்தின் அளவை விட அதிகரிக்கும் போது

Qdrant என்பது Rust மொழியில் எழுதப்பட்ட ஒரு பிரத்யேக vector store ஆகும். உங்கள் index-ன் அளவு, உங்கள் application-ன் Postgres-உடன் போட்டியிடாத வகையில் இருக்க வேண்டும் என்றாலோ, அல்லது pgvector-ல் இல்லாத payload filtering மற்றும் quantisation வசதிகள் தேவைப்பட்டாலோ Qdrant சிறந்த தேர்வாகும்.

docker run -d --name qdrant \
  -p 127.0.0.1:6333:6333 -p 127.0.0.1:6334:6334 \
  -e QDRANT__SERVICE__API_KEY="$(openssl rand -hex 32)" \
  -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
  qdrant/qdrant

Port 6333 ஆனது REST (representational state transfer) API மற்றும் /dashboard-ல் உள்ள dashboard-ஐ வழங்குகிறது, மேலும் 6334 ஆனது gRPC-ஐ வழங்குகிறது. பொதுவான VPS-ல் இரண்டு விவரங்கள் முக்கியமானவை. Qdrant-ன் ஆவணங்களின்படி, இந்த service இயல்பாகவே "எந்தவித encryption அல்லது authentication-ம் இன்றி" இயங்குகிறது. மேலும், quickstart-ல் உள்ள -p 6333:6333 அனைத்து interface-களிலும் இணைகிறது (bind). Docker தனது சொந்த forwarding விதிகளை எழுதுவதால், இது ufw விதியை மீறி port-ஐத் திறக்கிறது. எனவே, 127.0.0.1-ல் மட்டும் bind செய்து, ஒரு API key-ஐ அமைக்கவும். Key இல்லாமல் பொது IP-ல் அணுகக்கூடிய Qdrant instance, உங்கள் ஆவணங்களை எவரும் அணுகக்கூடிய பொதுவான நகலாக மாற்றிவிடும்.

curl -s http://127.0.0.1:6333/collections -H "api-key: $QDRANT_API_KEY"

சரியான பதில் {"result":{"collections":[]},"status":"ok","time":0.00002} என்பது போல இருக்கும். {"status":{"error":"Unauthorized"}} என்று பதில் கிடைத்தால், header பெயர் அல்லது key தவறாக உள்ளது என்று அர்த்தம். எந்த பதிலும் கிடைக்கவில்லை என்றால், container இயங்கவில்லை அல்லது வேறு எங்காவது bind செய்யப்பட்டுள்ளது என்று அர்த்தம். அந்த container உங்கள் server-க்கு ஏற்றதா என்பது ஒரு சாதாரண stateful-service சார்ந்த கேள்வி, எனவே Docker மற்றும் host database ஒப்பீடு இதற்கும் பொருந்தும்.

Chroma மற்றும் அதன் பயன்பாடு

Chroma என்பது எவ்வித தயாரிப்பும் இன்றி, ஒரு retrieval demo-வை விரைவாக உருவாக்குவதற்கான எளிய வழியாகும்.

pip install chromadb
chroma run --path /srv/chroma

இது 8000 port-ல் இயங்குகிறது, மேலும் chromadb.HttpClient(host="localhost", port=8000) இதனுடன் இணைகிறது. Chroma ஒரு default embedding function-ஐக் கொண்டுள்ளதால், ஆரம்பக்கட்ட prototype-க்குத் தனி model server தேவையில்லை.

இந்தத் தேர்வின் சாதக பாதகங்களை வெளிப்படையாகப் புரிந்துகொள்ள வேண்டும். Chroma-வைப் பயன்படுத்துவது எளிது, ஏனெனில் இந்த வழிகாட்டியில் விவாதிக்கப்படும் சிக்கலான முடிவுகளை இது மறைத்துவிடுகிறது: எந்த distance metric-ஐப் பயன்படுத்துவது, எவ்வளவு RAM தேவைப்படும் என்பது போன்றவற்றை இதுவே கவனித்துக்கொள்கிறது. ஒரு prototype-க்கு இது சரியாக இருக்கலாம், ஆனால் நீங்கள் பொறுப்பேற்று நிர்வகிக்கும் production சூழலுக்கு இது பொருத்தமற்றதாக இருக்கலாம். உங்கள் தரவு ஏற்கனவே Postgres-ல் இருந்தால், அதை Chroma-விற்கு மாற்றுவது ஒரு கூடுதல் process-ஐ உருவாக்குவதுடன், தரவு ஒத்திசைவு (synchronisation) சிக்கலையும் ஏற்படுத்தும். pgvector-ல் இல்லாத இத்தகைய சிக்கல்களைத் தவிர்க்க, உங்கள் தேவையைச் சரியாக மதிப்பிடுவது அவசியம்.

குறியீட்டிற்கு (index) எவ்வளவு RAM தேவைப்படும்

Raw vectors-ல் இருந்து தொடங்கவும், ஏனெனில் அவைதான் அடிப்படை அளவு; எந்தவொரு tuning-உம் இதை மாற்றாது.

bytes = number_of_vectors * dimensions * 4

ஒவ்வொரு dimension-க்கும் ஒரு 32-bit float என்பது நான்கு bytes ஆகும். Qdrant-ன் capacity planning ஆவணங்கள், metadata மற்றும் optimization-ன் போது உருவாக்கப்படும் தற்காலிக segments-க்காக 1.5 மடங்கினைச் சேர்க்க பரிந்துரைக்கின்றன:

memory_size = number_of_vectors * vector_dimension * 4 bytes * 1.5

ஒரு மில்லியன் vectors-க்கு, உண்மையான embedding models உருவாக்கும் dimensions-ஐக் கொண்டு கணக்கிடப்பட்ட சூத்திரம் இதோ:

ChartRAM for 1 million vectors, by embedding dimension
The data behind this chart
[
  {
    "label": "384 dims",
    "raw_gib": 1.43,
    "with_overhead_gib": 2.15
  },
  {
    "label": "768 dims",
    "raw_gib": 2.86,
    "with_overhead_gib": 4.29
  },
  {
    "label": "1024 dims",
    "raw_gib": 3.81,
    "with_overhead_gib": 5.72
  },
  {
    "label": "1536 dims",
    "raw_gib": 5.72,
    "with_overhead_gib": 8.58
  },
  {
    "label": "3072 dims",
    "raw_gib": 11.44,
    "with_overhead_gib": 17.17
  }
]

இவை சூத்திரத்தின் வெளியீடுகள், gibibytes (GiB)-ல் உள்ளன; இவை அளவீடுகள் அல்ல. நினைவகத்தில் (memory) நீங்கள் ஒதுக்க வேண்டிய இடத்தின் அளவாக இவற்றைக் கருதவும். ஒரு மில்லியன் chunks-க்கு nomic-embed-text போன்ற 768-dimension model-க்கு சுமார் 4.29 GiB தேவைப்படுகிறது, இது 8 GB திட்டத்தில் Postgres-க்கு இடமளித்து பொருந்தும். அதே corpus 3072 dimensions-ல் இருந்தால் 17.17 GiB தேவைப்படும், இது பொருந்தாது.

அந்த அட்டவணையின் முதல் நெடுவரிசைதான் முக்கியமானது, கடைசி அல்ல. Dimension-ஐ பாதியாகக் குறைப்பது, அதன் பின் வரும் ஒவ்வொரு byte-ஐயும் பாதியாகக் குறைக்கும். பொதுவான leaderboard-ல் சற்று குறைவான மதிப்பெண் பெறும் 768-dimension model, ஒரு VPS-ல் சிறந்த பொறியியல் தேர்வாக அமையும். pgvector-ன் halfvec வகை 16-bit floats-ஐச் சேமிக்கிறது, இது bytes-ஐ மீண்டும் பாதியாகக் குறைக்கிறது, மேலும் இது ஒரு expression மூலம் குறியீட்டை உருவாக்குகிறது:

CREATE INDEX ON chunks USING hnsw ((embedding::halfvec(768)) halfvec_cosine_ops);

ஒரு model-ஐத் தேர்ந்தெடுக்கும் முன் நீங்கள் அறிய வேண்டிய ஒரு வரம்பு. pgvector-ன் vector வகை 16,000 dimensions வரை ஏற்கும், ஆனால் அதன் HNSW மற்றும் IVFFlat குறியீடுகள் 2,000 வரை மட்டுமே செயல்படும். அதற்கு மேல் நீங்கள் ஒரு halfvec cast-ஐக் குறியீடு செய்ய வேண்டும், அது 4,000 வரை செல்லும், அல்லது நீங்கள் குறியீட்டை உருவாக்காமல் இருக்க வேண்டும்.

build time-ல் m மற்றும் ef_construction-ன் செலவு என்ன

HNSW (hierarchical navigable small world) என்பது pgvector மற்றும் Qdrant ஆகிய இரண்டிலும் பயன்படுத்தப்படும் index ஆகும். இது ஒரு அடுக்கு அமைப்பிலான வரைபடம் (layered graph). ஒவ்வொரு vector-ம் ஒரு node ஆகும், இது அருகிலுள்ள node-களுடன் இணைப்புகளைக் கொண்டிருக்கும். ஒரு தேடலின் போது, அனைத்தையும் வாசிப்பதற்குப் பதிலாக, இந்த இணைப்புகள் வழியாக query-ஐ நோக்கித் தாவும்.

m என்பது ஒவ்வொரு node-ம் எத்தனை இணைப்புகளை வைத்திருக்க வேண்டும் என்பதைக் குறிக்கிறது. Faiss ஆவணங்களின்படி, HNSW நினைவகத் தேவை ஒரு vector-க்கு (d * 4 + m * 2 * 4) bytes ஆகும், மேலும் m-ஐ 4 முதல் 64-க்குள் வைத்திருக்கப் பரிந்துரைக்கப்படுகிறது. இதை 768 dimensions மற்றும் ஒரு மில்லியன் vector-களுக்கு இயக்கிப் பார்க்கவும்.

ChartCost of raising m at 768 dimensions, 1 million vectors
The data behind this chart
[
  {
    "label": "m = 8",
    "link_bytes_per_vector": 64,
    "total_gib": 2.92
  },
  {
    "label": "m = 16 (default)",
    "link_bytes_per_vector": 128,
    "total_gib": 2.98
  },
  {
    "label": "m = 32",
    "link_bytes_per_vector": 256,
    "total_gib": 3.1
  },
  {
    "label": "m = 64",
    "link_bytes_per_vector": 512,
    "total_gib": 3.34
  }
]

இதன் அளவு வேறுபாடு ஒரு பயனுள்ள ஆச்சரியமாகும். இயல்புநிலை m = 16-லிருந்து m = 64-க்கு மாறும்போது, 3072 bytes vector தரவுகளுடன் கூடுதலாக 512 bytes இணைப்புகள் சேர்கின்றன. இதனால் மொத்த அளவு 2.98 GiB-லிருந்து 3.34 GiB-ஆக மாறுகிறது. இது சுமார் 12 சதவீதம் ஆகும். இந்த dimensions-ல் உங்கள் நினைவகம் m-ஆல் செலவாவதில்லை, மாறாக vector-களாலேயே செலவாகிறது.

m உண்மையில் build time மற்றும் insert time-ஐயே பாதிக்கிறது. ஏனெனில் ஒரு node-ஐ வைப்பது என்பது, அத்தனை அண்டை node-களைக் கண்டறிந்து இணைப்பதைக் குறிக்கும். ef_construction என்பது ஒவ்வொரு node-ஐயும் வைக்கும்போது builder கருத்தில் கொள்ளும் candidate list-ன் அளவு ஆகும். இதை அதிகரிப்பது சிறந்த வரைபடத்தை உருவாக்கும், ஆனால் build நேரத்தை மெதுவாக்கும். இது இறுதி index-ன் அளவை மாற்றாது.

SET maintenance_work_mem = '4GB';
SET max_parallel_maintenance_workers = 7;
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

maintenance_work_mem என்பது build செயல்முறை நிமிடங்களில் முடிய வேண்டுமா அல்லது மணிநேரங்கள் ஆக வேண்டுமா என்பதைத் தீர்மானிக்கும் அமைப்பாகும். ஏனெனில் pgvector, நினைவகத்தில் இடமிருக்கும் வரை வரைபடத்தை அங்கேயே கட்டமைக்கும். இடமில்லாமல் போகும்போது, pgvector பின்வருமாறு தெரிவிக்கும்:

NOTICE:  hnsw graph no longer fits into maintenance_work_mem after 100000 tuples
DETAIL:  Building will take significantly more time.
HINT:  Increase maintenance_work_mem to speed up builds.

இந்த அறிவிப்பு pgvector அச்சிடும் மிக முக்கியமான வரியாகும். இதன் பொருள், build செயல்முறை மிக மெதுவான பாதைக்கு மாறிவிட்டது என்பதாகும். எனவே, அதை ரத்து செய்து, நீங்கள் கணக்கிட்ட RAM அளவை விட இந்த அமைப்பை அதிகரித்து, மீண்டும் தொடங்கவும். இரண்டாவது session-லிருந்து முன்னேற்றத்தைக் கவனிக்கவும்:

SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS pct
FROM pg_stat_progress_create_index;

HNSW initializing மற்றும் loading tuples ஆகியவற்றை அறிக்கையிடுகிறது. ஒரு build நீண்ட நேரம் குறைந்த சதவீதத்திலேயே இருந்து, disk பயன்பாடு குறைவாக இருந்தால், அது maintenance_work_mem சிக்கலே தவிர, query தேக்கமடைந்ததல்ல.

திட்டமிடும்போது கவனிக்க வேண்டிய இரண்டு உண்மைகள்: pgvector-ன் README-ன் படி, HNSW ஆனது IVFFlat-ஐ விட "மெதுவான build நேரத்தையும் அதிக நினைவகத்தையும்" பயன்படுத்துகிறது, ஆனால் அதற்குப் பதிலாக சிறந்த வேகம் மற்றும் recall சமநிலையை வழங்குகிறது. மேலும், HNSW-ஐ காலி அட்டவணையில் உருவாக்க முடியும். ஆனால் IVFFlat-க்கு முதலில் தரவுகளைக் கொண்டு k-means இயக்க வேண்டும், எனவே காலி அட்டவணையில் அதை உருவாக்கினால் recall குறைவாக இருக்கும். புதிய schema-வில் HNSW-ஐ நீங்கள் ஆரம்பத்திலேயே உருவாக்கலாம்.

ef_search: build-க்கு பிறகு நீங்கள் மாற்றக்கூடிய அளவுரு

m மற்றும் ef_construction ஆகியவை index-ல் முடக்கப்பட்டவை (frozen). ef_search அவ்வாறு இல்லை. இது graph-ல் தேடும்போது எத்தனை வேட்பாளர்களை (candidates) வைத்திருக்க வேண்டும் என்பதைத் தீர்மானிக்கிறது; இதை ஒவ்வொரு session-க்கும் அல்லது query-க்கும் ஏற்ப நீங்கள் மாற்றிக்கொள்ளலாம்.

SET hnsw.ef_search = 100;
SELECT id, body FROM chunks ORDER BY embedding <=> $1 LIMIT 5;

இதன் default மதிப்பு 40 ஆகும். இதை அதிகரித்தால் recall உயரும், அதனுடன் latency-யும் உயரும். இதைக் குறைத்தால் இரண்டுமே குறையும். index-ஐ மீண்டும் உருவாக்காமலேயே (rebuild) நீங்கள் மாற்றக்கூடிய ஒரே recall கட்டுப்பாடு இதுதான். எனவே, சரியான விடைகள் உங்களுக்குத் தெரிந்த ஒரு குறிப்பிட்ட query தொகுப்பைக் கொண்டு இதைச் சோதித்து, recall மேம்படுவது நிற்கும் வரை சரிசெய்யவும்.

ஒரு சிக்கல் உள்ளது. ef_search ஒரு selective WHERE clause-உடன் சரியாகச் செயல்படாது. ஏனெனில், index ஒரு குறிப்பிட்ட எண்ணிக்கையிலான வேட்பாளர்களை மட்டுமே வழங்குகிறது, அதன் பிறகுதான் filter பயன்படுத்தப்படுகிறது. பெரும்பாலான வரிசைகளை (rows) நிராகரிக்கும் ஒரு filter-ஐப் பயன்படுத்தும்போது, அட்டவணையில் பொருந்தக்கூடிய வரிசைகள் இருந்தபோதிலும், உங்களுக்கு LIMIT-க்கும் குறைவான முடிவுகளே கிடைக்கலாம். pgvector 0.8 இதற்கு iterative scans மூலம் தீர்வு வழங்குகிறது:

SET hnsw.iterative_scan = relaxed_order;

limit பூர்த்தியாகும் வரை index மீண்டும் மீண்டும் scan செய்யப்படும். இது hnsw.max_scan_tuples வரை செல்லும், இதன் default மதிப்பு 20000 ஆகும். strict_order துல்லியமான distance வரிசையைத் தக்கவைக்கிறது, ஆனால் இதற்கு அதிக செலவாகும். Ubuntu 0.6.0 தொகுப்பில் இந்த வசதி இல்லை; filter பயன்படுத்தும்போது முடிவுகள் விடுபடுவதுதான் இந்த வசதி இல்லை என்பதற்கான அறிகுறியாகும்.

Index ஏன் RAM-ல் இருக்க வேண்டும்

HNSW தேடல் என்பது ஒரு வரைபடத்தின் (graph) வழியே செல்லும் பயணமாகும். ஒவ்வொரு தாவலும் (hop) முந்தைய இடத்திலிருந்து தொடர்பில்லாத ஒரு இடத்தில் சேமிக்கப்பட்டுள்ள node-ஐ வாசிக்கிறது. எனவே, இந்த அணுகல் முறை (access pattern) சீரற்றதாக (random) இருக்கும், மேலும் read-ahead நுட்பம் இதில் உதவாது. வரைபடம் RAM-ல் இருக்கும் வரை, ஒவ்வொரு தாவலும் ஒரு memory reference-ஆக இருக்கும். அது RAM-ல் இல்லாதபோது, ஒரு தாவல் disk read-ஆக மாறக்கூடும். சில நூறு node-களைத் தொடும் ஒரு தேடல், சில நூறு disk read-களாக மாறிவிடும்.

Qdrant ஆவணங்கள் இதைத் தெளிவாக விளக்குகின்றன: "நீங்கள் பாதியளவு vectors-ஐ மட்டுமே RAM-ல் சேமித்தால், தேடல் தாமதம் (search latency) தோராயமாக இருமடங்காகும்." இந்த உண்மையை மனதில் கொண்டு திட்டமிடுங்கள்.

Index முழுமையாக RAM-ல் பொருந்தாதபோது, நீங்கள் எடுக்கும் ஒவ்வொரு முடிவும் ஒரு சமரசமே (trade-off). அதைத் திட்டமிட்டுச் செய்யுங்கள்:

  • Vectors-ஐ memory-map செய்யுங்கள். இதன் மூலம் operating system அடிக்கடி பயன்படுத்தப்படும் (hot) பக்கங்களை cache-ல் வைக்கும், பயன்படுத்தப்படாத (cold) பக்கங்களை disk-ல் விட்டுவிடும். இது தாங்கக்கூடிய வேகத்தில் இருக்க, கீழே வேகமான NVMe (non-volatile memory express) சேமிப்பகம் தேவை.
  • Quantise செய்யுங்கள். ஒவ்வொரு பரிமாணத்தையும் (dimension) நான்கு bytes-க்கு பதிலாக ஒரு byte-ஆகச் சேமிக்கவும். இது vector-ன் அளவை நான்கு மடங்காகக் குறைக்கும், ஆனால் தேடல் துல்லியத்தில் (recall) சிறிய, அளவிடக்கூடிய இழப்பு ஏற்படும்.
  • pgvector-ல் halfvec-க்கு cast செய்யுங்கள். இது ஒரு-byte quantisation-ஐ விடக் குறைவான துல்லிய இழப்புடன், bytes-ன் அளவை பாதியாகக் குறைக்கும்.
  • சிறிய model-ஐப் பயன்படுத்தி embed செய்யுங்கள். இதுவே மிகவும் எளிதான தீர்வு, ஆனால் மக்கள் இதைத் தவிர்க்கிறார்கள். ஏனெனில், இது முழு தரவுத்தொகுப்பையும் (corpus) மீண்டும் embed செய்ய வேண்டிய கட்டாயத்தை உருவாக்குகிறது.

தோல்வி முறைகளும் நீங்கள் காணும் சரங்களும்

could not open extension control file. இயங்கும் Postgres முதன்மைப் பதிப்பிற்கான pgvector தொகுப்பு நிறுவப்படவில்லை. sudo -u postgres psql -tAc 'SHOW server_version' மூலம் பதிப்பைக் கண்டறிந்து, அதற்குப் பொருத்தமான postgresql-NN-pgvector-ஐ நிறுவவும்.

ERROR: expected 768 dimensions, not 1536. நெடுவரிசை வகை (column type) மற்றும் மாதிரி (model) ஆகியவற்றுக்கு இடையே முரண்பாடு உள்ளது. நீங்கள் embedding மாதிரிகளை மாற்றியுள்ளீர்கள், ஆனால் தரவுகளை மீண்டும் உட்பொதிக்கவில்லை (re-embed). இதற்குப் பகுதித் தீர்வு ஏதுமில்லை, ஏனெனில் வெவ்வேறு மாதிரிகளிலிருந்து பெறப்பட்ட வெக்டர்களை ஒப்பிட முடியாது. எனவே, ஒவ்வொரு வரிசையையும் மீண்டும் உருவாக்க வேண்டும்.

வினவல் மெதுவாக உள்ளது மற்றும் EXPLAIN ஒரு sequential scan-ஐக் காட்டுகிறது. குறியீட்டு ஆபரேட்டர் வகுப்பு (index operator class) மற்றும் வினவல் ஆபரேட்டர் ஆகியவை பொருந்தவில்லை. vector_cosine_ops ஆனது <=>-க்கு மட்டுமே உதவும். வினவலில் EXPLAIN ANALYZE-ஐ இயக்கி, Index Scan using ... on chunks உள்ளதா என்று பார்க்கவும். அதற்குப் பதிலாக Seq Scan on chunks-ஐக் கண்டால், நீங்கள் பயன்படுத்தும் வினவல் ஆபரேட்டருக்குப் பொருந்தும் வகையில் குறியீட்டு ஆபரேட்டர் வகுப்பைக் கொண்டு குறியீட்டை மீண்டும் உருவாக்கவும்.

LIMIT-ஐ விடக் குறைவான வரிசைகள் மற்றும் WHERE நிபந்தனை உள்ளது. இது மேலே குறிப்பிடப்பட்ட வடிகட்டுதல் சிக்கலாகும் (filtering trap). hnsw.ef_search-ஐ அதிகரிக்கவும் அல்லது pgvector 0.8-க்கு மாறி hnsw.iterative_scan-ஐ அமைக்கவும்.

குறியீட்டு உருவாக்கம் செயல்முறை முடிந்து, psql-ல் எந்தப் பிழையும் இல்லை. maintenance_work_mem கணினியின் பெரும்பகுதி நினைவகத்திற்கு அமைக்கப்பட்டுள்ளது, ஆனால் shared_buffers மற்றும் உங்கள் பயன்பாட்டிற்கும் நினைவகம் தேவைப்படுவதால், கர்னலின் out-of-memory killer அதை நிறுத்துகிறது. sudo dmesg -T | grep -i 'killed process'-ல் postgres-ஐக் குறிப்பிடும் வரியைக் காணலாம். இந்த அமைப்பைக் குறைக்கவும் அல்லது அதிக நினைவகம் கொண்ட திட்டத்தில் குறியீட்டை உருவாக்கி, தரவுத்தளத்தை மீட்டெடுக்கவும் (restore).

ஒன்றைத் தேர்ந்தெடுத்தல்

நீங்கள் ஏற்கனவே Postgres-ஐப் பயன்படுத்தி, சில மில்லியன் வெக்டார்களுக்கும் குறைவாக வைத்திருந்தால், pgvector-ஐப் பயன்படுத்தவும். இதன் index தரவுகளுக்கு அருகிலேயே அமையும், filtering என்பது ஒரு WHERE clause ஆகும், மேலும் உங்கள் தற்போதைய backup முறையே இதையும் உள்ளடக்கும். ஒருவேளை index-க்குத் தனி மெமரி தேவைப்பட்டாலோ அல்லது அதிகப்படியான payload filtering தேவைப்பட்டாலோ, Qdrant-ஐப் பயன்படுத்தவும்; ஆனால் இதற்காக இரண்டாவது service-ஐ நிர்வகிக்க வேண்டியிருக்கும்.

சுமார் நூறாயிரம் வெக்டார்களுக்குக் கீழ் இருந்தால், எதையும் நிறுவும் முன் brute-force scan-ஐச் சோதித்துப் பார்க்கவும். அந்த அளவில், துல்லியமான முடிவுகளைத் தரும் exhaustive search முறையே சிறந்தது; இதற்கு build step தேவையில்லை. இதுவே சரியான அணுகுமுறை. மாறாக, approximate index-ஐத் தேர்ந்தெடுப்பது என்பது, உங்களுக்குத் தேவைப்படாத மில்லி விநாடி வேகத்திற்காக, RAM பயன்பாடு மற்றும் tuning சிக்கல்களை நீங்களே உருவாக்கிக்கொள்வதாகும்.

FAQ

எனக்கு பிரத்யேக vector database தேவையா, அல்லது Postgres போதுமானதா?

உங்கள் தரவு ஏற்கனவே Postgres-ல் இருந்தால், பெரும்பாலான ஒப்பீடுகள் கூறுவதை விட நீண்ட காலத்திற்கு pgvector போதுமானது. இது vectors-ஐ ஒரு சாதாரண column-ல் சேமிக்கிறது, எனவே வடிகட்டப்பட்ட தேடல் (filtered search) என்பது ஒரு WHERE clause மட்டுமே, மேலும் உங்கள் தற்போதைய backup-ல் index-ம் அடங்கும். Vector பணிச்சுமைக்கு (workload) தனித்தனி memory வரம்பு தேவைப்படும்போதோ, அல்லது pgvector-ல் இல்லாத payload filtering மற்றும் quantisation தேவைப்படும்போதோ Qdrant போன்ற பிரத்யேக சேமிப்பகத்திற்கு மாறவும்.

ஒரு VPS-ல் எத்தனை vectors-ஐ வைத்திருக்க முடியும்?

ஊகிப்பதற்குப் பதிலாக, number_of_vectors * dimensions * 4 bytes * 1.5-ஐப் பயன்படுத்தி கணக்கிடுங்கள். ஒரு மில்லியன் 768-dimension vectors தோராயமாக 4.3 GiB அளவு இருக்கும், எனவே 8 GB plan-ல் Postgres-க்கு இடமளித்து இதையும் சேமிக்க முடியும். ஒரு மில்லியன் 3072-dimension vectors தோராயமாக 17 GiB அளவு இருக்கும், இதற்கு மிகப் பெரிய plan தேவைப்படும். உங்கள் embedding model-ன் dimension தான் இதைப் பாதிக்கும் மிக முக்கியமான காரணி, எனவே memory செலவைக் கருத்தில் கொண்டு அந்த model-ஐத் தேர்வு செய்யவும்.

ஒரே machine-ல் இருக்கும்போது network உங்கள் பிரச்சினை அல்ல, எனவே மற்ற இரண்டு காரணங்களைச் சரிபார்க்கவும். முதலாவதாக, embedding call-ஐத் தனியாக நேரமிடுங்கள் (time), ஏனெனில் CPU-ல் query vector-ஐ உருவாக்குவது தேடலை விட அதிக நேரம் எடுக்கலாம். இரண்டாவதாக, index RAM-ல் இருப்பதை உறுதி செய்யவும். HNSW தேடல் ஒரு graph-ல் சீரற்ற முறையில் தாவுகிறது, எனவே graph disk-க்குச் சென்றால், ஒவ்வொரு தாவலும் ஒரு disk read-ஆக மாறக்கூடும். RAM-ல் உள்ள vectors-ன் எண்ணிக்கையை பாதியாகக் குறைத்தால், தேடல் தாமதம் (latency) தோராயமாக இருமடங்காகும் என்பது Qdrant-ன் வழிகாட்டுதல்.

நான் HNSW index-ஐ உருவாக்க வேண்டுமா?

சுமார் ஒரு லட்சம் vectors-க்குக் குறைவாக இருக்கும்போது தேவையில்லை. ஒரு முழுமையான scan (exhaustive scan) ஒவ்வொரு query-க்கும் n * d * 4 bytes-ஐ வாசிக்கும். 768 dimensions கொண்ட 100,000 vectors-க்கு இது 307 MB ஆகும். நவீன CPU-க்கள் இதை சில மில்லி விநாடிகளில், துல்லியமான முடிவுகளுடன், எந்த build step-ம் இன்றிச் செய்துவிடும். முதலில் உங்கள் hardware-ல் scan நேரத்தை அளவிடுங்கள். ஒரு benchmark கட்டுரை கூறியது என்பதற்காக அல்லாமல், அளவிடப்பட்ட scan நேரம் மிக மெதுவாக இருக்கும்போது மட்டும் index-ஐ உருவாக்குங்கள்.

m-ஐ அதிகரிப்பதால் எனக்கு என்ன செலவு?

Memory-ஐ விட, build time மற்றும் insert time ஆகியவைதான் அதிகம் செலவாகும். 768 dimensions-ல், default m = 16-லிருந்து m = 64-க்கு மாறும்போது, 3072 bytes vector தரவுக்கு எதிராக 512 bytes graph links கூடுதலாகச் சேரும், எனவே மொத்த memory சுமார் 12 சதவீதம் உயரும். இருப்பினும், ஒவ்வொரு insert-ம் நான்கு மடங்கு அதிகமான அண்டை உறவுகளை (neighbours) கண்டறிந்து இணைக்க வேண்டும். முதலில் ef_search-ஐ மாற்றியமைக்கவும், ஏனெனில் அதை மாற்றுவதற்கு எந்தச் செலவும் இல்லை மற்றும் rebuild தேவையில்லை.

#vector-database#rag#pgvector#qdrant#self-hosting