क्या PostgreSQL में प्रदर्शन के लिए विचार हानिकारक हैं?


45

निम्नलिखित डीबी डिजाइन (शुरुआत डेटाबेस डिजाइन आईएसबीएन: 0-7645-7490-6) के बारे में एक पुस्तक का एक अंश है:

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

निम्नलिखित PostgreSQL 9.5 प्रलेखन से एक अंश है:

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

दो स्रोत एक दूसरे के विपरीत लगते हैं ("विचारों के साथ डिजाइन नहीं करते हैं" बनाम "विचारों के साथ डिजाइन करते हैं")।

हालांकि, पीजी विचारों में नियम प्रणाली का उपयोग करके लागू किया जाता है। तो, संभवतः (और यह मेरा सवाल है) दृश्य के खिलाफ किसी भी फ़िल्टरिंग को दृश्य के भीतर एक फ़िल्टर के रूप में फिर से लिखा जाता है, जिसके परिणामस्वरूप अंतर्निहित तालिकाओं के खिलाफ एक एकल क्वेरी निष्पादन होता है।

क्या मेरी व्याख्या सही है और पीजी को कहां और किस दृष्टिकोण से जोड़ा गया है? या यह उन्हें अलग-अलग चलाता है, एक के बाद एक? कोई छोटा, स्व निहित, सही (संकलित), उदाहरण?


मुझे लगता है कि सवाल सही नहीं है क्योंकि दोनों स्रोत एक ही चीज के बारे में बात नहीं कर रहे हैं। पहला एक दृश्य से क्वेरी से संबंधित है और एक फ़िल्टर लागू करने के बाद: SELECT * FROM my_view WHERE my_column = 'blablabla';दूसरा जो आपके डेटा मॉडल को उपयोग करने वाले एप्लिकेशन के लिए पारदर्शी बनाने के लिए विचारों का उपयोग करने के बारे में है। पहले स्रोत आपको WHERE my_column = 'blablabla'व्यू डेफिनिशन के अंदर फ़िल्टर को शामिल करने के लिए इंगित करते हैं , जिसके परिणामस्वरूप यह एक बेहतर निष्पादन योजना है।
ईएमेज़

जवाबों:


49

किताब गलत है।

एक दृश्य से चयन है बिल्कुल के रूप में तेजी से या अंतर्निहित SQL विवरण चल के रूप में धीमी गति से - आप आसानी से उपयोग कर रहा है कि जांच कर सकते हैं explain analyze

पोस्टग्रेजुएट ऑप्टिमाइज़र (और कई अन्य आधुनिक DBMSes के लिए ऑप्टिमाइज़र) वास्तविक दृश्य विवरण में दृश्य पर विधेय को नीचे धकेलने में सक्षम होंगे - बशर्ते कि यह एक सरल कथन है (फिर से, यह प्रयोग करके सत्यापित किया जा सकता है explain analyze)।

प्रदर्शन उपजी के बारे में "खराब प्रतिष्ठा" - मुझे लगता है - जब से आप विचारों का उपयोग करते हैं और उन विचारों का निर्माण करना शुरू करते हैं जो विचारों का उपयोग करते हैं। बहुत बार ऐसे बयानों का परिणाम होता है जो एक बयान की तुलना में बहुत अधिक होता है जो बिना विचारों के हाथ से तैयार किया गया था क्योंकि कुछ मध्यवर्ती तालिकाओं की आवश्यकता नहीं होगी। लगभग सभी मामलों में ऑप्टिमाइज़र उन अनावश्यक तालिकाओं / जोड़ को हटाने या विचारों के कई स्तरों पर विधेय को नीचे धकेलने के लिए पर्याप्त स्मार्ट नहीं है (यह अन्य DBMSes के लिए भी सही है)।


3
प्रस्तावित कुछ प्रति-जवाबों को देखते हुए, आप एक साधारण कथन क्या है, इस पर थोड़ा विस्तार कर सकते हैं ।
RDFozz

क्या आप बता सकते हैं कि explain analyzeकथन का उपयोग कैसे करें ?
डस्टिन मिशेल

@DustinMichels: मैन्युअल पर एक नज़र है: postgresql.org/docs/current/use-explain.html
a_horse_with_no_name

19

आपको @a_horse ने जो समझाया उसका एक उदाहरण देने के लिए :

सूचना स्कीमा को लागू करता है, जिसमें (कभी-कभी जटिल) विचार होते हैं जो मानकीकृत रूप में DB वस्तुओं के बारे में जानकारी प्रदान करते हैं। यह सुविधाजनक और विश्वसनीय है - और सीधे पोस्टग्रेज कैटलॉग टेबल तक पहुंचने की तुलना में काफी अधिक महंगा हो सकता है।

