2012-02-28 11 views
5

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

मुझे यहां कई समान प्रश्नों का उत्तर मिला, जिनमें से सबसे नज़दीक SQL query to return top N rows per ID across a range of IDs प्रतीत होता था, लेकिन उत्तर अधिकांश भाग प्रदर्शन-अज्ञेय समाधान के लिए थे जो मैंने पहले ही कोशिश की थी।

SELECT 
    * 
FROM 
    (
    SELECT 
     dailylogdata.*, 
     row_number() over (partition by dailylogdata.contextid order by occurrencedate) rn 
    FROM 
     dailylogdata 
    WHERE 
     shorturl in (?) 
    ) 
WHERE 
    rn <= 10; 

हालांकि यह देखते हुए कि इस तालिका किसी दिए गए दिन के लिए प्रविष्टियों के लाखों लोगों में शामिल है, और इस दृष्टिकोण से सभी पंक्तियों को पढ़ने जरूरी: उदाहरण के लिए, ROW_NUMBER() विश्लेषणात्मक समारोह से हम बिलकुल डेटा हम के लिए देख रहे हो जाता है सूचकांक जो row_number विश्लेषणात्मक फ़ंक्शन को लागू करने के लिए हमारे चयन मानदंड से मेल खाता है, प्रदर्शन भयानक है। हम अंत में लगभग दस लाख पंक्तियों का चयन, केवल बाहर उनमें से अधिकांश फेंकने के लिए है क्योंकि उनके ROW_NUMBER ऊपर क्वेरी को क्रियान्वित करने से 10 आँकड़े को पार कर:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 
|| Id | Operation       | Name     | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | Writes | OMem | 1Mem | Used-Mem | Used-Tmp|| 
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 
|| 0 | SELECT STATEMENT      |       |  1 |  | 12222 |00:09:08.94 |  895K| 584K| 301 |  |  |   |   || 
||* 1 | VIEW        |       |  1 | 4427K| 12222 |00:09:08.94 |  895K| 584K| 301 |  |  |   |   || 
||* 2 | WINDOW SORT PUSHED RANK   |       |  1 | 4427K| 13536 |00:09:08.94 |  895K| 584K| 301 | 2709K| 743K| 97M (1)| 4096 || 
|| 3 | PARTITION RANGE SINGLE   |       |  1 | 4427K| 932K|00:22:27.90 |  895K| 584K|  0 |  |  |   |   || 
|| 4 |  TABLE ACCESS BY LOCAL INDEX ROWID| DAILYLOGDATA   |  1 | 4427K| 932K|00:22:27.61 |  895K| 584K|  0 |  |  |   |   || 
||* 5 |  INDEX RANGE SCAN    | DAILYLOGDATA_URLCONTEXT |  1 | 17345 | 932K|00:00:00.75 | 1448 |  0 |  0 |  |  |   |   || 
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 
|                                             | 
|Predicate Information (identified by operation id):                                | 
|---------------------------------------------------                                | 
|                                             | 
| 1 - filter("RN"<=:SYS_B_2)                                     | 
| 2 - filter(ROW_NUMBER() OVER (PARTITION BY "DAILYLOGDATA"."CONTEXTID" ORDER BY "OCCURRENCEDATE")<=:SYS_B_2)                 | 
| 5 - access("SHORTURL"=:P1)                                     | 
|                                             | 
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 

हालांकि, अगर बजाय हम केवल पहले 10 परिणाम के लिए क्वेरी इस क्वेरी को चलने से

SELECT 
    * 
FROM 
    (
    SELECT 
     dailylogdata.* 
    FROM 
     dailylogdata 
    WHERE 
     shorturl in (?) 
     and contextid = ? 
    ) 
WHERE 
    rownum <= 10; 

आँकड़े:: एक विशिष्ट contextid के लिए, हम इस नाटकीय रूप से तेजी से निष्पादित कर सकते हैं

