पठन प्रदर्शन के लिए PostgreSQL को कॉन्फ़िगर करना


39

हमारा सिस्टम बहुत सारा डेटा (बिग डेटा सिस्टम का प्रकार) लिखता है। प्रदर्शन लिखना हमारी आवश्यकताओं के लिए काफी अच्छा है लेकिन पढ़ा हुआ प्रदर्शन वास्तव में बहुत धीमा है।

प्राथमिक कुंजी (बाधा) संरचना हमारे सभी तालिकाओं के लिए समान है:

timestamp(Timestamp) ; index(smallint) ; key(integer).

एक तालिका में लाखों पंक्तियाँ, यहां तक ​​कि अरबों पंक्तियाँ हो सकती हैं, और एक रीड रिक्वेस्ट आमतौर पर एक विशिष्ट अवधि (टाइमस्टैम्प / इंडेक्स) और टैग के लिए होती है। यह एक प्रश्न है जो लगभग 200k लाइनों पर लौटता है, आम है। वर्तमान में, हम प्रति सेकंड 15k लाइनों के बारे में पढ़ सकते हैं लेकिन हमें 10 गुना तेज होना चाहिए। क्या यह संभव है और यदि ऐसा हो तो कैसे?

नोट: PostgreSQL हमारे सॉफ्टवेयर के साथ पैक किया गया है, इसलिए हार्डवेयर एक क्लाइंट से दूसरे क्लाइंट में भिन्न है।

यह परीक्षण के लिए इस्तेमाल किया गया एक वीएम है। VM का होस्ट 24.0 GB RAM वाला Windows Server 2008 R2 x64 है।

सर्वर युक्ति (वर्चुअल मशीन VMWare)

Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)

postgresql.conf अनुकूलन

shared_buffers = 512MB (default: 32MB)
effective_cache_size = 1024MB (default: 128MB)
checkpoint_segment = 32 (default: 3)
checkpoint_completion_target = 0.9 (default: 0.5)
default_statistics_target = 1000 (default: 100)
work_mem = 100MB (default: 1MB)
maintainance_work_mem = 256MB (default: 16MB)

तालिका परिभाषा

CREATE TABLE "AnalogTransition"
(
  "KeyTag" integer NOT NULL,
  "Timestamp" timestamp with time zone NOT NULL,
  "TimestampQuality" smallint,
  "TimestampIndex" smallint NOT NULL,
  "Value" numeric,
  "Quality" boolean,
  "QualityFlags" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag" ),
  CONSTRAINT "FK_AnalogTransition_Tag" FOREIGN KEY ("KeyTag")
      REFERENCES "Tag" ("Key") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE,
  autovacuum_enabled=true
);

सवाल

PgAdmin3 में निष्पादित करने के लिए क्वेरी को लगभग 30 सेकंड लगते हैं, लेकिन यदि संभव हो तो हम 5 सेकंड के तहत एक ही परिणाम चाहते हैं।

SELECT 
    "AnalogTransition"."KeyTag", 
    "AnalogTransition"."Timestamp" AT TIME ZONE 'UTC', 
    "AnalogTransition"."TimestampQuality", 
    "AnalogTransition"."TimestampIndex", 
    "AnalogTransition"."Value", 
    "AnalogTransition"."Quality", 
    "AnalogTransition"."QualityFlags", 
    "AnalogTransition"."UpdateTimestamp"
FROM "AnalogTransition"
WHERE "AnalogTransition"."Timestamp" >= '2013-05-16 00:00:00.000' AND "AnalogTransition"."Timestamp" <= '2013-05-17 00:00:00.00' AND ("AnalogTransition"."KeyTag" = 56 OR "AnalogTransition"."KeyTag" = 57 OR "AnalogTransition"."KeyTag" = 58 OR "AnalogTransition"."KeyTag" = 59 OR "AnalogTransition"."KeyTag" = 60)
ORDER BY "AnalogTransition"."Timestamp" DESC, "AnalogTransition"."TimestampIndex" DESC
LIMIT 500000;

1 समझाएं

"Limit  (cost=0.00..125668.31 rows=500000 width=33) (actual time=2.193..3241.319 rows=500000 loops=1)"
"  Buffers: shared hit=190147"
"  ->  Index Scan Backward using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..389244.53 rows=1548698 width=33) (actual time=2.187..1893.283 rows=500000 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-16 01:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-16 15:00:00-04'::timestamp with time zone))"
"        Filter: (("KeyTag" = 56) OR ("KeyTag" = 57) OR ("KeyTag" = 58) OR ("KeyTag" = 59) OR ("KeyTag" = 60))"
"        Buffers: shared hit=190147"
"Total runtime: 3863.028 ms"

2 समझाएं

