2015-12-14 6 views
7

मैं निम्न तालिकाओं है:मैं अपने पोस्टग्रेस चयन कथन की गति कैसे बढ़ा सकता हूं?

CREATE TABLE views (
    view_id bigint NOT NULL, 
    usr_id bigint, 
    ip inet, 
    referer_id bigint, 
    country_id integer, 
    validated smallint, 
    completed smallint, 
    value numeric 
); 

ALTER TABLE ONLY views 
    ADD CONSTRAINT "Views_pkey" PRIMARY KEY (view_id); 

CREATE TABLE country (
    country_id integer NOT NULL, 
    country character varying(2) 
); 

ALTER TABLE ONLY country 
    ADD CONSTRAINT country_pkey PRIMARY KEY (country_id); 

CREATE TABLE file_id_view_id (
    file_id bigint, 
    view_id bigint, 
    created_ts timestamp without time zone 
); 

CREATE TABLE file_owner (
    file_id bigint NOT NULL, 
    owner_id bigint 
); 

ALTER TABLE ONLY file_owner 
     ADD CONSTRAINT owner_table_pkey PRIMARY KEY (file_id); 

CREATE TABLE referer (
    referer_id bigint NOT NULL, 
    referer character varying(255) 
); 

ALTER TABLE ONLY referer 
    ADD CONSTRAINT referer_pkey PRIMARY KEY (referer_id); 

views और file_id_view_id तालिका लगभग 340m पंक्तियों प्रत्येक है। प्रत्येक घंटे वे दोनों 600K पंक्तियों में वृद्धि करेंगे।

file_owner तालिका 75K पंक्तियां हैं और पंक्तियों से प्रति घंटा की वृद्धि होगी।

country तालिका में पंक्तियां और शायद ही कभी परिवर्तन होते हैं।

referer तालिका में पंक्तियां और शायद ही कभी परिवर्तन होते हैं।

SELECT Count(ft.*)      AS total_views, 
     (Count(ft.*) - SUM(ft.valid)) AS invalid_views, 
     SUM(ft.valid)     AS valid_views, 
     SUM(ft.values)     AS VALUES, 
     ft.day       AS day, 
     (CASE 
      WHEN r.referer IS NULL THEN 'Unknown' 
      ELSE r.referer 
     END)       AS referer, 
     (CASE 
      WHEN c.country IS NULL THEN 'Unknown' 
      ELSE c.country 
     END)       AS country 
FROM country c 
     right join (referer r 
        right join (SELECT v.validated AS valid, 
             v.value  AS VALUES, 
             vf.day  AS day, 
             vf.view_id AS view_id, 
             v.referer_id AS referer_id, 
             v.country_id AS country_id 
           FROM VIEWS v, 
             (SELECT view_id, 
fivi.created_ts :: timestamp :: DATE AS 
day 
FROM file_id_view_id fivi 
join (SELECT file_id 
     FROM file_owner 
     WHERE owner_id = 75 
     GROUP BY file_id) fo 
    ON (fo.file_id = fivi.file_id) 
WHERE (fivi.created_ts BETWEEN 
    '2015-11-01' AND '2015-12-01') 
GROUP BY view_id, 
    day) vf 
WHERE v.view_id = vf.view_id) ft 
ON (ft.referer_id = r.referer_id)) 
ON (ft.country_id = c.country_id) 
GROUP BY day, 
      referer, 
      country; 

उत्पादन करने के लिए:

मेरा लक्ष्य जैसे एक प्रश्न प्रदर्शन करने के लिए सक्षम होने के लिए है

total_views | invalid_views | valid_views | values | day  |  referer  | country 
------------+---------------+-------------+--------+------------+-----------------+--------- 

जब EXPLAIN ANALYZE के साथ इस तरह क्वेरी चलाने निम्नलिखित उत्पादन किया जाता है:

GroupAggregate (cost=38893491.99..40443007.61 rows=182295955 width=52) (actual time=183725.696..205882.889 rows=172 loops=1) 
    Group Key: ((fivi.created_ts)::date), r.referer, c.country 
    -> Sort (cost=38893491.99..38984639.97 rows=182295955 width=52) (actual time=183725.655..200899.098 rows=8390217 loops=1) 
     Sort Key: ((fivi.created_ts)::date), r.referer, c.country 
     Sort Method: external merge Disk: 420192kB 
     -> Hash Left Join (cost=16340128.88..24989809.75 rows=182295955 width=52) (actual time=23399.900..104337.332 rows=8390217 loops=1) 
       Hash Cond: (v.country_id = c.country_id) 
       -> Hash Left Join (cost=16340125.36..24800637.72 rows=182295955 width=49) (actual time=23399.782..102534.655 rows=8390217 loops=1) 
        Hash Cond: (v.referer_id = r.referer_id) 
        -> Merge Join (cost=16340033.52..24051874.62 rows=182295955 width=29) (actual time=23397.410..99955.000 rows=8390217 loops=1) 
          Merge Cond: (fivi.view_id = v.view_id) 
          -> Group (cost=16340033.41..16716038.36 rows=182295955 width=16) (actual time=23397.298..30454.444 rows=8390217 loops=1) 
           Group Key: fivi.view_id, ((fivi.created_ts)::date) 
           -> Sort (cost=16340033.41..16434985.73 rows=189904653 width=16) (actual time=23397.294..28165.729 rows=8390217 loops=1) 
             Sort Key: fivi.view_id, ((fivi.created_ts)::date) 
             Sort Method: external merge Disk: 180392kB 
             -> Nested Loop (cost=6530.43..8799350.01 rows=189904653 width=16) (actual time=63.123..15131.956 rows=8390217 loops=1) 
              -> HashAggregate (cost=6530.31..6659.62 rows=43104 width=8) (actual time=62.983..90.331 rows=43887 loops=1) 
                Group Key: file_owner.file_id 
                -> Bitmap Heap Scan on file_owner (cost=342.90..6508.76 rows=43104 width=8) (actual time=5.407..50.779 rows=43887 loops=1) 
                 Recheck Cond: (owner_id = 75) 
                 Heap Blocks: exact=5904 
                 -> Bitmap Index Scan on owner_id_index (cost=0.00..340.74 rows=43104 width=0) (actual time=4.327..4.327 rows=45576 loops=1) 
                   Index Cond: (owner_id = 75) 
              -> Index Scan using file_id_view_id_indexing on file_id_view_id fivi (cost=0.11..188.56 rows=4406 width=24) (actual time=0.122..0.306 rows=191 loops=43887) 
                Index Cond: (file_id = file_owner.file_id) 
                Filter: ((created_ts >= '2015-11-01 00:00:00'::timestamp without time zone) AND (created_ts <= '2015-12-01 00:00:00'::timestamp without time zone)) 
                Rows Removed by Filter: 184 
          -> Index Scan using "Views_pkey" on views v (cost=0.11..5981433.17 rows=338958763 width=25) (actual time=0.088..46804.757 rows=213018702 loops=1) 
        -> Hash (cost=68.77..68.77 rows=6591 width=28) (actual time=2.344..2.344 rows=6495 loops=1) 
          Buckets: 1024 Batches: 1 Memory Usage: 410kB 
          -> Seq Scan on referer r (cost=0.00..68.77 rows=6591 width=28) (actual time=0.006..1.156 rows=6495 loops=1) 
       -> Hash (cost=2.70..2.70 rows=233 width=7) (actual time=0.078..0.078 rows=233 loops=1) 
        Buckets: 1024 Batches: 1 Memory Usage: 10kB 
        -> Seq Scan on country c (cost=0.00..2.70 rows=233 width=7) (actual time=0.005..0.042 rows=233 loops=1) 
Planning time: 1.015 ms 
Execution time: 206034.660 ms 
(37 rows) 

व्याख्या पर योजना.depesz.com: http://explain.depesz.com/s/OiN

206s रन टाइम।

कुछ बातों का ध्यान रखना,

Postgresql संस्करण 9,4

मैं config समायोजित किया है इस प्रकार है:

  1. shared_buffers = 30GB
  2. work_mem = 32MB
  3. random_page_cost = 2.0
  4. cpu_tuple_cost = 0.0030
  5. cpu_index_tuple_cost = 0.0010
  6. cpu_operator_cost = 0।0005
  7. effective_cache_size = 52GB

निम्नलिखित अनुक्रमित वर्तमान में मौजूद:

  1. BTREE (देश) का उपयोग करते हुए देश सूचकांक country_index;
  2. INDEX बनाया_ts_index फ़ाइल_id_view_id पर btree (made_ts) का उपयोग करना;
  3. INDEX फ़ाइल_id_created_ts_index बनाएं file_id_view_id btree (create_ts, file_id) का उपयोग करना;
  4. INDEX फ़ाइल_id_view_id_indexing बनाएं file_id_view_id btree (file_id) का उपयोग करना;
  5. INDEX स्वामी_id_file_id_index बनाएं file_owner पर btree (file_id, owner_id) का उपयोग करना;
  6. INDEX owner_id_index बनाएं file_owner पर btree (owner_id) का उपयोग करना;
  7. बीटीआरई (रेफरर) का उपयोग करने वाले रेफरर पर INDEX referer_index बनाएं;