|-------------------------------------------------------------------------------------------------------------------------| 
|| Id | Operation       | Name     | Starts | E-Rows | A-Rows | A-Time | Buffers || 
|-------------------------------------------------------------------------------------------------------------------------| 
|| 0 | SELECT STATEMENT     |       |  1 |  |  10 |00:00:00.01 |  14 || 
||* 1 | COUNT STOPKEY      |       |  1 |  |  10 |00:00:00.01 |  14 || 
|| 2 | PARTITION RANGE SINGLE   |       |  1 |  10 |  10 |00:00:00.01 |  14 || 
|| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| DAILYLOGDATA   |  1 |  10 |  10 |00:00:00.01 |  14 || 
||* 4 |  INDEX RANGE SCAN    | DAILYLOGDATA_URLCONTEXT |  1 |  1 |  10 |00:00:00.01 |  5 || 
|-------------------------------------------------------------------------------------------------------------------------| 
|                               | 
|Predicate Information (identified by operation id):                  | 
|---------------------------------------------------                  | 
|                               | 
| 1 - filter(ROWNUM<=10)                        | 
| 4 - access("SHORTURL"=:P1 AND "CONTEXTID"=TO_NUMBER(:P2))                | 
|                               | 
+-------------------------------------------------------------------------------------------------------------------------+ 

इस उदाहरण में, ओरेकल चालाक है 10 परिणाम प्राप्त करने के बाद डेटा पुनर्प्राप्त करना बंद करने के लिए पर्याप्त है। I संदर्भों का एक पूरा सेट इकट्ठा कर सकता है और प्रोग्रामिक रूप से एक क्वेरी उत्पन्न करता है जिसमें प्रत्येक संदर्भ के लिए इस क्वेरी के एक उदाहरण और union all पूरी गड़बड़ी होती है, लेकिन संदर्भित संख्याओं को देखते हुए, हम आंतरिक ओरेकल सीमा में भाग ले सकते हैं, और भले ही नहीं, यह दृष्टिकोण kludge reeks।

क्या किसी को ऐसे प्रश्न के बारे में पता है जो पहली क्वेरी की सादगी को बनाए रखता है, जबकि दूसरी क्वेरी के साथ प्रदर्शन को बनाए रखने के दौरान? यह भी ध्यान रखें कि मुझे वास्तव में पंक्तियों के स्थिर सेट को पुनर्प्राप्त करने की परवाह नहीं है; जब तक वे मेरे मानदंडों को पूरा करते हैं, वे एक रिग्रेशन के प्रयोजनों के लिए ठीक हैं।

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

|-------------------------------------------------------------------------------------------------------------------------------------------------| 
|| Id | Operation      | Name    | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem || 
|-------------------------------------------------------------------------------------------------------------------------------------------------| 
|| 0 | SELECT STATEMENT    |     |  1 |  | 12624 |00:00:22.34 | 1186K| 931K|  |  |   || 
||* 1 | VIEW       |     |  1 | 1163K| 12624 |00:00:22.34 | 1186K| 931K|  |  |   || 
||* 2 | WINDOW NOSORT    |     |  1 | 1163K| 1213K|00:00:21.82 | 1186K| 931K| 3036M| 17M|   || 
|| 3 | TABLE ACCESS BY INDEX ROWID| TWTEST   |  1 | 1163K| 1213K|00:00:20.41 | 1186K| 931K|  |  |   || 
||* 4 |  INDEX RANGE SCAN   | TWTEST_URLCONTEXT |  1 | 1163K| 1213K|00:00:00.81 | 8568 |  0 |  |  |   || 
|-------------------------------------------------------------------------------------------------------------------------------------------------| 
|                                     | 
|Predicate Information (identified by operation id):                        | 
|---------------------------------------------------                        | 
|                                     | 
| 1 - filter("RN"<=10)                               | 
| 2 - filter(ROW_NUMBER() OVER (PARTITION BY "CONTEXTID" ORDER BY NULL)<=10)                 | 
| 4 - access("SHORTURL"=:P1)                             | 
+-------------------------------------------------------------------------------------------------------------------------------------------------+ 

मैं एडम के सुझाव थोड़ा नीचे उबलते की स्वतंत्रता लिया है; यहाँ संशोधित क्वेरी ...