मेरे नवीनतम परीक्षण में, मेरे डेटा का चयन करने में 7 मिनट का समय लगा! निचे देखो:

"Limit  (cost=0.00..313554.08 rows=250001 width=35) (actual time=0.040..410721.033 rows=250001 loops=1)"
"  ->  Index Scan using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..971400.46 rows=774511 width=35) (actual time=0.037..410088.960 rows=250001 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-22 20:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-24 20:00:00-04'::timestamp with time zone) AND ("KeyTag" = 16))"
"Total runtime: 411044.175 ms"

जवाबों:


52

डेटा संरेखण और भंडारण आकार

दरअसल, टपल हेडर के लिए ओवरहेड प्रति ट्यूल हेडर के लिए 24 बाइट और आइटम पॉइंटर के लिए 4 बाइट है।
इस संबंधित उत्तर में गणना के अधिक विवरण:

SO पर इस संबंधित उत्तर में डेटा संरेखण और पैडिंग की मूल बातें:

हमारे पास प्राथमिक कुंजी के लिए तीन कॉलम हैं:

PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag")

"Timestamp"      timestamp (8 bytes)
"TimestampIndex" smallint  (2 bytes)
"KeyTag"         integer   (4 bytes)

का परिणाम:

 पेज हेडर में 4 बाइट्स आइटम पॉइंटर (8 बाइट्स के कई की ओर गिनती नहीं)
---
टपल हेडर के लिए 23 बाइट्स
 डेटा संरेखण (या पूर्ण बिटमैप) के लिए 1 बाइट पैडिंग
 8 बाइट्स "टाइमस्टैम्प"
 2 बाइट्स "टाइमस्टैम्पइंडेक्स"
 2 बाइट्स डेटा संरेखण के लिए पैडिंग
 4 बाइट्स "कीटेग" 
 0 8 बाइट्स के सबसे पास में पैडिंग
-----
44 बाइट प्रति ट्यूपल

इस संबंधित उत्तर में वस्तु के आकार को मापने के बारे में अधिक जानकारी:

एक बहुरंगी सूचकांक में स्तंभों का क्रम

समझने के लिए ये दो प्रश्न और उत्तर पढ़ें:

जिस तरह से आपके पास आपकी अनुक्रमणिका (प्राथमिक कुंजी) है, आप पंक्तियों को बिना किसी क्रमबद्ध चरण के पुनः प्राप्त कर सकते हैं, यह विशेष रूप से आकर्षक है LIMIT। लेकिन पंक्तियों को पुनः प्राप्त करना बेहद महंगा लगता है।

आमतौर पर, बहु-स्तंभ सूचकांक में, "समानता" कॉलम को पहले जाना चाहिए और "रेंज" कॉलम अंतिम होना चाहिए:

इसलिए, उलटे स्तंभ क्रम के साथ एक अतिरिक्त सूचकांक आज़माएं :

CREATE INDEX analogransition_mult_idx1
   ON "AnalogTransition" ("KeyTag", "TimestampIndex", "Timestamp");

यह डेटा वितरण पर निर्भर करता है। लेकिन इसके साथ millions of row, even billion of rowsकाफी तेजी से हो सकता है।

ट्यूल का आकार डेटा संरेखण और पैडिंग के कारण 8 बाइट्स बड़ा है। यदि आप इसे सादे सूचकांक के रूप में उपयोग कर रहे हैं, तो आप तीसरे कॉलम को छोड़ने का प्रयास कर सकते हैं "Timestamp"। थोड़ा तेज हो सकता है या नहीं (क्योंकि यह छँटाई में मदद कर सकता है)।

आप दोनों अनुक्रमित रखना चाह सकते हैं। कई कारकों के आधार पर, आपका मूल सूचकांक बेहतर हो सकता है - विशेष रूप से एक छोटे से LIMIT

ऑटोवैक्यूम और टेबल आँकड़े

आपकी तालिका के आँकड़े अद्यतित होने चाहिए। मुझे यकीन है कि आपके पास ऑटोवैक्यूम चल रहा है।

चूँकि आपकी तालिका सही क्वेरी योजना के लिए बहुत बड़ी और महत्त्वपूर्ण प्रतीत होती है, इसलिए मैं प्रासंगिक कॉलम के लिए आँकड़े लक्ष्य को बहुत बढ़ाऊँगा:

ALTER TABLE "AnalogTransition" ALTER "Timestamp" SET STATISTICS 1000;

... या अरबों पंक्तियों के साथ भी अधिक। अधिकतम 10000 है, डिफ़ॉल्ट 100 है।

सभी कॉलम WHEREया ORDER BYक्लॉस में शामिल करें । फिर चला ANALYZE

टेबल लेआउट

