डेटा लेआउट ऑप्टिमाइज़ करें

Complete

डेटा मॉडलिंग निर्णय वेक्टर खोज प्रदर्शन को महत्वपूर्ण रूप से प्रभावित करते हैं। आप तालिकाओं की संरचना कैसे करते हैं, मेटाडेटा के लिए डेटा प्रकार कैसे चुनते हैं और सहायक अनुक्रमणिका कैसे बनाते हैं, यह निर्धारित करता है कि आपके डेटासेट के बढ़ने पर क्वेरीज़ कुशलतापूर्वक निष्पादित होती हैं या नहीं.

नोट

इस इकाई में कोड उदाहरण मेटाडेटा के साथ वेक्टर डेटा के लिए स्कीमा डिज़ाइन पैटर्न प्रदर्शित करते हैं। इन पैटर्न को अपने विशिष्ट डेटा मॉडल और क्वेरी आवश्यकताओं के अनुसार अनुकूलित करें।

वेक्टर भंडारण विचार

वेक्टर कॉलम पर्याप्त भंडारण और प्रसंस्करण संसाधनों का उपभोग करते हैं। भंडारण विशेषताओं को समझने से आपको स्कीमा डिज़ाइन के बारे में सूचित निर्णय लेने में मदद मिलती है।

प्रत्येक वेक्टर आयाम 4 बाइट्स स्टोरेज (सिंगल-प्रिसिजन फ्लोट के लिए) प्लस फिक्स्ड ओवरहेड जोड़ता है। आयामों और भंडारण के बीच संबंध रैखिक है:

आयाम बाइट्स प्रति वेक्टर 1 मिलियन वैक्टर
384 ~1.5 केबी ~ 1.5 जीबी
768 ~3 केबी ~3 जीबी
1536 ~6 केबी ~6 जीबी
3072 ~12 केबी ~12 जीबी

1536-आयामी एम्बेडिंग का उपयोग करके दो मिलियन आइटम वाले उत्पाद कैटलॉग के लिए, अकेले वेक्टर कॉलम को लगभग 12 जीबी स्टोरेज की आवश्यकता होती है। HNSW इंडेक्स जोड़ने से यह लगभग 50%बढ़ जाता है।

कई एम्बेडिंग मॉडल कई आयाम विकल्प प्रदान करते हैं। कम आयाम भंडारण और गणना लागत को कम करते हैं जबकि कई उपयोग के मामलों के लिए उचित गुणवत्ता बनाए रखते हैं। कॉलम परिभाषा में आयामों को निर्दिष्ट करना सत्यापन प्रदान करता है। विभिन्न आयामों के साथ वैक्टर सम्मिलित करने का प्रयास एक त्रुटि के साथ विफल हो जाता है, जिससे सूक्ष्म बग को बेमेल एम्बेडिंग मॉडल से रोका जा सकता है। स्तंभ परिभाषा में उपयोग करके एक embedding vector(768) स्पष्ट आयाम बाधा के साथ अपनी तालिका को परिभाषित करें.

कुछ अनुप्रयोगों को विभिन्न मॉडलों से वैक्टर की आवश्यकता होती है। उदाहरण के लिए, आप उत्पाद शीर्षक एम्बेडिंग, छवि एम्बेडिंग और उपयोगकर्ता व्यवहार एम्बेडिंग को अलग-अलग संग्रहीत कर सकते हैं. प्रत्येक वेक्टर स्तंभ को अपनी स्वयं की अनुक्रमणिका की आवश्यकता होती है क्योंकि आप एक एकल अनुक्रमणिका नहीं बना सकते हैं जो कई वेक्टर स्तंभों को कवर करता है.

CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    title_embedding vector(768),      -- Text embedding model
    image_embedding vector(512),       -- Image embedding model
    category_id INTEGER,
    price NUMERIC(10,2)
);

-- Create separate indexes for each embedding type
CREATE INDEX ON products USING hnsw (title_embedding vector_cosine_ops);
CREATE INDEX ON products USING hnsw (image_embedding vector_cosine_ops);

मेटाडेटा डेटा प्रकार: संरचित कॉलम बनाम JSONB