बहुत सरल उदाहरण है,
जानकारी स्कीमा से ... तालिका के सभी दृश्यमान कॉलम प्राप्त करने के लिए :

SELECT column_name
FROM   information_schema.columns
WHERE  table_name = 'big'
AND    table_schema = 'public';

... सिस्टम कैटलॉग से:

SELECT attname
FROM   pg_catalog.pg_attribute
WHERE  attrelid = 'public.big'::regclass
AND    attnum > 0
AND    NOT attisdropped;

दोनों के लिए क्वेरी योजनाओं और निष्पादन समय की तुलना करें EXPLAIN ANALYZE

  • पहली क्वेरी दृश्य पर आधारित है information_schema.columns, जो कई तालिकाओं से जुड़ती है जिसके लिए हमें इसकी आवश्यकता नहीं है।

  • दूसरी क्वेरी केवल एक तालिका को स्कैन करती है pg_catalog.pg_attribute, इसलिए बहुत तेज है। (लेकिन पहली क्वेरी को अभी भी आम डीबी में केवल कुछ एमएस की जरूरत है।)

विवरण:


7

संपादित करें:

क्षमा याचना के साथ, मुझे अपने दावे को वापस लेने की आवश्यकता है कि स्वीकृत उत्तर हमेशा सही नहीं होता है - यह बताता है कि दृश्य हमेशा एक ही चीज़ के समान होता है जिसे उपश्रेणी के रूप में लिखा जाता है। मुझे लगता है कि यह निर्विवाद है, और मुझे लगता है कि मुझे अब पता है कि मेरे मामले में क्या हो रहा है।

मुझे अब यह भी लगता है कि मूल प्रश्न का बेहतर उत्तर है।

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

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

मेरे विंडो फ़ंक्शन की जटिलता अनावश्यक है। इसके लिए व्याख्या योजना:

SELECT DISTINCT ts.train_service_key,
            pc.assembly_key,
            count(*) OVER 
              (PARTITION BY ts.train_service_key) AS train_records
FROM staging.train_service ts
   JOIN staging.portion_consist pc 
     USING (ds_code, train_service_key)
WHERE assembly_key = '185132';

इस से बहुत कम महंगा है:

SELECT *
FROM (SELECT DISTINCT ts.train_service_key,
            pc.assembly_key,
            count(*) OVER
              (PARTITION BY ts.train_service_key) AS train_records
FROM staging.train_service ts
   JOIN staging.portion_consist pc
     USING (ds_code, train_service_key)) AS query
WHERE assembly_key = '185132';

उम्मीद है कि थोड़ा और अधिक विशिष्ट और उपयोगी है।

मेरे हाल के अनुभव (मुझे इस सवाल को खोजने के लिए) के कारण, ऊपर दिया गया स्वीकृत उत्तर सभी परिस्थितियों में सही नहीं है। मेरे पास अपेक्षाकृत सरल क्वेरी है जिसमें एक विंडो फ़ंक्शन शामिल है:

SELECT DISTINCT ts.train_service_key,
                pc.assembly_key,
                dense_rank() OVER (PARTITION BY ts.train_service_key
                ORDER BY pc.through_idx DESC, pc.first_portion ASC,
               ((CASE WHEN (NOT ts.primary_direction)
                 THEN '-1' :: INTEGER
                 ELSE 1
                 END) * pc.first_seq)) AS coach_block_idx
FROM (staging.train_service ts
JOIN staging.portion_consist pc USING (ds_code, train_service_key))

अगर मैं यह फ़िल्टर जोड़ूँ:

where assembly_key = '185132'

मेरे द्वारा समझाई गई योजना इस प्रकार है:

QUERY PLAN
Unique  (cost=11562.66..11568.77 rows=814 width=43)
  ->  Sort  (cost=11562.66..11564.70 rows=814 width=43)
    Sort Key: ts.train_service_key, (dense_rank() OVER (?))
    ->  WindowAgg  (cost=11500.92..11523.31 rows=814 width=43)
          ->  Sort  (cost=11500.92..11502.96 rows=814 width=35)
                Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
                ->  Nested Loop  (cost=20.39..11461.57 rows=814 width=35)
                      ->  Bitmap Heap Scan on portion_consist pc  (cost=19.97..3370.39 rows=973 width=38)
                            Recheck Cond: (assembly_key = '185132'::text)
                            ->  Bitmap Index Scan on portion_consist_assembly_key_index  (cost=0.00..19.72 rows=973 width=0)
                                  Index Cond: (assembly_key = '185132'::text)
                      ->  Index Scan using train_service_pk on train_service ts  (cost=0.43..8.30 rows=1 width=21)
                            Index Cond: ((ds_code = pc.ds_code) AND (train_service_key = pc.train_service_key))

