टाइमस्टैम्प की एक सीमा पर प्रश्नों का अनुकूलन (दो कॉलम)


96

मैं Ubuntu 12.04 पर PostgreSQL 9.1 का उपयोग करता हूं।

मुझे समय की सीमा के भीतर रिकॉर्ड का चयन करने की आवश्यकता है: मेरी तालिका time_limitsमें दो timestampफ़ील्ड और एक integerसंपत्ति है। मेरी वास्तविक तालिका में अतिरिक्त कॉलम हैं जो इस क्वेरी के साथ शामिल नहीं हैं।

create table (
   start_date_time timestamp,
   end_date_time timestamp, 
   id_phi integer, 
   primary key(start_date_time, end_date_time,id_phi);

इस तालिका में लगभग 2M रिकॉर्ड हैं।

निम्नलिखित की तरह क्वेरीज़ ने भारी मात्रा में समय लिया:

select * from time_limits as t 
where t.id_phi=0 
and t.start_date_time <= timestamp'2010-08-08 00:00:00'
and t.end_date_time   >= timestamp'2010-08-08 00:05:00';

इसलिए मैंने एक और सूचकांक जोड़ने की कोशिश की - पीके का विलोम:

create index idx_inversed on time_limits(id_phi, start_date_time, end_date_time);

मुझे यह आभास हुआ कि प्रदर्शन में सुधार हुआ है: तालिका के मध्य में रिकॉर्ड तक पहुंचने का समय अधिक उचित प्रतीत होता है: कहीं 40 से 90 सेकंड के बीच।

लेकिन यह अभी भी समय सीमा के बीच में मूल्यों के लिए कई दसियों सेकंड है। और तालिका के अंत को लक्षित करते समय दो बार और अधिक (कालानुक्रमिक रूप से बोलना)।

मैंने explain analyzeपहली बार इस क्वेरी प्लान को प्राप्त करने की कोशिश की :

 Bitmap Heap Scan on time_limits  (cost=4730.38..22465.32 rows=62682 width=36) (actual time=44.446..44.446 rows=0 loops=1)
   Recheck Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
   ->  Bitmap Index Scan on idx_time_limits_phi_start_end  (cost=0.00..4714.71 rows=62682 width=0) (actual time=44.437..44.437 rows=0 loops=1)
         Index Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
 Total runtime: 44.507 ms

परिणाम देखें depesz.com पर।

मैं खोज को अनुकूलित करने के लिए क्या कर सकता था? एक बार id_phiसेट होने पर आप दो टाइमस्टैम्प कॉलम को स्कैन करते हुए बिताए गए सभी समय देख सकते हैं 0। और मैं टाइमस्टैम्प पर बड़े स्कैन (60K पंक्तियों!) को नहीं समझता। क्या वे प्राथमिक कुंजी द्वारा अनुक्रमित नहीं हैं और idx_inversedमैंने जोड़ा है?

क्या मुझे टाइमस्टैम्प प्रकार से कुछ और में बदलना चाहिए?

मैंने GIST और GIN इंडेक्स के बारे में थोड़ा पढ़ा है। मैं इकट्ठा करता हूं कि वे कस्टम प्रकारों के लिए कुछ शर्तों पर अधिक कुशल हो सकते हैं। क्या यह मेरे उपयोग के मामले के लिए एक व्यवहार्य विकल्प है?


1
अच्छी तरह से यह 45s है। मुझे नहीं पता कि यह 45ms क्यों कहता है। मैं भी शिकायत शुरू नहीं होगा अगर वह 45ms के रूप में तेजी से था ... :-) शायद विश्लेषण विश्लेषण के उत्पादन में एक बग। या शायद यह प्रदर्शन करने के लिए विश्लेषण का समय है। पता नहीं। लेकिन 40/50 सेकंड मैं क्या मापता हूं।
स्टीफन रोलैंड

2
explain analyzeआउटपुट में रिपोर्ट किया गया समय सर्वर पर आवश्यक क्वेरी है । यदि आपकी क्वेरी 45 सेकंड लेती है, तो अतिरिक्त समय डेटाबेस से प्रोग्राम को क्वेरी चलाने वाले प्रोग्राम में डेटा ट्रांसफर करने में व्यतीत होता है। आखिरकार यह 62682 पंक्तियाँ हैं और यदि प्रत्येक पंक्ति बड़ी है (उदाहरण के लिए लंबी varcharया textकॉलम), तो यह ट्रांसफर टाइम को प्रभावित कर सकती है। काफी।
a_horse_with_no_name