select 
    * 
from 
    twtest 
where 
    rowid in (
    select 
      rowid 
    from (
      select 
        rowid, 
        shorturl, 
        row_number() over (partition by shorturl, contextid 
                 order by null) rn 
      from 
        twtest 
    ) 
    where rn <= 10 
    and shorturl in (?) 
); 

है ... और उसके (कैश्ड) मूल्यांकन से आंकड़े:

|--------------------------------------------------------------------------------------------------------------------------------------| 
|| Id | Operation     | Name    | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem || 
|--------------------------------------------------------------------------------------------------------------------------------------| 
|| 0 | SELECT STATEMENT   |     |  1 |  | 12624 |00:00:01.33 | 19391 |  |  |   || 
|| 1 | NESTED LOOPS    |     |  1 |  1 | 12624 |00:00:01.33 | 19391 |  |  |   || 
|| 2 | VIEW      | VW_NSO_1   |  1 | 1163K| 12624 |00:00:01.27 | 6770 |  |  |   || 
|| 3 | HASH UNIQUE    |     |  1 |  1 | 12624 |00:00:01.27 | 6770 | 1377K| 1377K| 5065K (0)|| 
||* 4 |  VIEW     |     |  1 | 1163K| 12624 |00:00:01.25 | 6770 |  |  |   || 
||* 5 |  WINDOW NOSORT   |     |  1 | 1163K| 1213K|00:00:01.09 | 6770 | 283M| 5598K|   || 
||* 6 |  INDEX RANGE SCAN  | TWTEST_URLCONTEXT |  1 | 1163K| 1213K|00:00:00.40 | 6770 |  |  |   || 
|| 7 | TABLE ACCESS BY USER ROWID| TWTEST   | 12624 |  1 | 12624 |00:00:00.04 | 12621 |  |  |   || 
|--------------------------------------------------------------------------------------------------------------------------------------| 
|                                  | 
|Predicate Information (identified by operation id):                     | 
|---------------------------------------------------                     | 
|                                  | 
| 4 - filter("RN"<=10)                            | 
| 5 - filter(ROW_NUMBER() OVER (PARTITION BY "SHORTURL","CONTEXTID" ORDER BY NULL NULL)<=10)          | 
| 6 - access("SHORTURL"=:P1)                           | 
|                                  | 
|Note                                 | 
|-----                                 | 
| - dynamic sampling used for this statement (level=2)                    | 
|                                  | 
+--------------------------------------------------------------------------------------------------------------------------------------+ 

के रूप में विज्ञापित, हम केवल पूरी तरह से फिल्टर्ड पंक्तियों के लिए dailylogdata मेज तक पहुँच रहे हैं ।मुझे चिंतित है कि प्रतीत होता है कि अभी भी urlcontext अनुक्रमणिका का पूर्ण स्कैन कर रहा है, जो कि पंक्तियों की संख्या (1213 के) के आधार पर है, लेकिन यह देखते हुए कि यह केवल 6770 बफर का उपयोग कर रहा है (यह संख्या स्थिर भी है अगर मैं संदर्भ-विशिष्ट परिणामों की संख्या बढ़ाता हूं) यह भ्रामक हो सकता है।

+0

क्या आपने अन्य तरीकों/प्रश्नों को आजमाया है जो एक ही परिणाम (अधिकतम-एन-प्रति-समूह) प्राप्त करते हैं? –

+0

आपकी दूसरी क्वेरी को "पहली 10 पंक्तियां" नहीं मिलती हैं, यह केवल 10 पंक्तियां, अर्ध-यादृच्छिक रूप से प्राप्त होती है। इसे पहली क्वेरी के एक विभाजन के बराबर करने के लिए, आपको आंतरिक क्वेरी में 'ऑर्डर बाय' जोड़ना होगा। अधिकांश भाग के लिए – Allan

+0