यह ट्रेन सेवा तालिका पर प्राथमिक कुंजी सूचकांक और part_consist तालिका पर एक गैर-अद्वितीय सूचकांक का उपयोग कर रहा है। यह 90ms में निष्पादित होता है।

मैंने एक दृश्य बनाया (इसे यहां चिपकाना बिल्कुल स्पष्ट है लेकिन यह वस्तुतः एक दृश्य में प्रश्न है):

CREATE OR REPLACE VIEW staging.v_unit_coach_block AS
SELECT DISTINCT ts.train_service_key,
            pc.assembly_key,
            dense_rank() OVER (PARTITION BY ts.train_service_key
              ORDER BY pc.through_idx DESC, pc.first_portion ASC, (
                (CASE
              WHEN (NOT ts.primary_direction)
                THEN '-1' :: INTEGER
              ELSE 1
              END) * pc.first_seq)) AS coach_block_idx
 FROM (staging.train_service ts
  JOIN staging.portion_consist pc USING (ds_code, train_service_key))

जब मैं इस दृश्य को समान फ़िल्टर से क्वेरी करता हूं:

select * from staging.v_unit_coach_block
where assembly_key = '185132';

यह व्याख्या योजना है:

QUERY PLAN
Subquery Scan on v_unit_coach_block  (cost=494217.13..508955.10     rows=3275 width=31)
Filter: (v_unit_coach_block.assembly_key = '185132'::text)
 ->  Unique  (cost=494217.13..500767.34 rows=655021 width=43)
    ->  Sort  (cost=494217.13..495854.68 rows=655021 width=43)
          Sort Key: ts.train_service_key, pc.assembly_key, (dense_rank() OVER (?))
          ->  WindowAgg  (cost=392772.16..410785.23 rows=655021 width=43)
                ->  Sort  (cost=392772.16..394409.71 rows=655021 width=35)
                      Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
                      ->  Hash Join  (cost=89947.40..311580.26 rows=655021 width=35)
                            Hash Cond: ((pc.ds_code = ts.ds_code) AND (pc.train_service_key = ts.train_service_key))
                            ->  Seq Scan on portion_consist pc  (cost=0.00..39867.86 rows=782786 width=38)
                            ->  Hash  (cost=65935.36..65935.36 rows=1151136 width=21)
                                  ->  Seq Scan on train_service ts  (cost=0.00..65935.36 rows=1151136 width=21)

यह दोनों तालिकाओं पर पूर्ण स्कैन कर रहा है और 17s लेता है।

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

मुझे यह भी पता है कि PostgreSQL में CTE का कड़ाई से डिजाइन द्वारा अलग-अलग मूल्यांकन किया जाता है, इसलिए मैं उन्हें उसी तरह से उपयोग नहीं करता हूं जैसे मैं SQL सर्वर के साथ करता हूं, उदाहरण के लिए, जहां वे उपश्रेणी के रूप में अनुकूलित होने लगते हैं।

मेरा जवाब, इसलिए, ऐसे उदाहरण हैं, जिसमें दृश्य बिल्कुल वैसा प्रदर्शन नहीं करते हैं, जिस पर वे आधारित हैं, इसलिए सावधानी बरतने की सलाह दी जाती है। मैं PostgreSQL 9.6.6 पर आधारित Amazon Aurora का उपयोग कर रहा हूं।


2
अन्य उत्तर में चेतावनी पर ध्यान दें - " बशर्ते यह एक सरल कथन है "।
RDFozz

एक साइड नोट के रूप में, CASE WHEN (NOT ts.primary_direction) THEN '-1' :: INTEGER ELSE 1 ENDजरूरत से ज्यादा धीमी गति से क्वेरी को धीमा कर देगा ताकि आप ऑर्डर में दो और सशर्त लिखने से बेहतर हो।
इवान कैरोल

@EvanCarroll मैं इससे थोड़ी देर के लिए जूझता रहा। बस पाया कि यह एक स्तर के मामले को बाहर खींचने के लिए थोड़ा तेज है:CASE WHEN (NOT ts.primary_direction) THEN dense_rank() OVER (PARTITION BY ts.train_service_key ORDER BY pc.through_idx DESC, pc.first_portion ASC, pc.first_seq DESC) ELSE dense_rank() OVER (PARTITION BY ts.train_service_key ORDER BY pc.through_idx DESC, pc.first_portion ASC, pc.first_seq ASC) END AS coach_block_idx
enjayaitch