@a_horse_with_no_name: rows=62682 rowsयोजनाकार का अनुमान है । क्वेरी 0 पंक्तियों को देती है। (actual time=44.446..44.446 rows=0 loops=1)
इरविन ब्रान्डेसटेटर

@ErwinBrandstetter: आह, सही। मैंने उसकी अनदेखी की। लेकिन फिर भी मैंने निष्पादन समय के बारे में व्याख्या विश्लेषण झूठ के उत्पादन को कभी नहीं देखा है।
a_horse_with_no_name 6

जवाबों:


162

9.1 या बाद के पोस्टग्रेज के लिए:

CREATE INDEX idx_time_limits_ts_inverse
ON time_limits (id_phi, start_date_time, end_date_time DESC);

ज्यादातर मामलों में एक इंडेक्स का क्रम क्रम शायद ही प्रासंगिक है। Postgres पीछे की ओर व्यावहारिक रूप से तेजी से स्कैन कर सकते हैं। लेकिन कई स्तंभों पर श्रेणी प्रश्नों के लिए यह एक बड़ा अंतर ला सकता है । बारीकी से संबंधित:

अपनी क्वेरी पर विचार करें:

SELECT *
FROM   time_limits
WHERE  id_phi = 0
AND    start_date_time <= '2010-08-08 00:00'
AND    end_date_time   >= '2010-08-08 00:05';

id_phiसूचकांक में पहले स्तंभ का क्रम क्रम अप्रासंगिक है। चूंकि यह समानता ( =) के लिए जाँच की जाती है , इसलिए इसे पहले आना चाहिए। आपको लगता है कि सही मिला। इस संबंधित उत्तर में और अधिक:

पोस्टग्रैज id_phi = 0बिना किसी समय के बगल में कूद सकते हैं और मिलान सूचकांक के निम्नलिखित दो स्तंभों पर विचार कर सकते हैं। इन्हें उल्टे क्रम क्रम ( <=, >=) की श्रेणी स्थितियों से समझा जाता है । मेरे सूचकांक में, योग्यता पंक्तियाँ पहले आती हैं। बी-ट्री इंडेक्स 1 के साथ सबसे तेज़ संभव तरीका होना चाहिए :

  • आप चाहते हैं start_date_time <= something: सूचकांक में सबसे पहले टाइमस्टैम्प है।
    • यदि यह अर्हता प्राप्त करता है, तो कॉलम की भी जाँच करें।
      जब तक पहली पंक्ति अर्हता प्राप्त नहीं कर लेती (सुपर फास्ट) पुनर्खरीद।
  • आप चाहते हैं end_date_time >= something: सूचकांक में नवीनतम टाइमस्टैम्प पहले है।
    • यदि यह योग्य है, तो पहले (सुपर फास्ट) नहीं होने तक पंक्तियों को जारी रखें।
      कॉलम 2 के लिए अगले मूल्य के साथ जारी रखें।

पोस्टग्रैज या तो आगे या पीछे स्कैन कर सकते हैं। जिस तरह से आपके पास इंडेक्स था, उसे पहले दो कॉलम पर मेल करने वाली सभी पंक्तियों को पढ़ना होगा और फिर तीसरे पर फ़िल्टर करना होगा। अध्याय इंडेक्स औरORDER BY मैनुअल में पढ़ना सुनिश्चित करें । यह आपके प्रश्न को अच्छी तरह से फिट करता है।

पहले दो कॉलम में कितनी पंक्तियाँ मेल खाती हैं? तालिका की समय सीमा की शुरुआत के करीब
केवल कुछ ही start_date_time। लेकिन तालिका के कालानुक्रमिक छोर पर लगभग सभी पंक्तियाँ id_phi = 0! इसलिए प्रदर्शन बाद के समय के साथ बिगड़ जाता है।

नियोजक का अनुमान

rows=62682आपके उदाहरण क्वेरी के लिए योजनाकार अनुमान लगाता है । उन में से, कोई भी योग्य नहीं ( rows=0)। यदि आप तालिका के लिए आँकड़े लक्ष्य बढ़ाते हैं तो आपको बेहतर अनुमान मिल सकता है। 2.000.000 पंक्तियों के लिए ...

ALTER TABLE time_limits ALTER start_date_time SET STATISTICS 1000;
ALTER TABLE time_limits ALTER end_date_time   SET STATISTICS 1000;