@ypercube मैंने इसी तरह के प्रदर्शन परिणामों के साथ अन्य विश्लेषणात्मक कार्यों की कोशिश की है। मैंने कुछ और गूढ़ सामानों की कोशिश की [जैसे] [http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions056.htm#sthref1389) फ़ंक्शन जो मुझे आवश्यकतानुसार कई पंक्तियों को वापस नहीं करेगा , लेकिन मैंने सोचा कि यह इस क्वेरी से बेहतर प्रदर्शन कर सकता है। अन्य विश्लेषणात्मक कार्यों की तरह, यह आवश्यकतानुसार इंडेक्स से अधिक स्कैन किया गया है, हालांकि। – Trevor

उत्तर

4

यह एक janky समाधान की तरह है, लेकिन यह क्या लगता है आप चाहते हैं: जितनी जल्दी हो सके इंडेक्स स्कैन को शॉर्टकट करें, और तब तक डेटा न पढ़ें जब तक कि कंडीशनिंग और टॉप-एन क्वेरी मानदंड फ़िल्टर करके दोनों योग्यता प्राप्त न हो जाएं।

ध्यान दें कि यह shorturl = स्थिति के साथ परीक्षण किया गया था, shorturl IN स्थिति नहीं।

with rowid_list as 
(select rowid 
    from (select * 
      from (select rowid, 
         row_number() over (partition by shorturl, contextid 
              order by null) rn 
        from dailylogdata 
       ) 
      where rn <= 10 
     ) 
    where shorturl = ? 
) 
select * 
    from dailylogdata 
where rowid in (select rowid from rowid_list) 

with खंड shorturl और contextid कि आपके मानदंडों को पूरा करती से प्रत्येक अद्वितीय संयोजन के लिए एक खिड़की NOSORT छानने पहले 10 rowids पकड़ लेता है। फिर यह पंक्तियों के उस सेट पर लूप करता है, प्रत्येक को पंक्तिबद्ध द्वारा लाता है।

---------------------------------------------------------------------------------------------------- 
| Id | Operation     | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |      |  1 | 286 | 1536 (1)| 00:00:19 | 
| 1 | NESTED LOOPS    |      |  1 | 286 | 1536 (1)| 00:00:19 | 
| 2 | VIEW      | VW_NSO_1    | 136K| 1596K| 910 (1)| 00:00:11 | 
| 3 | HASH UNIQUE    |      |  1 | 3326K|   |   | 
|* 4 |  VIEW     |      | 136K| 3326K| 910 (1)| 00:00:11 | 
|* 5 |  WINDOW NOSORT   |      | 136K| 2794K| 910 (1)| 00:00:11 | 
|* 6 |  INDEX RANGE SCAN  | TABLE_REDACTED_INDEX | 136K| 2794K| 910 (1)| 00:00:11 | 
| 7 | TABLE ACCESS BY USER ROWID| TABLE_REDACTED  |  1 | 274 |  1 (0)| 00:00:01 | 
---------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    4 - filter("RN"<=10) 
    5 - filter(ROW_NUMBER() OVER (PARTITION BY "CLIENT_ID","SCE_ID" ORDER BY NULL NULL 
      )<=10) 
    6 - access("TABLE_REDACTED"."SHORTURL"=:b1) 
+0

मेरा मानना ​​है कि यह समाधान कुछ अच्छा है –

+0

एक अच्छा समाधान की तरह दिखता है, और यह मेरी शुरुआती क्वेरी को काफी हद तक बेहतर बनाता है। मैंने पुराने प्रश्न के प्रदर्शन माप और आपके थोड़ा-संशोधित संस्करण के साथ प्रश्न अपडेट किया है। मैं मदद की सराहना करता हूं! – Trevor

0

ऐसा लगता है कि यह हर समय ले रहा है। occurrenceDate आपकी क्लस्टर इंडेक्स है, और यदि नहीं, तो क्या आप अपने क्लस्टर इंडेक्स द्वारा ऑर्डर करने के लिए बदलते हैं? अर्थात। अगर यह अनुक्रमिक आईडी द्वारा क्लस्टर किया गया है, तो उसके द्वारा ऑर्डर करें।

+0

मैं असहमत हूं, इंडेक्स में पहली कुंजी फ़ील्ड होना चाहिए जिसे 'संदर्भित' द्वारा विभाजित किया जा रहा है, फिर ऑर्डर कॉलम 'घटना दिनांक' - यह वह क्रम है जिसमें डेटा को –

+0

@JCooper तक एक्सेस करने की आवश्यकता नहीं है यहां इंडेक्स को बदलने का सुझाव है, बस क्वेरी बदल रहा है। और यदि यह लॉग डेटा है, तो आप 'contextid' द्वारा क्लस्टर नहीं करना चाहते हैं क्योंकि इंडेक्स को तेजी से INSERT के लिए क्रोनोलॉजिकल होना आवश्यक है। –

+0

@ टिमरोगर्स इस तालिका में क्लस्टर्ड इंडेक्स नहीं है (और मैं पहले शब्द से अपरिचित था), हालांकि जो भी मैं पढ़ रहा हूं, उससे वे मदद कर सकते हैं। इसे घटित कॉलम पर एक इंडेक्स-संगठित-तालिका के रूप में परिवर्तित करने से एक ही लक्ष्य पूरा हो जाएगा, सही? मैं जिस मौजूदा इंडेक्स का उपयोग कर रहा हूं वह चालू है (शॉर्टरल, कॉन्फिडिड), जिसे मैं बदलने के लिए खुला हूं। मेरी चिंता यह है कि row_number फ़ंक्शन के उपयोग के कारण ओरेकल डेटा संरचना के बावजूद 10 प्रति संदर्भित चयन करने के बाद इसे छोड़ने के बारे में अभी भी नहीं जान सकता है। मैं यह देखने के लिए कुछ परीक्षण करूंगा कि इससे मदद मिलती है या नहीं। – Trevor

0

पिछली बार मैंने अंतिम को एक छोटी तालिका में सबसे रोचक पंक्तियों को कैश किया था। मेरे डेटा वितरण के साथ थोक तालिका पूछने के बजाय प्रत्येक डालने पर कैश तालिका को अपडेट करना सस्ता था।

0

मुझे लगता है कि आपको एक ही परिणाम सेट प्राप्त करने के लिए अन्य तरीकों/प्रश्नों की जांच करनी चाहिए।


Self-JOIN/GROUP BY

SELECT 
    d.* 
    , COUNT(*) AS rn 

FROM 
     dailylogdata AS d 
    LEFT OUTER JOIN 
     dailylogdata AS d2 
      ON d.contextid = d2.contextid 
      AND d.occurrencedate >= d2.occurrencedate) 
      AND d2.shorturl IN (?) 

