ग्रुप BY और ORDER BY के साथ बड़े टेबल पर धीमी क्वेरी


14

मेरे पास 7.2 मिलियन टुपल्स वाली एक तालिका है जो इस तरह दिखती है:

                               table public.methods
 column |          type         |                      attributes
--------+-----------------------+----------------------------------------------------
 id     | integer               | not null DEFAULT nextval('methodkey'::regclass)
 hash   | character varying(32) | not null
 string | character varying     | not null
 method | character varying     | not null
 file   | character varying     | not null
 type   | character varying     | not null
Indexes:
    "methods_pkey" PRIMARY KEY, btree (id)
    "methodhash" btree (hash)

अब मैं कुछ मूल्यों का चयन करना चाहता हूं, लेकिन क्वेरी अविश्वसनीय रूप से धीमी है:

db=# explain 
    select hash, string, count(method) 
    from methods 
    where hash not in 
          (select hash from nostring) 
    group by hash, string 
    order by count(method) desc;
                                            QUERY PLAN
----------------------------------------------------------------------------------------
 Sort  (cost=160245190041.10..160245190962.07 rows=368391 width=182)
   Sort Key: (count(methods.method))
   ->  GroupAggregate  (cost=160245017241.77..160245057764.73 rows=368391 width=182)
       ->  Sort  (cost=160245017241.77..160245026451.53 rows=3683905 width=182)
             Sort Key: methods.hash, methods.string
             ->  Seq Scan on methods  (cost=0.00..160243305942.27 rows=3683905 width=182)
                   Filter: (NOT (SubPlan 1))
                   SubPlan 1
                   ->  Materialize  (cost=0.00..41071.54 rows=970636 width=33)
                     ->  Seq Scan on nostring  (cost=0.00..28634.36 rows=970636 width=33)

hashस्तंभ के MD5 हैश है stringऔर एक सूचकांक है। तो मुझे लगता है कि मेरी समस्या यह है कि पूरी तालिका को आईडी द्वारा क्रमबद्ध किया गया है न कि हैश द्वारा, इसलिए इसे पहले सॉर्ट करने में कुछ समय लगता है और फिर इसे समूहित करता है?

तालिका nostringमें केवल उन हैश की एक सूची है जो मेरे पास नहीं है। लेकिन मुझे सभी मूल्यों के लिए दोनों तालिकाओं की आवश्यकता है। इसलिए यह इन्हें हटाने का विकल्प नहीं है।

अतिरिक्त जानकारी: स्तंभों में से कोई भी शून्य नहीं हो सकता है (तालिका परिभाषा में तय किया गया है) और मैं पोस्टग्रेजिक 9.2 का उपयोग कर रहा हूं।


1
हमेशा आपके द्वारा उपयोग किए जाने वाले PostgreSQL का संस्करण प्रदान करें। NULLकॉलम में मानों का प्रतिशत क्या है method? वहाँ पर डुप्लिकेट हैं string?
एरविन ब्रान्डसेट्टर 0

जवाबों:


18

LEFT JOINमें @ Dezso का जवाब अच्छा होना चाहिए। एक इंडेक्स, हालांकि, शायद ही उपयोगी (प्रति से) होगा, क्योंकि क्वेरी को पूरे टेबल को वैसे भी पढ़ना है - अपवाद केवल 9.2 और पोस्टग्रेज में अनुक्रमणिका स्कैन करता है, और अनुकूल परिस्थितियां, नीचे देखें।

SELECT m.hash, m.string, count(m.method) AS method_ct
FROM   methods m
LEFT   JOIN nostring n USING (hash)
WHERE  n.hash IS NULL
GROUP  BY m.hash, m.string 
ORDER  BY count(m.method) DESC;

EXPLAIN ANALYZEक्वेरी पर चलाएँ । कई बार कैशिंग प्रभाव और शोर को बाहर करने के लिए। सर्वोत्तम परिणामों की तुलना करें।

एक बहु-स्तंभ सूचकांक बनाएं जो आपकी क्वेरी से मेल खाता हो:

CREATE INDEX methods_cluster_idx ON methods (hash, string, method);

रुको? बाद मैंने कहा कि एक सूचकांक मदद नहीं करेगा? ठीक है, हमें इसकी आवश्यकता CLUSTERतालिका पर है:

CLUSTER methods USING methods_cluster_idx;
ANALYZE methods;

ररून EXPLAIN ANALYZE। कोई तेज? यह होना चाहिए।