... भुगतान कर सकते हैं। या इससे भी अधिक। इस संबंधित उत्तर में और अधिक:

मुझे लगता है कि आपको इसकी आवश्यकता नहीं है id_phi(केवल कुछ विशिष्ट मूल्यों के लिए, समान रूप से वितरित), लेकिन टाइमस्टैम्प्स के लिए (बहुत सारे विशिष्ट मूल्य, असमान रूप से वितरित)।
मैं यह भी नहीं सोचता कि यह बेहतर सूचकांक के साथ बहुत मायने रखता है।

CLUSTER / pg_repack

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

ALTER TABLE time_limits CLUSTER ON idx_time_limits_inversed;

समवर्ती पहुंच के साथ, pg_repack पर विचार करें , जो अनन्य लॉक के बिना भी ऐसा कर सकता है।

किसी भी तरह से, प्रभाव यह है कि कम ब्लॉकों को तालिका से पढ़ा जाना चाहिए और सब कुछ पूर्व-सॉर्ट किया गया है। यह समय के साथ बिगड़ता हुआ एक बार का प्रभाव है, जो भौतिक क्रम क्रम को खंडित करते हुए तालिका पर लिखता है।

Postgres में GiST सूचकांक 9.2+

1 pg के साथ 9.2+ में एक और, संभवतः तेज विकल्प है: रेंज कॉलम के लिए एक जीएसटी सूचकांक।

  • अंतर्निहित रेंज प्रकार हैं timestampऔर timestamp with time zone: tsrange,tstzrange । एक btree सूचकांक आमतौर पर एक अतिरिक्त integerकॉलम के लिए तेजी से होता है जैसे id_phi। छोटे और बनाए रखने के लिए सस्ता, भी। लेकिन क्वेरी संभवतः संयुक्त सूचकांक के साथ समग्र रूप से तेज होगी।

  • अपनी तालिका परिभाषा बदलें या एक अभिव्यक्ति सूचकांक का उपयोग करें ।

  • हाथ पर बहुरंगी जीएसटी इंडेक्स के लिए आपको अतिरिक्त मॉड्यूल btree_gistस्थापित करना होगा (एक बार प्रति डेटाबेस) जो ऑपरेटर वर्गों को शामिल करने के लिए प्रदान करता है integer

ट्राइफेक्टा! एक बहुआयामी कार्यात्मक GiST सूचकांक :

CREATE EXTENSION IF NOT EXISTS btree_gist;  -- if not installed, yet

CREATE INDEX idx_time_limits_funky ON time_limits USING gist
(id_phi, tsrange(start_date_time, end_date_time, '[]'));

अपनी क्वेरी में "शामिल सीमा" ऑपरेटर@> का उपयोग करें :

SELECT *
FROM   time_limits
WHERE  id_phi = 0
AND    tsrange(start_date_time, end_date_time, '[]')
    @> tsrange('2010-08-08 00:00', '2010-08-08 00:05', '[]')

Postgres में SP-GiST सूचकांक 9.3+

एक SP-सार सूचकांक क्वेरी इस तरह के लिए भी तेजी से हो सकता है - को छोड़कर , कि मैनुअल के हवाले से :

वर्तमान में, केवल B- ट्री, GiST, GIN, और BRIN इंडेक्स प्रकार बहुकोणीय इंडेक्स का समर्थन करते हैं।

Postgres 12. में अभी भी सही है।
आपको एक spgistइंडेक्स को सिर्फ (tsrange(...))दूसरे btreeइंडेक्स के साथ जोड़ना होगा (id_phi)। अतिरिक्त उपरि के साथ, मुझे यकीन नहीं है कि यह प्रतिस्पर्धा कर सकता है।
सिर्फ एक tsrangeकॉलम के लिए बेंचमार्क के साथ संबंधित जवाब :


78
मुझे यह कम से कम केवल एक बार बताना चाहिए, कि एसओ और डीबीए पर आपके प्रत्येक उत्तर वास्तव में उच्च जोड़ा गया मूल्य / विस्तार है , और अधिकांश समय सबसे पूर्ण। बस एक बार कहने के लिए: सम्मान!
स्टीफन रोलैंड

1
मर्सी बिएन! :) तो क्या आपको तेजी से परिणाम मिले?
इरविन ब्रान्डेसटेटर

