पोस्टग्रेज की दुनिया में, टेबलडेटा स्टोरेज (उर्फ "हीप") को कुशलतापूर्वक नेविगेट करने के लिए इंडेक्स आवश्यक हैं। पोस्टग्रेज ढेर के लिए क्लस्टरिंग को बनाए नहीं रखता है, और एमवीसीसी आर्किटेक्चर एक ही टुपलीइंग के कई संस्करणों की ओर जाता है। अनुप्रयोगों का समर्थन करने के लिए प्रभावी और कुशल अनुक्रमणिका बनाना और बनाए रखना एक आवश्यक कौशल है।
अपने परिनियोजन में अनुक्रमणिका के उपयोग को अनुकूलित करने और सुधारने के बारे में कुछ युक्तियों को देखने के लिए आगे पढ़ें।
नोट:नीचे दिखाई गई क्वेरी एक अनमॉडिफाइड पेजिला नमूना डेटाबेस पर चलाई जाती हैं।
कवरिंग इंडेक्स का उपयोग करें
सभी निष्क्रिय ग्राहकों के ईमेल लाने के लिए एक प्रश्न पर विचार करें। ग्राहक तालिका में एक सक्रिय है कॉलम, और क्वेरी सीधी है:
pagila=# EXPLAIN SELECT email FROM customer WHERE active=0;
QUERY PLAN
-----------------------------------------------------------
Seq Scan on customer (cost=0.00..16.49 rows=15 width=32)
Filter: (active = 0)
(2 rows)
क्वेरी ग्राहक तालिका के पूर्ण अनुक्रमिक स्कैन के लिए कहती है। आइए सक्रिय कॉलम पर एक इंडेक्स बनाएं:
pagila=# CREATE INDEX idx_cust1 ON customer(active);
CREATE INDEX
pagila=# EXPLAIN SELECT email FROM customer WHERE active=0;
QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using idx_cust1 on customer (cost=0.28..12.29 rows=15 width=32)
Index Cond: (active = 0)
(2 rows)
यह मदद करता है, और अनुक्रमिक स्कैन एक "इंडेक्स स्कैन" बन गया है। इसका मतलब है कि पोस्टग्रेस "idx_cust1" इंडेक्स को स्कैन करेगा, और फिर अन्य कॉलम वैल्यू (इस मामले में, ईमेल) को पढ़ने के लिए टेबल के शीप को और खोजेगा। कॉलम) जिसे क्वेरी की आवश्यकता है।
PostgreSQL 11 ने कवरिंग इंडेक्स पेश किया। यह सुविधा आपको इंडेक्स में ही एक या अधिक अतिरिक्त कॉलम शामिल करने की अनुमति देती है - यानी, इन अतिरिक्त कॉलम के मान इंडेक्स डेटा स्टोरेज में संग्रहीत होते हैं।
यदि हम इस सुविधा का उपयोग करते हैं और सूचकांक के अंदर ईमेल के मूल्य को शामिल करते हैं, तो पोस्टग्रेज को ईमेल का मूल्य प्राप्त करने के लिए तालिका के ढेर में देखने की आवश्यकता नहीं होगी . देखते हैं कि क्या यह काम करता है:
pagila=# CREATE INDEX idx_cust2 ON customer(active) INCLUDE (email);
CREATE INDEX
pagila=# EXPLAIN SELECT email FROM customer WHERE active=0;
QUERY PLAN
----------------------------------------------------------------------------------
Index Only Scan using idx_cust2 on customer (cost=0.28..12.29 rows=15 width=32)
Index Cond: (active = 0)
(2 rows)
"इंडेक्स ओनली स्कैन" हमें बताता है कि क्वेरी अब इंडेक्स द्वारा ही पूरी तरह से संतुष्ट है, इस प्रकार संभावित रूप से टेबल के ढेर को पढ़ने के लिए सभी डिस्क I/O से बचा जा सकता है।
कवरिंग इंडेक्स अभी केवल बी-ट्री इंडेक्स के लिए उपलब्ध हैं। साथ ही, एक कवरिंग इंडेक्स को बनाए रखने की लागत स्वाभाविक रूप से एक नियमित इंडेक्स की तुलना में अधिक होती है।
आंशिक इंडेक्स का उपयोग करें
आंशिक अनुक्रमणिका केवल तालिका में पंक्तियों के सबसेट को अनुक्रमित करती है। यह इंडेक्स को आकार में छोटा और स्कैन करने में तेज़ रखता है।
मान लें कि हमें कैलिफ़ोर्निया में स्थित ग्राहकों के ईमेल की सूची प्राप्त करने की आवश्यकता है। पूछताछ है:
SELECT c.email FROM customer c
JOIN address a ON c.address_id = a.address_id
WHERE a.district = 'California';
जिसमें एक क्वेरी योजना है जिसमें शामिल होने वाली दोनों तालिकाओं को स्कैन करना शामिल है:
pagila=# EXPLAIN SELECT c.email FROM customer c
pagila-# JOIN address a ON c.address_id = a.address_id
pagila-# WHERE a.district = 'California';
QUERY PLAN
----------------------------------------------------------------------
Hash Join (cost=15.65..32.22 rows=9 width=32)
Hash Cond: (c.address_id = a.address_id)
-> Seq Scan on customer c (cost=0.00..14.99 rows=599 width=34)
-> Hash (cost=15.54..15.54 rows=9 width=4)
-> Seq Scan on address a (cost=0.00..15.54 rows=9 width=4)
Filter: (district = 'California'::text)
(6 rows)
आइए देखें कि एक नियमित सूचकांक हमें क्या देता है:
pagila=# CREATE INDEX idx_address1 ON address(district);
CREATE INDEX
pagila=# EXPLAIN SELECT c.email FROM customer c
pagila-# JOIN address a ON c.address_id = a.address_id
pagila-# WHERE a.district = 'California';
QUERY PLAN
---------------------------------------------------------------------------------------
Hash Join (cost=12.98..29.55 rows=9 width=32)
Hash Cond: (c.address_id = a.address_id)
-> Seq Scan on customer c (cost=0.00..14.99 rows=599 width=34)
-> Hash (cost=12.87..12.87 rows=9 width=4)
-> Bitmap Heap Scan on address a (cost=4.34..12.87 rows=9 width=4)
Recheck Cond: (district = 'California'::text)
-> Bitmap Index Scan on idx_address1 (cost=0.00..4.34 rows=9 width=0)
Index Cond: (district = 'California'::text)
(8 rows)
पता . का स्कैन idx_address1 . पर एक इंडेक्स स्कैन से बदल दिया गया है , और पते के ढेर का स्कैन।
यह मानते हुए कि यह एक बार-बार की जाने वाली क्वेरी है और इसे अनुकूलित करने की आवश्यकता है, हम अपार्टियल इंडेक्स का उपयोग कर सकते हैं जो केवल पते की उन पंक्तियों को अनुक्रमित करता है जहां जिला 'कैलिफ़ोर्निया' है:
pagila=# CREATE INDEX idx_address2 ON address(address_id) WHERE district='California';
CREATE INDEX
pagila=# EXPLAIN SELECT c.email FROM customer c
pagila-# JOIN address a ON c.address_id = a.address_id
pagila-# WHERE a.district = 'California';
QUERY PLAN
------------------------------------------------------------------------------------------------
Hash Join (cost=12.38..28.96 rows=9 width=32)
Hash Cond: (c.address_id = a.address_id)
-> Seq Scan on customer c (cost=0.00..14.99 rows=599 width=34)
-> Hash (cost=12.27..12.27 rows=9 width=4)
-> Index Only Scan using idx_address2 on address a (cost=0.14..12.27 rows=9 width=4)
(5 rows)
क्वेरी अब केवल अनुक्रमणिका पढ़ती है idx_address2 और तालिका को नहीं छूतापता ।
मल्टी-वैल्यू इंडेक्स का उपयोग करें
कुछ कॉलम जिन्हें अनुक्रमणित करने की आवश्यकता होती है उनमें स्केलर डेटा प्रकार नहीं हो सकता है। कॉलम प्रकार जैसे jsonb , सरणी और tsvector समग्र या एकाधिक मान हैं। यदि आपको ऐसे स्तंभों को अनुक्रमित करने की आवश्यकता है, तो आमतौर पर ऐसा होता है कि आपको उन स्तंभों में अलग-अलग मानों के माध्यम से भी खोज करने की आवश्यकता होती है।
आइए उन सभी फ़िल्मों के शीर्षक ढूँढ़ने का प्रयास करें जिनमें परदे के पीछे की फ़िल्में शामिल हैं। दफ़िल्म तालिका में special_features . नामक टेक्स्ट सरणी कॉलम है , जिसमें टेक्स्ट सरणी तत्व शामिल है पर्दे के पीछे अगर किसी फिल्म में वह सुविधा है। ऐसी सभी फिल्मों को खोजने के लिए, हमें उन सभी पंक्तियों का चयन करना होगा जिनमें "पर्दे के पीछे"किसी भी में हो सरणी के मानों की विशेष_सुविधाएं :
SELECT title FROM film WHERE special_features @> '{"Behind The Scenes"}';
नियंत्रण ऑपरेटर @> जाँचता है कि क्या बायाँ हाथ दाईं ओर का सुपरसेट है।
यहाँ क्वेरी योजना है:
pagila=# EXPLAIN SELECT title FROM film
pagila-# WHERE special_features @> '{"Behind The Scenes"}';
QUERY PLAN
-----------------------------------------------------------------
Seq Scan on film (cost=0.00..67.50 rows=5 width=15)
Filter: (special_features @> '{"Behind The Scenes"}'::text[])
(2 rows)
जो 67 की कीमत पर ढेर के पूर्ण स्कैन की मांग करता है।
आइए देखें कि क्या नियमित बी-ट्री इंडेक्स मदद करता है:
pagila=# CREATE INDEX idx_film1 ON film(special_features);
CREATE INDEX
pagila=# EXPLAIN SELECT title FROM film
pagila-# WHERE special_features @> '{"Behind The Scenes"}';
QUERY PLAN
-----------------------------------------------------------------
Seq Scan on film (cost=0.00..67.50 rows=5 width=15)
Filter: (special_features @> '{"Behind The Scenes"}'::text[])
(2 rows)
सूचकांक पर भी विचार नहीं किया जाता है। बी-ट्री इंडेक्स को इस बात का अंदाजा नहीं है कि उसके द्वारा अनुक्रमित मूल्य में अलग-अलग तत्व हैं।
हमें GIN इंडेक्स की आवश्यकता है।
pagila=# CREATE INDEX idx_film2 ON film USING GIN(special_features);
CREATE INDEX
pagila=# EXPLAIN SELECT title FROM film
pagila-# WHERE special_features @> '{"Behind The Scenes"}';
QUERY PLAN
---------------------------------------------------------------------------
Bitmap Heap Scan on film (cost=8.04..23.58 rows=5 width=15)
Recheck Cond: (special_features @> '{"Behind The Scenes"}'::text[])
-> Bitmap Index Scan on idx_film2 (cost=0.00..8.04 rows=5 width=0)
Index Cond: (special_features @> '{"Behind The Scenes"}'::text[])
(4 rows)
जीआईएन इंडेक्स इंडेक्स किए गए कंपोजिट वैल्यू के खिलाफ व्यक्तिगत मूल्य के मिलान का समर्थन करने में सक्षम है, जिसके परिणामस्वरूप एक क्वेरी प्लान मूल की लागत के आधे से भी कम है।
डुप्लिकेट इंडेक्स को हटा दें
समय के साथ अनुक्रमित जमा होते हैं, और कभी-कभी एक जोड़ा जाता है जिसकी सटीक परिभाषा दूसरे के समान होती है। आप कैटलॉग व्यू का उपयोग कर सकते हैं pg_indexes
अनुक्रमणिका की मानव-पठनीय SQL परिभाषाएँ प्राप्त करें। आप समान परिभाषाओं का भी आसानी से पता लगा सकते हैं:
SELECT array_agg(indexname) AS indexes, replace(indexdef, indexname, '') AS defn
FROM pg_indexes
GROUP BY defn
HAVING count(*) > 1;
और स्टॉक पेजिला डेटाबेस पर चलने पर यह परिणाम है:
pagila=# SELECT array_agg(indexname) AS indexes, replace(indexdef, indexname, '') AS defn
pagila-# FROM pg_indexes
pagila-# GROUP BY defn
pagila-# HAVING count(*) > 1;
indexes | defn
------------------------------------------------------------------------+------------------------------------------------------------------
{payment_p2017_01_customer_id_idx,idx_fk_payment_p2017_01_customer_id} | CREATE INDEX ON public.payment_p2017_01 USING btree (customer_id
{payment_p2017_02_customer_id_idx,idx_fk_payment_p2017_02_customer_id} | CREATE INDEX ON public.payment_p2017_02 USING btree (customer_id
{payment_p2017_03_customer_id_idx,idx_fk_payment_p2017_03_customer_id} | CREATE INDEX ON public.payment_p2017_03 USING btree (customer_id
{idx_fk_payment_p2017_04_customer_id,payment_p2017_04_customer_id_idx} | CREATE INDEX ON public.payment_p2017_04 USING btree (customer_id
{payment_p2017_05_customer_id_idx,idx_fk_payment_p2017_05_customer_id} | CREATE INDEX ON public.payment_p2017_05 USING btree (customer_id
{idx_fk_payment_p2017_06_customer_id,payment_p2017_06_customer_id_idx} | CREATE INDEX ON public.payment_p2017_06 USING btree (customer_id
(6 rows)
सुपरसेट इंडेक्स
यह भी संभव है कि आप कई इंडेक्स के साथ समाप्त हो जाएं जहां एक इंडेक्स कॉलम के सुपरसेट को इंडेक्स करता है जो दूसरा करता है। यह वांछनीय हो सकता है या नहीं भी हो सकता है - सुपरसेट एक के परिणामस्वरूप केवल-इंडेक्स-स्कैन हो सकता है जो एक अच्छी बात है, लेकिन बहुत अधिक स्थान ले सकता है, या हो सकता है कि जिस क्वेरी को मूल रूप से अनुकूलित करने का इरादा था वह अब उपयोग नहीं किया जाता है।
यदि आप ऐसी अनुक्रमणिका की पहचान को स्वचालित करना चाहते हैं, तो pg_catalog तालिकाpg_index एक अच्छा प्रारंभिक बिंदु है।
अप्रयुक्त अनुक्रमणिका
जैसे-जैसे डेटाबेस का उपयोग करने वाले अनुप्रयोग विकसित होते हैं, वैसे ही उनके द्वारा उपयोग किए जाने वाले प्रश्न भी विकसित होते हैं। इंडेक्स जो पहले जोड़े गए थे अब किसी भी क्वेरी द्वारा उपयोग नहीं किए जा सकते हैं। हर बार एक इंडेक्स स्कैन किया जाता है, यह सांख्यिकी प्रबंधक द्वारा नोट किया जाता है और सिस्टम कैटलॉग व्यू में संचयी गणना उपलब्ध होती है pg_stat_user_indexes
मान के रूप में idx_scan
. इस मान की समय-समय पर निगरानी करना (जैसे, एक महीने) एक अच्छा विचार देता है कि कौन से इंडेक्स अप्रयुक्त हैं और जिन्हें हटाया जा सकता है।
यहाँ 'पब्लिक'स्कीमा में सभी इंडेक्स के लिए वर्तमान स्कैन काउंट प्राप्त करने के लिए क्वेरी है:
SELECT relname, indexrelname, idx_scan
FROM pg_catalog.pg_stat_user_indexes
WHERE schemaname = 'public';
इस तरह के आउटपुट के साथ:
pagila=# SELECT relname, indexrelname, idx_scan
pagila-# FROM pg_catalog.pg_stat_user_indexes
pagila-# WHERE schemaname = 'public'
pagila-# LIMIT 10;
relname | indexrelname | idx_scan
---------------+--------------------+----------
customer | customer_pkey | 32093
actor | actor_pkey | 5462
address | address_pkey | 660
category | category_pkey | 1000
city | city_pkey | 609
country | country_pkey | 604
film_actor | film_actor_pkey | 0
film_category | film_category_pkey | 0
film | film_pkey | 11043
inventory | inventory_pkey | 16048
(10 rows)
कम लॉकिंग वाले इंडेक्स फिर से बनाएं
यह असामान्य नहीं है कि अनुक्रमणिका को फिर से बनाने की आवश्यकता है। इंडेक्स भी फूला हुआ हो सकता है, और इंडेक्स को फिर से बनाने से इसे ठीक किया जा सकता है, जिससे यह तेजी से टूस्कैन हो जाता है। सूचकांक भी भ्रष्ट हो सकते हैं। इंडेक्स पैरामीटर्स को बदलने के लिए भी इंडेक्स को फिर से बनाने की आवश्यकता हो सकती है।
समानांतर अनुक्रमणिका निर्माण सक्षम करें
PostgreSQL 11 में, बी-ट्री इंडेक्स निर्माण समवर्ती है। यह सूचकांक के निर्माण में तेजी लाने के लिए कई समानांतर श्रमिकों का उपयोग कर सकता है। हालांकि, आपको यह सुनिश्चित करना होगा कि ये कॉन्फ़िगरेशन प्रविष्टियां उपयुक्त रूप से सेट की गई हैं:
SET max_parallel_workers = 32;
SET max_parallel_maintenance_workers = 16;
डिफ़ॉल्ट मान अनुचित रूप से छोटे हैं। आदर्श रूप से, ये संख्या CPU कोर की संख्या के साथ बढ़नी चाहिए। अधिक जानकारी के लिए दस्तावेज़ देखें।
पृष्ठभूमि में अनुक्रमणिका बनाएं
आप CONCURRENTLY . का उपयोग करके पृष्ठभूमि में एक अनुक्रमणिका भी बना सकते हैं इंडेक्स बनाएं . का पैरामीटर आदेश:
pagila=# CREATE INDEX CONCURRENTLY idx_address1 ON address(district);
CREATE INDEX
यह एक नियमित रूप से इंडेक्स बनाने से अलग है जिसमें इसे टेबल पर लॉक की आवश्यकता नहीं होती है, और इसलिए लॉक आउट लिखने की आवश्यकता नहीं होती है। दूसरी ओर, इसे पूरा करने में अधिक समय और संसाधन लगते हैं।