उत्पाद अनुशंसाएँ शायद ही कभी अकेले वेक्टर समानता का उपयोग करती हैं। क्वेरी आमतौर पर वेक्टर खोज से पहले या उसके साथ श्रेणी, मूल्य सीमा, उपलब्धता या अन्य विशेषताओं के आधार पर फ़िल्टर की जाती हैं. आप इस मेटाडेटा को कैसे संग्रहीत करते हैं, क्वेरी प्रदर्शन को प्रभावित करता है।

संरचित कॉलम स्पष्ट स्कीमा के साथ PostgreSQL के मूल डेटा प्रकारों (पूर्णांक, टाइमस्टैम्प, न्यूमेरिक, टेक्स्ट) का उपयोग करते हैं। ये कॉलम क्वेरी प्रदर्शन लाभ प्रदान करते हैं क्योंकि मूल प्रकार समानता और श्रेणी क्वेरीज़ के लिए कुशल बी-ट्री अनुक्रमणिका, अनुकूलित भंडारण स्वरूपों के माध्यम से भंडारण दक्षता, सम्मिलित समय सत्यापन के माध्यम से प्रकार सुरक्षा और सटीक योजनाकार आँकड़ों के माध्यम से क्वेरी अनुकूलन सक्षम करते हैं। संरचित स्तंभों का उपयोग तब करें जब डिज़ाइन समय पर विशेषताएँ ज्ञात होती हैं, आप अक्सर विशिष्ट विशेषताओं के आधार पर फ़िल्टर या सॉर्ट करते हैं, या क्वेरी प्रदर्शन महत्वपूर्ण होता है.

CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    embedding vector(1536),
    category_id INTEGER NOT NULL,
    price NUMERIC(10,2) NOT NULL,
    in_stock BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    brand TEXT,
    rating NUMERIC(2,1)
);

JSONB अर्ध-संरचित डेटा को बाइनरी JSON के रूप में संग्रहीत करता है, जो गतिशील विशेषताओं के लिए लचीलापन प्रदान करता है। JSONB स्कीमा लचीलापन (विभिन्न उत्पादों में अलग-अलग विशेषताएँ हो सकती हैं), आसान विकास (स्कीमा माइग्रेशन के बिना नई विशेषताएँ जोड़ें), और नेस्टेड संरचनाएँ (जटिल पदानुक्रमित डेटा संग्रहीत करें) प्रदान करता है। हालाँकि, JSONB क्वेरी ओवरहेड (मान निकालने के लिए पार्सिंग की आवश्यकता होती है), अनुक्रमणिका सीमाएँ (GIN अनुक्रमणिका रोकथाम क्वेरीज़ के लिए काम करते हैं लेकिन श्रेणी क्वेरीज़ नहीं), और योजनाकार अनिश्चितता (आँकड़े JSONB सामग्री के लिए कम सटीक हैं)।

फ़िल्टर की गई वेक्टर खोजों के लिए, मेटाडेटा फ़िल्टर का प्रदर्शन सीधे कुल क्वेरी समय को प्रभावित करता है. बी-ट्री इंडेक्स के साथ संरचित कॉलम PostgreSQL को वेक्टर दूरी गणना से पहले उम्मीदवारों को जल्दी से संकीर्ण करने में सक्षम बनाते हैं, जबकि JSONB को अलग-अलग क्वेरी पैटर्न और इंडेक्स प्रकारों की आवश्यकता होती है। एक संरचित कॉलम फ़िल्टर जैसे उम्मीदवार WHERE category_id = 5 AND price BETWEEN 100 AND 500 पंक्तियों को जल्दी से पहचानने के लिए बी-ट्री इंडेक्स (category_id, price) का उपयोग कर सकता है। एक JSONB फ़िल्टर की तरह या WHERE attributes @> '{"category": "electronics"}' AND (attributes->>'price')::numeric BETWEEN 100 AND 500 तो एक GIN इंडेक्स (जो कीमत पर रेंज क्वेरी में मदद नहीं करता है) या JSONB कॉलम के अनुक्रमिक स्कैन की आवश्यकता होती है।