मुझे गहन रूप से अजीब क्वेरी से उत्पन्न बड़ी थोक प्रतिलिपि को खत्म करने देना है, इसलिए प्रक्रिया वास्तव में धीमी हो जाती है, इससे पहले कि मैं प्रश्न पूछूं, यह घंटों के लिए बदल रहा था। लेकिन मैंने गणना की है, और मैंने इसे सुबह तक टर्नओवर करने का फैसला किया है, यह समाप्त हो जाएगा, और नए टेबल टोमोरो को भरने के लिए तैयार है। मैंने नौकरी के दौरान आपके सूचकांक को समवर्ती बनाने की कोशिश की है, लेकिन बहुत अधिक पहुंच (मुझे लगता है) के कारण, सूचकांक का निर्माण बंद होना चाहिए। मैं आपके समाधान के साथ फिर से इसी परीक्षण समय को दोहराऊंगा। मैंने यह भी देखा है कि 9.2 /; अपग्रेड के लिए debian / ubuntu कैसे।
स्टीफन रोलैंड

2
@StephaneRolland: यह अभी भी दिलचस्प होगा कि एक्सप्लेन एनालिसिस आउटपुट को 45 मिलिसेकंड्स क्यों दिखाता है जबकि आप क्वेरी को 40 सेकंड तक देखते हैं।
a_horse_with_no_name

1
@ जॉन: पोस्टग्रैड्स एक इंडेक्स को आगे या पीछे की ओर ले जा सकते हैं, लेकिन यह एक ही स्कैन में दिशा नहीं बदल सकता है। आदर्श रूप से, आपके पास पहले (या अंतिम) प्रति नोड सभी योग्य पंक्तियाँ हैं, लेकिन सभी कॉलमों के लिए समान परिणाम प्राप्त करने के लिए एक ही संरेखण (मिलान क्वेरी की भविष्यवाणी) करनी होगी।
इरविन ब्रान्डेसटेटर

5

एर्विन का जवाब पहले से ही व्यापक है, हालांकि:

जेस्ट डेविस से टेम्पोरल एक्सटेंशन के साथ टाइमस्टैम्प के लिए रेंज प्रकार पोस्टग्रेसीक्यू 9.1 में उपलब्ध हैं: https://github.com/jeff-davis/PostgreSQL-Temporal

नोट: सीमित विशेषताएं हैं (टाइमस्टैम्प्ट का उपयोग करता है, और आप केवल '[]' शैली ओवरलैप afaik कर सकते हैं)। इसके अलावा, PostgreSQL 9.2 में अपग्रेड करने के कई अन्य महान कारण हैं।


3

आप एक अलग क्रम में बहुरंगी सूचकांक बनाने की कोशिश कर सकते हैं:

primary key(id_phi, start_date_time,end_date_time);

मैंने एक बार एक समान प्रश्न पोस्ट किया था जो कि एक बहुरंगी सूचकांक पर अनुक्रमित के क्रम से संबंधित था। कुंजी खोज स्थान को कम करने के लिए सबसे अधिक प्रतिबंधात्मक स्थितियों का उपयोग करने की कोशिश कर रही है।

संपादित करें : मेरी गलती। अब मैं देखता हूं कि आपके पास पहले से ही यह सूचकांक परिभाषित है।


मेरे पास पहले से ही दोनों सूचकांक हैं। प्राथमिक कुंजी को छोड़कर दूसरा है, लेकिन आपके द्वारा प्रस्तावित किया गया सूचकांक पहले से ही मौजूद है, और वह है जिसका उपयोग यदि आप व्याख्या को देखते हैं:Bitmap Index Scan on idx_time_limits_phi_start_end
स्टीफन रोलैंड

1

मैं तेजी से वृद्धि करने में कामयाब रहा (1 सेकंड से 70ms तक)

मेरे पास कई मापों और कई स्तरों ( lकॉलम) (30s, 1m, 1h, आदि) के एकत्रीकरण के साथ एक तालिका है दो सीमाबद्ध स्तंभ हैं: $sशुरुआत के लिए और $eअंत के लिए।

मैंने दो बहुरंगी सूचकांक बनाए: एक शुरुआत के लिए और दूसरा अंत के लिए।

मैंने चुनिंदा क्वेरी को समायोजित किया: उन श्रेणियों का चयन करें जहां उनकी शुरुआत बाध्य दी गई सीमा में है। अतिरिक्त रूप से उन श्रेणियों का चयन करें जहां उनका अंत बाउंड दी गई सीमा में है।

