एक PostgreSQL तालिका पंक्ति के आकार को मापें


83

मेरे पास एक PostgreSQL टेबल है। select *बहुत धीमा है जबकि select idअच्छा और जल्दी है। मुझे लगता है कि यह हो सकता है कि पंक्ति का आकार बहुत बड़ा है और परिवहन में थोड़ा समय लग रहा है, या यह कोई अन्य कारक हो सकता है।

मुझे सभी क्षेत्रों (या उनमें से लगभग सभी) की आवश्यकता है, इसलिए केवल एक सबसेट का चयन करना एक त्वरित समाधान नहीं है। मेरे इच्छित फ़ील्ड का चयन करना अभी भी धीमा है।

यहाँ मेरे टेबल स्कीमा के नाम हैं:

integer                  | not null default nextval('core_page_id_seq'::regclass)
character varying(255)   | not null
character varying(64)    | not null
text                     | default '{}'::text
character varying(255)   | 
integer                  | not null default 0
text                     | default '{}'::text
text                     | 
timestamp with time zone | 
integer                  | 
timestamp with time zone | 
integer                  | 

टेक्स्ट फ़ील्ड का आकार किसी भी आकार का हो सकता है। लेकिन फिर भी, सबसे खराब स्थिति में कुछ किलोबाइट से अधिक नहीं।

प्रशन

  1. क्या इसके बारे में ऐसा कुछ है जो 'पागल अक्षम' चिल्लाता है?
  2. क्या मुझे इसे डीबग करने में मदद करने के लिए Postgres कमांड-लाइन पर पृष्ठ का आकार मापने का एक तरीका है?

दरअसल ... एक कॉलम 11 एमबी का है। मुझे लगता है कि यह समझाएगा। तो क्या length(*)सिर्फ करने के बजाय एक तरीका है length(field)? मुझे पता है कि यह बाइट्स नहीं है, लेकिन मुझे केवल एक अनुमानित मूल्य की आवश्यकता है।
जो

जवाबों:


101

Q2: way to measure page size

PostgreSQL डेटाबेस ऑब्जेक्ट आकार कार्यों के एक नंबर प्रदान करता है । मैंने इस क्वेरी में सबसे दिलचस्प वाले पैक किए और नीचे कुछ सांख्यिकी एक्सेस फ़ंक्शंस जोड़े । (अतिरिक्त मॉड्यूल pgstattuple अभी तक अधिक उपयोगी कार्य प्रदान करता है।)

यह दिखाने के लिए है कि "पंक्ति के आकार" को मापने के लिए अलग-अलग तरीके बहुत अलग परिणाम देते हैं। यह सब उस पर निर्भर करता है जो आप मापना चाहते हैं, बिल्कुल।

इस क्वेरी को Postgres 9.3 या बाद के संस्करण की आवश्यकता है । पुराने संस्करणों के लिए नीचे देखें।

हर पंक्ति के लिए गणना से बचने के लिए, VALUESएक LATERALउपशम में एक अभिव्यक्ति का उपयोग करना ।

public.tblअपनी पंक्तियों के आकार के बारे में एकत्रित आँकड़ों का एक कॉम्पैक्ट दृश्य प्राप्त करने के लिए अपने वैकल्पिक स्कीमा-योग्य तालिका नाम के साथ (दो बार) बदलें । आप इसे बार-बार उपयोग करने के लिए एक plpgsql फ़ंक्शन में लपेट सकते हैं, पैरामीटर और उपयोग के रूप में तालिका नाम में हाथ EXECUTE...

SELECT l.metric, l.nr AS "bytes/ct"
     , CASE WHEN is_size THEN pg_size_pretty(nr) END AS bytes_pretty
     , CASE WHEN is_size THEN nr / NULLIF(x.ct, 0) END AS bytes_per_row
FROM  (
   SELECT min(tableoid)        AS tbl      -- = 'public.tbl'::regclass::oid
        , count(*)             AS ct
        , sum(length(t::text)) AS txt_len  -- length in characters
   FROM   public.tbl t                     -- provide table name *once*
   ) x
 , LATERAL (
   VALUES
      (true , 'core_relation_size'               , pg_relation_size(tbl))
    , (true , 'visibility_map'                   , pg_relation_size(tbl, 'vm'))
    , (true , 'free_space_map'                   , pg_relation_size(tbl, 'fsm'))
    , (true , 'table_size_incl_toast'            , pg_table_size(tbl))
    , (true , 'indexes_size'                     , pg_indexes_size(tbl))
    , (true , 'total_size_incl_toast_and_indexes', pg_total_relation_size(tbl))
    , (true , 'live_rows_in_text_representation' , txt_len)
    , (false, '------------------------------'   , NULL)
    , (false, 'row_count'                        , ct)
    , (false, 'live_tuples'                      , pg_stat_get_live_tuples(tbl))
    , (false, 'dead_tuples'                      , pg_stat_get_dead_tuples(tbl))
   ) l(is_size, metric, nr);