कई अनुप्रयोग संरचित स्तंभों और JSONB के संयोजन से लाभान्वित होते हैं: अक्सर फ़िल्टर की गई विशेषताओं के लिए संरचित स्तंभों का उपयोग करें जहाँ क्वेरी प्रदर्शन मायने रखता है, और JSONB गतिशील या शायद ही कभी फ़िल्टर की गई विशेषताओं के लिए जहाँ स्कीमा लचीलापन अधिक मूल्यवान है। यह पैटर्न आपको किनारे के मामलों के लिए लचीलेपन का त्याग किए बिना सामान्य मामले को अनुकूलित करने देता है।

CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    embedding vector(1536),
    -- Structured columns for common filters
    category_id INTEGER NOT NULL,
    price NUMERIC(10,2) NOT NULL,
    in_stock BOOLEAN DEFAULT true,
    -- JSONB for dynamic attributes
    attributes JSONB DEFAULT '{}'
);

फ़िल्टर की गई खोजों के लिए मेटाडेटा इंडेक्स

मेटाडेटा इंडेक्स क्वेरी के फ़िल्टरिंग चरण को तेज करके वेक्टर इंडेक्स को पूरक करते हैं। उचित मेटाडेटा इंडेक्स के बिना, PostgreSQL को वेक्टर खोज से पहले फ़िल्टर लागू करने के लिए सभी पंक्तियों को स्कैन करने की आवश्यकता हो सकती है।

WHERE क्लॉज में उपयोग किए जाने वाले कॉलम पर बी-ट्री इंडेक्स बनाएं। एकल-स्तंभ अनुक्रमणिका सटीक मिलान संभालती हैं, जबकि समग्र अनुक्रमणिका फ़िल्टर संयोजनों को संभालती हैं। समग्र अनुक्रमणिका सबसे प्रभावी होती हैं जब क्वेरीज़ सबसे बाईं ओर के स्तंभों पर फ़िल्टर होती हैं। एक सूचकांक कुशलता से (category_id, price) संभालता है WHERE category_id = 5 और WHERE category_id = 5 AND price < 100, लेकिन यह अकेले मदद WHERE price < 100 नहीं करता है क्योंकि कीमत सबसे बाएं कॉलम नहीं है।

-- Single-column index for exact matches
CREATE INDEX idx_products_category ON products (category_id);

-- Composite index for common filter combinations
CREATE INDEX idx_products_category_price ON products (category_id, price);

यदि अधिकांश क्वेरीज़ एक ही स्थिति (जैसे इन-स्टॉक उत्पाद) पर फ़िल्टर होती हैं, तो आंशिक अनुक्रमणिका अनुक्रमणिका आकार को कम करता है और प्रदर्शन में सुधार करता है। यह अनुक्रमणिका पूर्ण अनुक्रमणिका से छोटी है और इसका उपयोग केवल उन प्रश्नों के लिए किया जाता है जिनमें शामिल हैं WHERE in_stock = true. एक ई-कॉमर्स अनुशंसा इंजन के लिए जहां लगभग सभी प्रश्न उपलब्ध उत्पादों को लक्षित करते हैं, यह सूचकांक रखरखाव ओवरहेड को काफी कम कर सकता है। के साथ CREATE INDEX idx_products_instock_category ON products (category_id) WHERE in_stock = true;एक आंशिक अनुक्रमणिका बनाएं।

यदि आप विशेषताओं के लिए JSONB का उपयोग करते हैं, तो GIN अनुक्रमणिका (इसमें शामिल हैं), ( @> द्वारा निहित), <@ (कुंजी मौजूद है), और (??|/कोई भी/सभी कुंजी मौजूद) ऑपरेटरों का उपयोग करके ?& रोकथाम क्वेरी का समर्थन करते हैं। वे रेंज क्वेरीज़ या मनमाने JSON पथ अभिव्यक्तियों को तेज नहीं करते हैं। के साथ CREATE INDEX idx_products_attributes ON products USING gin (attributes);एक GIN अनुक्रमणिका बनाएं। अक्सर पूछे जाने वाले JSONB फ़ील्ड के लिए जिन्हें रेंज क्वेरी की आवश्यकता होती है, अभिव्यक्ति अनुक्रमणिका पर विचार करें। उस फ़ील्ड पर श्रेणी क्वेरीज़ को सक्षम करने के लिए संख्यात्मक CREATE INDEX idx_products_json_price ON products (((attributes->>'price')::numeric)); के रूप में निकाले गए JSONB फ़ील्ड पर एक अभिव्यक्ति अनुक्रमणिका बनाएँ।