यह एक अच्छा विचार नहीं है .. आपको यहां कुछ समस्याएं मिली हैं। मेरा मतलब है कि बड़ी बात यह है कि आपके विचार का वास्तव में कोई मतलब नहीं है और यह आपके उपयोग के कारण अलग-अलग चीजें करता है, dense_rank()इसलिए यह वास्तव में एक प्रदर्शन मुद्दा नहीं है।
इवान कैरोल

1
@EvanCarroll ने आपकी टिप्पणी से मुझे स्वयं वहां पहुंचने के लिए प्रेरित किया (इसलिए मेरा संपादित उत्तर)। धन्यवाद।
enjayaitch

0

(मैं विचारों का बहुत बड़ा प्रशंसक हूं, लेकिन आपको यहां पीजी के साथ बहुत सावधान रहना होगा और मैं सभी को प्रोत्साहित करना चाहूंगा कि वे आम तौर पर पीजी में बेहतर समझ और प्रश्नों / कोड की स्थिरता के लिए विचारों का उपयोग करें। )

वास्तव में और दुख की बात है (पोस्टिंग में विचारों का उपयोग करके :) हमें वास्तविक समस्याओं का सामना करना पड़ा और हमने इसके अंदर उपयोग की जाने वाली सुविधाओं के आधार पर हमारे प्रदर्शन को बुरी तरह से घटा दिया :-( कम से कम v10.1 के साथ)। (यह अन्य के साथ ऐसा नहीं होगा। ओरेकल जैसी आधुनिक डीबी प्रणाली।)

तो, संभवतः (और यह मेरा सवाल है) दृश्य के खिलाफ कोई भी फ़िल्टरिंग ... जिसके परिणामस्वरूप अंतर्निहित तालिकाओं के खिलाफ एक एकल क्वेरी निष्पादन है।

(आप वास्तव में क्या मतलब के आधार पर - नहीं - मध्यवर्ती अस्थायी तालिकाओं को उत्प्रेरित किया जा सकता है कि आप नहीं होना चाहते हैं या जहां विधेय को नीचे नहीं धकेला जा सकता है ...)

मैं कम से कम दो प्रमुख "विशेषताओं" को जानता हूं, जो हमें Oracle से पोस्टग्रैज के प्रवास के बीच में नीचे करते हैं इसलिए हमें एक परियोजना में पीजी को छोड़ना पड़ा:

  • सीटीई ( with-clause सबक्वेरी / आम तालिका भाव ) कर रहे हैं (आमतौर पर) संरचना और अधिक जटिल प्रश्नों (यहां तक कि छोटे एप्लिकेशन में) के लिए उपयोगी है, लेकिन पीजी में के रूप में लागू डिजाइन द्वारा कर रहे हैं "छिपा" अनुकूलक संकेत (पैदा करने जैसे गैर-सूचीबद्ध अस्थायी तालिकाओं) और इस प्रकार घोषणात्मक एसक्यूएल ( ओरेकल दान ) की अवधारणा (मेरे लिए और अन्य बहुत से महत्वपूर्ण के लिए) का उल्लंघन करते हैं : जैसे

    • सरल प्रश्न:

      explain
      
        select * from pg_indexes where indexname='pg_am_name_index'
      
      /* result: 
      
      Nested Loop Left Join  (cost=12.38..26.67 rows=1 width=260)
        ...
        ->  Bitmap Index Scan on pg_class_relname_nsp_index  (cost=0.00..4.29 rows=2 width=0)
                                               Index Cond: (relname = 'pg_am_name_index'::name)
        ...
      */
      
    • कुछ CTE का उपयोग करके फिर से लिखा गया:

      explain
      
        with 
      
        unfiltered as (
          select * from pg_indexes
        ) 
      
        select * from unfiltered where indexname='pg_am_name_index'
      
      /* result:
      
      CTE Scan on unfiltered  (cost=584.45..587.60 rows=1 width=288)
         Filter: (indexname = 'pg_am_name_index'::name)
         CTE unfiltered
           ->  Hash Left Join  (cost=230.08..584.45 rows=140 width=260)  
      ...
      */
      
    • चर्चा आदि के साथ आगे के स्रोत: https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/

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


with-clauses के लिए हमारा वर्कअराउंड

हम सभी "इनलाइन विचारों" को एक विशेष उपसर्ग के साथ वास्तविक विचारों में बदल देंगे ताकि वे विचारों की सूची / नाम स्थान को गड़बड़ न करें और आसानी से मूल "बाहरी दृश्य" से संबंधित हो सकें: - /


खिड़की के कार्यों के लिए हमारा समाधान

हमने इसे Oracle डेटाबेस का उपयोग करके सफलतापूर्वक लागू किया।


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