CLUSTERप्रयुक्त सूचकांक के क्रम में पूरी तालिका को फिर से लिखने के लिए एक बार का ऑपरेशन है। यह भी प्रभावी रूप से एक है VACUUM FULL। यदि आप निश्चित होना चाहते हैं, तो आप यह VACUUM FULLदेखने के लिए अकेले परीक्षण कर सकते हैं कि इसके लिए क्या जिम्मेदार ठहराया जा सकता है।

यदि आपकी तालिका में बहुत सारे लेखन कार्य हैं, तो प्रभाव समय के साथ कम हो जाएगा। CLUSTERप्रभाव को बहाल करने के लिए ऑफ-टाइम पर अनुसूची । फाइन ट्यूनिंग आपके सटीक उपयोग-मामले पर निर्भर करता है। मैनुअल के बारे में CLUSTER

CLUSTERएक नहीं बल्कि कच्चे उपकरण है, मेज पर एक विशेष ताला की जरूरत है। यदि आप ऐसा नहीं कर सकते हैं, तो विचार करें कि pg_repackजो अनन्य लॉक के बिना भी कर सकता है। इस उत्तर में और अधिक:


यदिNULL स्तंभ में मानों का प्रतिशत methodअधिक है (वास्तविक पंक्ति आकारों के आधार पर ~ 20 प्रतिशत से अधिक), तो आंशिक सूचकांक को मदद करनी चाहिए:

CREATE INDEX methods_foo_idx ON methods (hash, string)
WHERE method IS NOT NULL;

(आपका बाद का अपडेट आपके कॉलम दिखाता है NOT NULL, इसलिए लागू नहीं है।)

यदि आप PostgreSQL 9.2 या बाद में ( @deszo टिप्पणी के रूप में ) चला रहे हैं , तो प्रस्तुत इंडेक्स केवल-स्कैन केCLUSTER उपयोग के बिना ही उपयोगी हो सकते हैं । केवल अनुकूल परिस्थितियों में ही लागू होता है: कोई भी लिखने का कार्य जो दृश्यता के नक्शे को प्रभावित करेगा क्योंकि क्वेरी में अंतिम और सभी कॉलमों को सूचकांक द्वारा कवर किया जाना है। मूल रूप से रीड-ओनली टेबल किसी भी समय इसका उपयोग कर सकते हैं, जबकि भारी लिखित टेबल सीमित हैं। Postgres Wiki में अधिक जानकारी।VACUUM

उपर्युक्त आंशिक सूचकांक उस स्थिति में और भी अधिक उपयोगी हो सकता है।

यदि , दूसरी ओर, कॉलम में कोई NULL मान नहीं हैं method, तो आपको
1.) को परिभाषित करना चाहिए NOT NULLऔर
2. के count(*)बजाय इसका उपयोग करना चाहिए count(method), जो थोड़ा तेज़ है और NULLमूल्यों के अभाव में भी ऐसा ही करता है ।

यदि आपको इस क्वेरी को अक्सर कॉल करना है और तालिका केवल पढ़ने के लिए है, तो बनाएं MATERIALIZED VIEW


विदेशी ठीक बिंदु: आपकी तालिका का नाम रखा गया है nostring, फिर भी इसमें हैश लगता है। स्ट्रिंग्स के बजाय हैश को छोड़कर, एक मौका है कि आप उद्देश्य से अधिक स्ट्रिंग्स को बाहर करते हैं। अत्यधिक संभावना नहीं है, लेकिन संभव है।


क्लस्टर के साथ इसकी बहुत अधिक तेजी से। क्वेरी के लिए अभी भी 5min की जरूरत है, लेकिन यह पूरी रात चलने की तुलना में बहुत बेहतर है: D
reox

@reox: जब से आप v9.2 चलाते हैं: आपने क्लस्टरिंग से पहले केवल इंडेक्स के साथ परीक्षण किया था? यदि आपने अंतर देखा तो दिलचस्प होगा। (आप क्लस्टरिंग के बाद अंतर को पुन: उत्पन्न नहीं कर सकते।) इसके अलावा (और यह सस्ता होगा), क्या एक्सप्लेन एक सूचकांक स्कैन या एक पूर्ण तालिका स्कैन दिखाता है?
इरविन ब्रान्डसेट्टर

5

DBA.SE में आपका स्वागत है!

आप इस तरह से अपनी क्वेरी को पुनःप्रकाशित करने का प्रयास कर सकते हैं:

SELECT m.hash, string, count(method) 
FROM 
    methods m
    LEFT JOIN nostring n ON m.hash = n.hash