स्पष्ट रूप से हमारे अनुक्रमित का उपयोग करके पंक्तियों की दो धाराओं को स्पष्ट करें।

इंडेक्स:

drop index if exists agg_search_a;
CREATE INDEX agg_search_a
ON agg (measurement_id, l, "$s");

drop index if exists agg_search_b;
CREATE INDEX agg_search_b
ON agg (measurement_id, l, "$e");

क्वेरी का चयन करें:

select "$s", "$e", a, t, b, c from agg
where 
    measurement_id=0 
    and l =  '30s'
    and (
        (
            "$s" > '2013-05-01 02:05:05'
            and "$s" < '2013-05-01 02:18:15'
        )
        or 
        (
             "$e" > '2013-05-01 02:00:05'
            and "$e" < '2013-05-01 02:18:05'
        )
    )

;

के बारे में बताएं:

[
  {
    "Execution Time": 0.058,
    "Planning Time": 0.112,
    "Plan": {
      "Startup Cost": 10.18,
      "Rows Removed by Index Recheck": 0,
      "Actual Rows": 37,
      "Plans": [
    {
      "Startup Cost": 10.18,
      "Actual Rows": 0,
      "Plans": [
        {
          "Startup Cost": 0,
          "Plan Width": 0,
          "Actual Rows": 26,
          "Node Type": "Bitmap Index Scan",
          "Index Cond": "((measurement_id = 0) AND ((l)::text = '30s'::text) AND (\"$s\" > '2013-05-01 02:05:05'::timestamp without time zone) AND (\"$s\" < '2013-05-01 02:18:15'::timestamp without time zone))",
          "Plan Rows": 29,
          "Parallel Aware": false,
          "Actual Total Time": 0.016,
          "Parent Relationship": "Member",
          "Actual Startup Time": 0.016,
          "Total Cost": 5,
          "Actual Loops": 1,
          "Index Name": "agg_search_a"
        },
        {
          "Startup Cost": 0,
          "Plan Width": 0,
          "Actual Rows": 36,
          "Node Type": "Bitmap Index Scan",
          "Index Cond": "((measurement_id = 0) AND ((l)::text = '30s'::text) AND (\"$e\" > '2013-05-01 02:00:05'::timestamp without time zone) AND (\"$e\" < '2013-05-01 02:18:05'::timestamp without time zone))",
          "Plan Rows": 39,
          "Parallel Aware": false,
          "Actual Total Time": 0.011,
          "Parent Relationship": "Member",
          "Actual Startup Time": 0.011,
          "Total Cost": 5.15,
          "Actual Loops": 1,
          "Index Name": "agg_search_b"
        }
      ],
      "Node Type": "BitmapOr",
      "Plan Rows": 68,
      "Parallel Aware": false,
      "Actual Total Time": 0.027,
      "Parent Relationship": "Outer",
      "Actual Startup Time": 0.027,
      "Plan Width": 0,
      "Actual Loops": 1,
      "Total Cost": 10.18
    }
      ],
      "Exact Heap Blocks": 1,
      "Node Type": "Bitmap Heap Scan",
      "Plan Rows": 68,
      "Relation Name": "agg",
      "Alias": "agg",
      "Parallel Aware": false,
      "Actual Total Time": 0.037,
      "Recheck Cond": "(((measurement_id = 0) AND ((l)::text = '30s'::text) AND (\"$s\" > '2013-05-01 02:05:05'::timestamp without time zone) AND (\"$s\" < '2013-05-01 02:18:15'::timestamp without time zone)) OR ((measurement_id = 0) AND ((l)::text = '30s'::text) AND (\"$e\" > '2013-05-01 02:00:05'::timestamp without time zone) AND (\"$e\" < '2013-05-01 02:18:05'::timestamp without time zone)))",
      "Lossy Heap Blocks": 0,
      "Actual Startup Time": 0.033,
      "Plan Width": 44,
      "Actual Loops": 1,
      "Total Cost": 280.95
    },
    "Triggers": []
  }
]

चाल यह है कि आपकी योजना नोड्स में केवल वांछित पंक्तियाँ हैं। पहले हमें योजना नोड में हजारों पंक्तियाँ मिलीं क्योंकि यह चयनित थी all points from some point in time to the very end, फिर अगली नोड ने अप्राकृतिक पंक्तियों को हटा दिया।

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