इस पर बने रहते हुए, यदि आप लागू करते हैं कि आपने डेटा संरेखण और पैडिंग के बारे में क्या सीखा है, तो इस अनुकूलित तालिका लेआउट को कुछ डिस्क स्थान को बचाने और प्रदर्शन को थोड़ा कम करने में मदद करनी चाहिए (पीके और fk को अनदेखा करना):

CREATE TABLE "AnalogTransition"(
  "Timestamp" timestamp with time zone NOT NULL,
  "KeyTag" integer NOT NULL,
  "TimestampIndex" smallint NOT NULL,
  "TimestampQuality" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  "QualityFlags" smallint,
  "Quality" boolean,
  "Value" numeric
);

CLUSTER / pg_repack

एक निश्चित अनुक्रमणिका का उपयोग करने वाले प्रश्नों के लिए पठन प्रदर्शन को अनुकूलित करने के लिए (जैसा कि यह आपका मूल एक या मेरा सुझाया गया विकल्प है), आप तालिका को भौतिक अनुक्रम में पुनः लिख सकते हैं। CLUSTERऐसा करता है, लेकिन यह आक्रामक है और ऑपरेशन की अवधि के लिए एक विशेष लॉक की आवश्यकता होती है। pg_repackएक अधिक परिष्कृत विकल्प है जो टेबल पर एक विशेष लॉक के बिना भी ऐसा कर सकता है।
यह बड़ी तालिकाओं के साथ काफी हद तक मदद कर सकता है, क्योंकि तालिका के बहुत कम ब्लॉकों को पढ़ना पड़ता है।

राम

आम तौर पर, 2 जीबी की भौतिक रैम अरबों पंक्तियों से जल्दी से निपटने के लिए पर्याप्त नहीं है। अधिक रैम एक लंबा रास्ता तय कर सकती है - अनुकूलित सेटिंग के साथ: स्पष्ट रूप effective_cache_sizeसे शुरू करने के लिए एक बड़ा ।


2
मैंने केवल KeyTag पर एक सरल इंडेक्स जोड़ा है और यह अब बहुत तेज़ लगता है। मैं डेटा संरेखण के बारे में आपकी सिफारिशें भी लागू करूंगा। आपका बहुत बहुत धन्यवाद!
JPelletier

9

इसलिए, मैं जिन योजनाओं को देखता हूं, उनमें से एक: आप सूचकांक या तो फूला हुआ है (फिर अंतर्निहित तालिका के साथ) या इस प्रकार की क्वेरी के लिए वास्तव में अच्छा नहीं है (मैंने ऊपर मेरी नवीनतम टिप्पणी में इसे संबोधित करने की कोशिश की)।

सूचकांक की एक पंक्ति में 14 बाइट्स डेटा (और हेडर के लिए कुछ) हैं। अब, योजना में दिए गए नंबरों से गणना करें: आपको 190147 पृष्ठों से 500,000 पंक्तियां मिलीं - इसका मतलब है कि औसतन, प्रति पृष्ठ 3 से कम उपयोगी पंक्तियां, यानी 8 केबी पृष्ठ के प्रति 37 बाइट्स। यह एक बहुत बुरा अनुपात है, है ना? चूंकि सूचकांक का पहला स्तंभ Timestampक्षेत्र है और इसे क्वेरी में एक सीमा के रूप में उपयोग किया जाता है, इसलिए योजनाकार मिलान पंक्तियों को खोजने के लिए सूचकांक का चयन कर सकता है - और करता है। लेकिन शर्तों TimestampIndexमें कोई उल्लेख नहीं किया गया है WHERE, इसलिए फ़िल्टर करना KeyTagबहुत प्रभावी नहीं है क्योंकि उन मानों को इंडेक्स पेजों में बेतरतीब ढंग से प्रकट होता है।

इसलिए, एक संभावना सूचकांक परिभाषा को बदल रही है

CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp", "KeyTag", "TimestampIndex")

(या, आपके सिस्टम के भार को देखते हुए, इस इंडेक्स को एक नया बनाएँ:

CREATE INDEX CONCURRENTLY "idx_AnalogTransition" 
    ON "AnalogTransition" ("Timestamp", "KeyTag", "TimestampIndex");
  • यह सुनिश्चित करने में थोड़ा समय लगेगा लेकिन आप इस बीच काम कर सकते हैं।)

दूसरी संभावना है कि सूचकांक पृष्ठों का एक बड़ा हिस्सा मृत पंक्तियों द्वारा कब्जा कर लिया गया है, जिसे वैक्यूम करके हटाया जा सकता है। आपने सेटिंग के साथ तालिका बनाई है autovacuum_enabled=true- लेकिन क्या आपने कभी ऑटोवैक्यूमिंग शुरू किया है? या VACUUMमैन्युअल चलाएं ?

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.