नतीजा:

              मीट्रिक | बाइट्स / ct | bytes_pretty | bytes_per_row
----------------------------------- + ---------- + --- ----------- + ---------------
 core_relation_size | 44138496 | 42 एमबी | 91
 दृश्यता_मैप | 0 | 0 बाइट्स | 0
 free_space_map | 32768 | 32 केबी | 0
 table_size_incl_toast | 44179456 | 42 एमबी | 91
 indexes_size | 33128448 | 32 एमबी | 68
 Total_size_incl_toast_and_indexes | 77307904 | 74 एमबी | 159
 live_rows_in_text_repretation | 29987360 | 29 एमबी | 62
 ------------------------------ | | |
 row_count | 483424 | |
 live_tuples | 483424 | |
 dead_tuples | 2677 | |

पुराने संस्करणों के लिए (9.2 या इससे अधिक पुराने पोस्टग्रैड्स):

WITH x AS (
   SELECT count(*)               AS ct
        , sum(length(t::text))   AS txt_len  -- length in characters
        , 'public.tbl'::regclass AS tbl      -- provide table name as string
   FROM   public.tbl t                       -- provide table name as name
   ), y AS (
   SELECT ARRAY [pg_relation_size(tbl)
               , pg_relation_size(tbl, 'vm')
               , pg_relation_size(tbl, 'fsm')
               , pg_table_size(tbl)
               , pg_indexes_size(tbl)
               , pg_total_relation_size(tbl)
               , txt_len
             ] AS val
        , ARRAY ['core_relation_size'
               , 'visibility_map'
               , 'free_space_map'
               , 'table_size_incl_toast'
               , 'indexes_size'
               , 'total_size_incl_toast_and_indexes'
               , 'live_rows_in_text_representation'
             ] AS name
   FROM   x
   )
SELECT unnest(name)                AS metric
     , unnest(val)                 AS "bytes/ct"
     , pg_size_pretty(unnest(val)) AS bytes_pretty
     , unnest(val) / NULLIF(ct, 0) AS bytes_per_row
FROM   x, y

UNION ALL SELECT '------------------------------', NULL, NULL, NULL
UNION ALL SELECT 'row_count', ct, NULL, NULL FROM x
UNION ALL SELECT 'live_tuples', pg_stat_get_live_tuples(tbl), NULL, NULL FROM x
UNION ALL SELECT 'dead_tuples', pg_stat_get_dead_tuples(tbl), NULL, NULL FROM x;

एक ही परिणाम।

Q1: anything inefficient?

आप प्रति पंक्ति कुछ बाइट्स सहेजने के लिए स्तंभ क्रम का अनुकूलन कर सकते हैं , वर्तमान में संरेखण पैडिंग के लिए बर्बाद हो गए:

integer                  | not null default nextval('core_page_id_seq'::regclass)
integer                  | not null default 0
character varying(255)   | not null
character varying(64)    | not null
text                     | default '{}'::text
character varying(255)   | 
text                     | default '{}'::text
text                     |
timestamp with time zone |
timestamp with time zone |
integer                  |
integer                  |

यह प्रति पंक्ति 8 से 18 बाइट्स के बीच बचाता है। मैं इसे "कॉलम टेट्रिस" कहता हूं । विवरण:

इस पर भी विचार करें:


यदि टेबल खाली है तो आपका स्निपेट 9.3 पूर्व का विभाजन शून्य से फेंकता है। मैं वास्तव में 9.3+ संस्करण का उपयोग करना चाहता था, लेकिन गलती से एक को उठाया और इसे ठीक करने में कुछ घंटे बिताने पड़े ... अब मैं उस सभी समय को बेकार नहीं जाने दे सकता। , unnest(val) / ctद्वारा प्रतिस्थापित किया जाता है , (LEAST(unnest(val), unnest(val) * ct)) / (ct - 1 + sign(ct))और यह फेंकेगा नहीं। तर्क यह है कि, जब ctहै 0, valद्वारा प्रतिस्थापित किया जाएगा 0और ctइसके द्वारा प्रतिस्थापित किया जाएगा 1
13