पूर्व क्वेरी एक स्वामी आईडी जो परंपरागत ढंग से चुना गया था उपयोग कर रहा था, कुछ क्वेरी परिणाम हो सकता है में 1/3 file_id_view_id तालिका केविचारों साथ जोड़ा जा सकेगा।

डेटा संरचना बदलना अंतिम रिज़ॉर्ट है। इस स्तर पर इस तरह के बदलाव गंभीर चिंताओं के कारण होना चाहिए।

डाटाबेस के रूप में केवल पढ़ने के लिए अगर, डेटा लिखा जा रहा करने की जरुरत घंटे के हिसाब और साँस लेने के कमरे के बहुत सारे पर किया जाता है प्रत्येक लिखने के बाद Postgres को दिया जाता है माना जा सकता है। वर्तमान समय में 600K प्रति घंटा के दौरान डीबी 1100 में लौट रहा है (यह सम्मिलित लागत के साथ अन्य कारणों के कारण है)। अतिरिक्त इंडेक्स जोड़ने के लिए बहुत सारे कमरे हैं यदि यह पढ़ने की गति को बढ़ाएगा, तो पढ़ने की गति प्राथमिकता है।

हार्डवेयर विनिर्देशों हैं:

सीपीयू: http://ark.intel.com/products/83356/Intel-Xeon-Processor-E5-2630-v3-20M-Cache-2_40-GHz

रैम: 128GB

भंडारण: 1.5TB PCIe SSD

मैं या तो मेरे डेटाबेस या क्वेरी कैसे अनुकूलन कर सकते हैं ताकि मैं उचित समय सीमा में डीबी से बाहर की जाने वाली जानकारी को पुनर्प्राप्त कर सकूं?

मुझे अपने वर्तमान डिजाइन अनुकूलन करने के लिए कर सकते हैं?

मैं Postgres विश्वास करते हैं और हार्डवेयर इस पर चल रहा है क्षमता ज्यादा बेहतर की तुलना में यह वर्तमान में है प्रदर्शन करने के लिए है।

अद्यतन

मैं कोशिश की है:

  1. तालिकाओं का विश्लेषण करें, प्रदर्शन प्रभावित नहीं किया।
  2. work_mem बढ़ाएं, इसके परिणामस्वरूप गति में 116 की वृद्धि हुई है।
  3. उप-चयनों से परहेज करके पोस्टग्रेस के प्रश्न योजनाकार पर निर्भर करता है, यह नकारात्मक प्रभाव प्रदर्शन।
  4. हाथ से अलग डीबी लुकअप, यह प्रतीत होता है कि कोई सकारात्मक/नकारात्मक प्रभाव नहीं है।

क्या किसी के पास कोई बड़ा अनुभव पुनर्गठन सारणी है? क्या यह व्यवहार्य है? क्या इसमें दिन लगेंगे, घंटे (कोर्स का अनुमान)?

मैं डेटाबेस को डी-सामान्य करने पर विचार कर रहा हूं, क्योंकि इसे वास्तव में केवल इस विधि में संदर्भित किया जाएगा। इसके साथ मेरी एकमात्र चिंता यह है कि - यदि किसी इंडेक्स किए गए मालिक_आईडी के साथ एक टेबल से 100 एम पंक्तियों को बुलाया जाना चाहिए, तो यह पर्याप्त तेज़ होगा या फिर भी मुझे एक ही प्रदर्शन के मुद्दों का सामना करना पड़ेगा? एक तरफ जाने से नफरत होगी तो पीछे हटना होगा।

मैं जिस अन्य समाधान में देख रहा हूं वह @ ivan.panasuik सुझाव है, पूरे दिन डेटा को किसी अन्य तालिका में समूहित करें क्योंकि एक बार दिन बीत चुका है कि जानकारी स्थिर है और इसे बदलने या अपडेट करने की आवश्यकता नहीं है। हालांकि मुझे यह सुनिश्चित नहीं है कि इसे आसानी से कैसे कार्यान्वित किया जाए - क्या मुझे डेटा के माध्यम से पूछताछ होनी चाहिए, जबकि आवेषण होल्ड हो और जितनी जल्दी संभव हो सके दिन पकड़ लें? तब से एक ट्रिगर सेट है?

+2

अनुमान वास्तव में सटीक नहीं हैं।क्या आपने शामिल तालिकाओं का विश्लेषण किया था? आपके पास डिस्क पर किए गए दो बहुत बड़े प्रकार भी हैं। आप work_mem को उस क्वेरी_ के लिए भारी रूप से बढ़ाने की कोशिश कर सकते हैं, उदा। 'work_mem =' 512MB 'सेट करें या यहां तक ​​कि' work_mem = '1GB'' –