WHERE n.hash IS NULL
GROUP BY hash, string 
ORDER BY count(method) DESC;

या एक और संभावना:

SELECT m.hash, string, count(method) 
FROM 
    methods m
WHERE NOT EXISTS (SELECT hash FROM nostring WHERE hash = m.hash)
GROUP BY hash, string 
ORDER BY count(method) DESC;

NOT IN प्रदर्शन के लिए एक विशिष्ट सिंक है क्योंकि इसके साथ एक सूचकांक का उपयोग करना मुश्किल है।

इंडेक्स के साथ इसे और बढ़ाया जा सकता है। एक इंडेक्स nostring.hashउपयोगी लगता है। लेकिन पहले: अब आपको क्या मिलेगा? (आउटपुट को देखना बेहतर होगा EXPLAIN ANALYZEक्योंकि लागतें स्वयं उस समय को नहीं बताती हैं जो संचालन में लगा था।)


एक इंडेक्स nostring.hash पर पहले से ही बनाया गया है, लेकिन मुझे लगता है कि पोस्टग्रेज बहुत अधिक ट्यूपल्स के कारण इसका उपयोग नहीं करते हैं ... जब मैं अक्षम अनुक्रम स्कैन को खोजता हूं, तो यह इंडेक्स का उपयोग करता है। अगर मैं बाएं हाथ का उपयोग करता हूं तो मुझे 32 मिलियन की लागत आती है, इसलिए इसका तरीका बेहतर है ... लेकिन मैं इसे और अधिक अनुकूलित करने की कोशिश कर रहा हूं ...
reox

3
लागत केवल योजनाकार के लिए पर्याप्त रूप से अच्छी योजना बनाने में सक्षम होने के लिए है। वास्तविक समय आमतौर पर इसके साथ सहसंबद्ध होता है, लेकिन जरूरी नहीं। इसलिए यदि आप सुनिश्चित होना चाहते हैं, तो उपयोग करें EXPLAIN ANALYZE
dezso

1

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

अन्य लोगों ने पहले से ही एक pl / pgsql फ़ंक्शन बनाया है जो पाठ से स्ट्रिंग तक एक md5 मान को परिवर्तित (का हिस्सा) करता है। उदाहरण के लिए /programming/9809381/hashing-a-string-to-a-numeric-value-in-postgressql देखें

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


1
मुझे संदेह है कि यह रूपांतरण चीजों को गति देगा। यहां सभी प्रश्न तुलना के लिए समानता का उपयोग करते हैं। संख्यात्मक अभिकलन की गणना करना और फिर समानता की जांच करना मेरे लिए बड़े लाभ का वादा नहीं करता है।
dezso 15

2
मुझे लगता है कि मैं अंतरिक्ष दक्षता के लिए एक नंबर के बजाय बाइटिया के रूप में md5 को स्टोर करूंगा: sqlfiddle.com/# .12/d41d8/252
जैक कहते हैं कि 16ans में topanswers.xyz

इसके अलावा, dba.se में आपका स्वागत है!
जैक का कहना है कि

@JackDouglas: दिलचस्प टिप्पणी! 32 के बजाय 16 बाइट प्रति md5 बड़ी तालिकाओं के लिए काफी थोड़ा है।
एरविन ब्रान्डेसटेटर

0

मैं इस मुद्दे में बहुत भागता हूं, और एक सरल 2-भाग चाल की खोज की।

  1. हैश मान पर सबस्ट्रिंग इंडेक्स बनाएं: (7 आमतौर पर एक अच्छी लंबाई है)

    create index methods_idx_hash_substring ON methods(substring(hash,1,7))

  2. क्या आपकी खोजों / जुड़ावों में एक विकल्प मिलान शामिल है, इसलिए क्वेरी प्लानर को सूचकांक का उपयोग करने के लिए संकेत दिया गया है:

    पुराना: WHERE hash = :kwarg

    नया: WHERE (hash = :kwarg) AND (substring(hash,1,7) = substring(:kwarg,1,7))

आपके पास कच्चे पर भी एक इंडेक्स होना चाहिए hash

परिणाम (आमतौर पर) यह है कि योजनाकार सबस्ट्रिंग इंडेक्स से पहले परामर्श करेगा और अधिकांश पंक्तियों का निराकरण करेगा। तब यह पूर्ण 32 वर्ण हैश से संबंधित सूचकांक (या तालिका) से मेल खाता है। इस दृष्टिकोण ने मेरे लिए 800ms प्रश्नों को 4 से नीचे कर दिया है।

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