1
@GuiRitter: इंगित करने के लिए धन्यवाद। मैंने एक सरल सुधार लागू किया, यद्यपि। इसके साथ ही कुछ सामान्य अपडेट भी - लेकिन क्वेरी समान बनी हुई है।
Erwin Brandstetter

35

एक पंक्ति के आकार का, जिसमें TOAST की एड सामग्री शामिल है, पूरी पंक्ति के TEXT प्रतिनिधित्व की लंबाई को क्वेरी करके प्राप्त करना आसान है:

SELECT octet_length(t.*::text) FROM tablename AS t WHERE primary_key=:value;

यह बाइट्स की संख्या के करीब एक सन्निकटन है जिसे निष्पादित करते समय क्लाइंट-साइड को पुनः प्राप्त किया जाएगा:

SELECT * FROM tablename WHERE primary_key=:value;

... यह मानते हुए कि क्वेरी का कॉलर पाठ प्रारूप में परिणाम का अनुरोध कर रहा है, जो कि अधिकांश कार्यक्रम करता है (द्विआधारी प्रारूप संभव है, लेकिन यह ज्यादातर मामलों में परेशानी के लायक नहीं है)।

उसी तकनीक को N"सबसे बड़ी-इन-टेक्स्ट" पंक्तियों का पता लगाने के लिए लागू किया जा सकता है tablename:

SELECT primary_key, octet_length(t.*::text) FROM tablename AS t
   ORDER BY 2 DESC LIMIT :N;

बड़े डेटा के साथ काम करते समय जल्दी से कुछ अनुमान लगाने का उत्कृष्ट तरीका (उदाहरण के लिए पंक्ति आकार का अधिकांश भाग चर-लंबाई टोस्ट-संग्रहित कॉलम में निहित है), अच्छा विचार है!
fgblomqvist

14

कुछ चीजें हैं जो हो सकती हैं। सामान्य तौर पर, मुझे संदेह है कि लंबाई समीपस्थ समस्या है। मुझे संदेह है कि इसके बजाय आपको लंबाई से संबंधित समस्या है।

आप कहते हैं कि टेक्स्ट फ़ील्ड कुछ k तक मिल सकती है। एक पंक्ति मुख्य भंडारण में 8k से अधिक नहीं जा सकती है, और यह संभावना है कि आपके बड़े पाठ क्षेत्र टोस्ट किए गए हैं, या मुख्य भंडारण से अलग फ़ाइलों में विस्तारित भंडारण में चले गए हैं। यह आपके मुख्य भंडारण को तेज़ बनाता है (इसलिए सेलेक्ट आईडी वास्तव में तेज़ है क्योंकि एक्सेस करने के लिए कम डिस्क पृष्ठ हैं) लेकिन चयन * धीमा हो जाता है क्योंकि अधिक यादृच्छिक I / O है।

यदि आपकी कुल पंक्ति का आकार अभी भी 8k से कम है तो आप स्टोरेज सेटिंग में फेरबदल कर सकते हैं। हालाँकि, मैं आपको चेतावनी दूंगा कि जब आप को ऐसा नहीं करना है और यदि आपको ऐसा नहीं करना है, तो इसे न छूने के लिए मुख्य स्टोरेज में एक ओवरसाइज़्ड विशेषता डालते समय खराब चीजें हो सकती हैं। तो परिवहन की संभावना ही नहीं है। यह कई, कई क्षेत्रों को समेटे हुए हो सकता है जिन्हें यादृच्छिक रीड की आवश्यकता होती है। बड़ी संख्या में रैंडम रीड्स भी कैश मिस हो सकते हैं, और बड़ी मात्रा में आवश्यक मेमोरी की आवश्यकता हो सकती है कि चीजें डिस्क पर और बड़ी संख्या में विस्तृत पंक्तियों में भौतिक हो जाएं, यदि कोई ज्वाइन मौजूद है (और अगर टोस्ट शामिल है तो) महंगा हो सकता है पैटर्न में शामिल हों, आदि।

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


4

ऊपर बताए गए डेटाबेस ऑब्जेक्ट साइज़ फ़ंक्शंस का उपयोग करना :

SELECT primary_key, pg_column_size(tablename.*) FROM tablename;


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