मेटाडेटा फ़िल्टर के साथ वेक्टर खोज को संयोजित करें

PostgreSQL फ़िल्टरिंग के साथ इंडेक्स स्कैन को जोड़कर प्रश्नों को निष्पादित करता है। निष्पादन पैटर्न को समझने से आपको कुशल प्रश्न लिखने में मदद मिलती है।

सबसे कुशल पैटर्न पहले मेटाडेटा फ़िल्टर लागू करता है, वैक्टर के सेट को कम करता है जिन्हें समानता गणना की आवश्यकता होती है। PostgreSQL फ़िल्टर से मेल खाने वाले उत्पादों की पहचान करने के लिए मेटाडेटा इंडेक्स का उपयोग करता है, फिर केवल उन उम्मीदवारों पर वेक्टर समानता लागू करता है। यदि 5% उत्पाद फ़िल्टर से मेल खाते हैं, तो आप 2 मिलियन के बजाय 100,000 वैक्टर खोज रहे हैं।

-- Efficient: filter narrows candidates before vector search
SELECT id, name, embedding <=> $1 AS distance
FROM products
WHERE category_id = 5
  AND in_stock = true
  AND price BETWEEN 100 AND 500
ORDER BY embedding <=> $1
LIMIT 10;

यह सत्यापित करने के लिए उपयोग करें EXPLAIN ANALYZE कि क्वेरीज़ अपेक्षित अनुक्रमणिका का उपयोग करें और प्रदर्शन बाधाओं की पहचान करने के लिए। क्वेरी योजना से पता चलता है कि क्या PostgreSQL वेक्टर खोज से पहले उम्मीदवारों को फ़िल्टर करने के लिए आपके मेटाडेटा इंडेक्स का उपयोग करता है, या क्या यह अनुक्रमिक स्कैन का सहारा लेता है जो हर पंक्ति की जांच करता है। मेटाडेटा कॉलम (कुशल) पर इंडेक्स स्कैन या बिटमैप इंडेक्स स्कैन देखें, वेक्टर इंडेक्स (कुशल) का उपयोग करके इंडेक्स स्कैन करें, और बड़ी तालिकाओं पर Seq स्कैन (संभावित रूप से अक्षम)। यदि आप अनपेक्षित अनुक्रमिक स्कैन देखते हैं, तो जाँच लें कि उपयुक्त अनुक्रमणिका मौजूद हैं और आँकड़े वर्तमान का उपयोग कर रहे ANALYZE products;हैं.

कुछ प्रश्न कुशल फ़िल्टरिंग के लिए खुद को उधार नहीं देते हैं। जब फ़िल्टर कई पंक्तियों को समाप्त नहीं करते हैं (जैसे कि WHERE price > 0 लगभग सभी उत्पाद मेल खाते हैं), तो PostgreSQL मेटाडेटा इंडेक्स को पूरी तरह से छोड़ सकता है और अकेले वेक्टर इंडेक्स पर भरोसा कर सकता है। यह अपेक्षित व्यवहार है क्योंकि अनुकूलक लागत-आधारित निर्णय लेता है।

कभी-कभी आपको वेक्टर इंडेक्स से परिणामों की आवश्यकता होती है जो पहले से कुशलता से फ़िल्टर करने योग्य बाधाओं को भी पूरा करते हैं। पोस्ट-फ़िल्टरिंग पैटर्न आवश्यकता से अधिक वेक्टर-समान उम्मीदवारों को प्राप्त करता है, फिर फ़िल्टर लागू करता है। अपेक्षित फ़िल्टर चयनात्मकता के आधार पर आंतरिक सीमा को समायोजित करें।