WHERE 
    d.shorturl IN (?) 

GROUP BY 
    d.* 

HAVING 
    COUNT(*) <= 10 

और एक और एक है जो मुझे पता नहीं है अगर इसे सही ढंग से काम करता है:

SELECT 
    d.* 
    , COUNT(*) AS rn 

FROM 
     (SELECT DISTINCT 
       contextid 
      FROM 
       dailylogdata 
      WHERE 
       shorturl IN (?) 
     ) AS dd 
    JOIN 
     dailylogdata AS d 
      ON d.PK IN 
       (SELECT 
         d10.PK 
        FROM 
         dailylogdata AS d10 
        WHERE 
         d10.contextid = dd.contextid 
        AND 
         d10.shorturl IN (?) 
        AND 
         rownum <= 10 
        ORDER BY 
         d10.occurrencedate 
       ) 
+0

वह स्वयं-जुड़ना वास्तव में एक बुरा विचार है। यह प्रत्येक पंक्ति में प्रत्येक पंक्ति में शामिल होगा जो इसका अनुसरण करता है। उसी 'संदर्भ' के साथ 100 पंक्तियों के परिणामस्वरूप 4900 पंक्तियां परिणाम सेट में होंगी। – Allan

+0

@ एलन: मैं 100% सहमत हूं। तालिका में कई अलग-अलग संदर्भ हैं (प्रति पंक्ति कुछ पंक्तियां) जब स्व-जॉइन बेहतर काम करता है। –

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