+0

मैं इंप्रेशन के तहत था पोस्टग्रेस स्वचालित रूप से टेबल का विश्लेषण करेगा, क्या मुझे इसे मैन्युअल रूप से भी करना चाहिए? जब आप _that query_ कहते हैं, तो क्या आपका मतलब है कि एक ही क्वेरी के लिए work_mem सेट करने का एक विशिष्ट तरीका है? –

+0

यह _should_ स्वचालित रूप से ऐसा करता है, लेकिन कभी-कभी (जैसे प्रारंभिक लोड के बाद) यह पर्याप्त तेज़ नहीं होता है। आपके द्वारा दिखाए जाने वाले बयान _before_ आपकी क्वेरी को चालू सत्र के लिए 'work_mem' बदल देंगे: http://www.postgresql.org/docs/current/static/sql-set.html –

उत्तर

2

आमतौर पर आपके डेटाबेस की गति आपके हार्डवेयर नहीं है, यह कितनी अच्छी तरह से आप इंजन की खुफिया और विशेषताओं का उपयोग करते हैं।

  1. उप-चयनों से बचने की कोशिश करें - खासकर जब बहुत सारे डेटा से निपटना है। इन्हें अक्सर क्वेरी प्लानर द्वारा अनुकूलित नहीं किया जा सकता है। ज्यादातर मामलों में यदि आप आवश्यक हो तो हाथ से पहले सरल उप-चयनों को जॉइन या यहां तक ​​कि अलग डेटाबेस लुकअप में परिवर्तित करने में सक्षम होना चाहिए।

  2. अपनी तालिकाओं को विभाजित करें - PostgreSQL यह मूल रूप से नहीं करता है (प्रकार) लेकिन यदि आप अक्सर हाल ही में डेटा तक पहुंचते हैं तो आप संग्रह डेटा को रास्ते से बाहर ले जाकर बहुत सारे काम निकाल सकते हैं।

  3. डेटा वेयरहाउसिंग रणनीति पर विचार करें - जब आप उस डेटा के डेटा से निपटने पर विचार करते हैं तो आपको डेटा की एक प्रतिलिपि को एक असामान्य तरीके से संग्रहीत करना चाहिए जो बहुत जल्दी है, क्योंकि ग़रीब जॉइन का पहले से ही ख्याल रखा गया है। हम इसे Redshift (PostgeSQL का व्युत्पन्न) के साथ करते हैं ताकि रिपोर्ट चलाने के दौरान हमें कोई भी जॉइन करने की आवश्यकता न हो।

+0

मुझे कुछ सामानों के लिए आपका तीसरा सुझाव पसंद है जो मैं काम कर रहा हूं ... एक उचित विंडो पास होने तक कुशलतापूर्वक संग्रहीत डेटा के समानांतर डेटा के अस्थायी कैश को रखना। धन्यवाद! –

2
  1. निकालें (गणना (फुट *) -। योगफल (ft.valid)) invalid_views के रूप में, जब से तुम पहले से ही इस मान हो और आप इसे बाद में गणना कर सकते हैं, प्रदर्शन के दौरान परिणाम
  2. सूचकांक पर जोड़े file_owner.file_id और जांचें कि क्वेरी में उपयोग करने वाले प्रत्येक फ़ील्ड में इंडेक्स (फ़ील्ड जिन्हें आप शर्तों में उपयोग कर रहे हैं: जहां, समूह, इत्यादि)
  3. मैंने क्वेरी का अधिक विश्लेषण नहीं किया, लेकिन ऐसा लगता है कि आपको विभाजित करना चाहिए कुछ छोटे (और तेज़) प्रश्नों में क्वेरी करें और temp टेबल या संग्रहीत प्रक्रिया का उपयोग करके इसे कनेक्ट करें।
  4. मानते हैं कि कल के परिणामस्वरूप कुछ ऐसा नहीं होगा जो आप बदल नहीं पाएंगे ... आप शर्त दिन = आज() के साथ क्वेरी चला सकते हैं और दिन-प्रतिदिन समूह से बच सकते हैं। सभी दिन के परिणाम आप एक अलग मेज में सहेज सकते हैं। मैं देखता हूं कि ज्यादातर समय यह समूहबद्ध काम करता है।

कोशिश किए बिना ऑप्टिमाइज़ेशन की भविष्यवाणी करना बहुत मुश्किल है और कोशिश करें .... तो एक-एक करके प्रयास करें। और शुभकामनाएं।

संबंधित मुद्दे