-- Get more candidates than needed, then filter
WITH candidates AS (
    SELECT id, name, price, in_stock, embedding <=> $1 AS distance
    FROM products
    ORDER BY embedding <=> $1
    LIMIT 100
)
SELECT id, name, distance
FROM candidates
WHERE in_stock = true AND price BETWEEN 100 AND 500
ORDER BY distance
LIMIT 10;

बड़े डेटासेट के लिए तालिका विभाजन

विभाजन एक बड़ी तालिका को छोटे, अधिक प्रबंधनीय टुकड़ों में विभाजित करता है। वेक्टर कार्यभार के लिए, विभाजन क्वेरी प्रदर्शन में सुधार कर सकता है और रखरखाव को सरल बना सकता है।

विभाजन पर विचार करें जब तालिकाओं की दसियों लाख पंक्तियों से अधिक हो जाती है, प्रश्न स्वाभाविक रूप से विभाजन कुंजी (दिनांक, श्रेणी, किरायेदार) द्वारा फ़िल्टर करते हैं, आपको पुराने डेटा (विभाजन छंटाई) को कुशलतापूर्वक छोड़ने की आवश्यकता होती है, या अनुक्रमणिका निर्माण समय पूर्ण तालिका पर निषेधात्मक हो जाता है।

समय-श्रृंखला एम्बेडिंग (जैसे उपयोगकर्ता गतिविधि वैक्टर या समय के साथ प्रकाशित सामग्री) को संसाधित करने वाले अनुप्रयोगों के लिए, दिनांक के अनुसार श्रेणी विभाजन प्रभावी है। दिनांक के अनुसार फ़िल्टर करने वाले क्वेरीज़ केवल प्रासंगिक विभाजन को स्कैन करते हैं। प्रत्येक विभाजन के अपने सूचकांक होते हैं, जिससे रखरखाव अधिक प्रबंधनीय हो जाता है।

-- Create partitioned table
CREATE TABLE user_interactions (
    id BIGSERIAL,
    user_id BIGINT NOT NULL,
    embedding vector(768),
    created_at TIMESTAMP NOT NULL,
    interaction_type TEXT
) PARTITION BY RANGE (created_at);

-- Create partitions for each month
CREATE TABLE user_interactions_2025_01
    PARTITION OF user_interactions
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

मल्टीटेनेंट अनुप्रयोगों या प्राकृतिक श्रेणियों के साथ उत्पाद कैटलॉग के लिए, सूची या हैश विभाजन मदद कर सकते हैं। श्रेणी द्वारा फ़िल्टर किए गए प्रश्न केवल प्रासंगिक विभाजन को स्कैन करते हैं, स्कैन किए गए डेटा और अनुक्रमणिका आकार दोनों को कम करते हैं।

का उपयोग करके CREATE INDEX ON products USING hnsw (embedding vector_cosine_ops);सभी विभाजनों पर स्वचालित रूप से मिलान अनुक्रमणिका बनाने के लिए पैरेंट तालिका पर अनुक्रमणिका बनाएं। प्रत्येक विभाजन का अपना सूचकांक होता है, जिसे स्वतंत्र रूप से बनाया या फिर से बनाया जा सकता है। यह बड़े डेटासेट के लिए मूल्यवान है जहां एकल वैश्विक सूचकांक के पुनर्निर्माण में घंटों लगेंगे।

विभाजन जटिलता जोड़ता है। कई विभाजन वाले प्रश्न एक ही टेबल की तुलना में धीमे हो सकते हैं। क्रॉस-पार्टीशन अद्वितीय बाधाओं के लिए बाधा में विभाजन कुंजी की आवश्यकता होती है। अनुप्रयोग तर्क को विभाजन सीमाओं के बारे में जागरूकता की आवश्यकता हो सकती है। मूल्यांकन करें कि विभाजन को लागू करने से पहले आपके क्वेरी प्रतिमान संभावित विभाजन कुंजियों के साथ संरेखित हैं या नहीं।

अतिरिक्त संसाधन