2009-02-04 8 views
34

मैं (मैं इस डिजाइन नहीं था) कैसे MySQL में एक बहुत ही धीमी गति से क्वेरी अनुकूलन करने के लिए यह पता लगाने की कोशिश कर रहा हूँ के साथ:"COUNT का चयन करें (*)" धीमी है, यहां तक ​​कि जहां खंड

SELECT COUNT(*) FROM change_event me WHERE change_event_id > '1212281603783391'; 
+----------+ 
| COUNT(*) | 
+----------+ 
| 3224022 | 
+----------+ 
1 row in set (1 min 0.16 sec) 

तुलना है कि एक पूर्ण गिनती के लिए:

select count(*) from change_event; 
+----------+ 
| count(*) | 
+----------+ 
| 6069102 | 
+----------+ 
1 row in set (4.21 sec) 

बयान की व्याख्या मुझे यहाँ मदद नहीं करता है:

explain SELECT COUNT(*) FROM change_event me WHERE change_event_id > '1212281603783391'\G 
*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: me 
     type: range 
possible_keys: PRIMARY 
      key: PRIMARY 
     key_len: 8 
      ref: NULL 
     rows: 4120213 
     Extra: Using where; Using index 
1 row in set (0.00 sec) 

ठीक है, यह अभी भी सोचता है कि यह मोटे तौर पर 4 लाख प्रविष्टियों की जरूरत है गिनती करने के लिए, बू मैं उस फ़ाइल से लाइनों को तेज़ी से गिन सकता हूं! मुझे समझ में नहीं आता क्यों MySQL यह लंबा ले रहा है।

यहाँ तालिका परिभाषा है:

CREATE TABLE `change_event` (
    `change_event_id` bigint(20) NOT NULL default '0', 
    `timestamp` datetime NOT NULL, 
    `change_type` enum('create','update','delete','noop') default NULL, 
    `changed_object_type` enum('Brand','Broadcast','Episode','OnDemand') NOT NULL, 
    `changed_object_id` varchar(255) default NULL, 
    `changed_object_modified` datetime NOT NULL default '1000-01-01 00:00:00', 
    `modified` datetime NOT NULL default '1000-01-01 00:00:00', 
    `created` datetime NOT NULL default '1000-01-01 00:00:00', 
    `pid` char(15) default NULL, 
    `episode_pid` char(15) default NULL, 
    `import_id` int(11) NOT NULL, 
    `status` enum('success','failure') NOT NULL, 
    `xml_diff` text, 
    `node_digest` char(32) default NULL, 
    PRIMARY KEY (`change_event_id`), 
    KEY `idx_change_events_changed_object_id` (`changed_object_id`), 
    KEY `idx_change_events_episode_pid` (`episode_pid`), 
    KEY `fk_import_id` (`import_id`), 
    KEY `idx_change_event_timestamp_ce_id` (`timestamp`,`change_event_id`), 
    KEY `idx_change_event_status` (`status`), 
    CONSTRAINT `fk_change_event_import` FOREIGN KEY (`import_id`) REFERENCES `import` (`import_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

संस्करण:

$ mysql --version 
mysql Ver 14.12 Distrib 5.0.37, for pc-solaris2.8 (i386) using readline 5.0 

वहाँ कुछ स्पष्ट मैं याद कर रहा हूँ है? (हां, मैंने पहले ही "SELECT COUNT (change_event_id)" का प्रयास किया है, लेकिन इसमें कोई प्रदर्शन अंतर नहीं है)।

+0

अगर आप कुछ ऐसा करने की कोशिश करते हैं तो कैसे ... चुनें COUNT (*) परिवर्तन से मुझे बदलें_event_id> 0; क्या यह प्रदर्शन को प्रभावित करता है? –

+0

ओविड - यदि आप सक्षम हैं, तो कृपया 'SHOW INDEX FROM change_event' के आउटपुट को जोड़ें – Alnitak

उत्तर

39

InnoDB पर एक नज़र डालें संकुल प्राथमिक कुंजी का उपयोग करता है, तो प्राथमिक कुंजी डेटा पृष्ठों, अलग सूचकांक पन्नों में नहीं में पंक्ति के साथ साथ संग्रहीत किया जाता है। एक श्रेणी स्कैन करने के लिए आपको अभी भी डेटा पृष्ठों में संभावित रूप से विस्तृत पंक्तियों के माध्यम से स्कैन करना होगा; ध्यान दें कि इस तालिका में एक टेक्स्ट कॉलम है।

दो बातें मैं कोशिश करेंगे:

  1. रन optimize table। यह सुनिश्चित करेगा कि डेटा पेज क्रमबद्ध क्रम में भौतिक रूप से संग्रहीत हैं। यह एक क्लस्टर प्राथमिक कुंजी पर एक रेंज स्कैन की कल्पना कर सकता है।
  2. केवल change_event_id कॉलम पर एक अतिरिक्त गैर-प्राथमिक अनुक्रमणिका बनाएं। यह इंडेक्स पृष्ठों में उस कॉलम की एक प्रति स्टोर करेगा जो स्कैन करने के लिए बहुत तेज़ होगा। इसे बनाने के बाद, यह सुनिश्चित करने के लिए कि यह नई अनुक्रमणिका का उपयोग कर रहा है, समझाएं योजना की जांच करें।

(आप भी शायद change_event_id स्तंभ bigint अहस्ताक्षरित बनाना चाहते हैं तो यह शून्य से incrementing है)

+5

"ऑप्टिमाइज़ टेबल" ने बहुत मदद नहीं की, लेकिन अनावश्यक सूचकांक ने हल किया मुसीबत। धन्यवाद! – Ovid

+12

यह पहली बार है जब मैंने कभी किसी को किसी प्राथमिक कुंजी कॉलम पर MySQL में प्रदर्शन हैक के रूप में एक अनावश्यक अनुक्रमणिका बनाने का सुझाव दिया है। मुझे यह बताने में बहुत दिलचस्पी है कि यह क्यों काम करता है और किस तरह के प्रश्नों के लिए यह उपयोगी है। क्या आपके पास विषय पर आगे पढ़ने के लिए कोई लिंक है? –

+0

'ऑप्टिमाइज़ टेबल' शायद ही कभी उपयोग में है, खासकर इनो डीबी टेबल पर। कोई भी सुधार _may_ क्योंकि आप पूरी तालिका को कैश में ताज़ा रूप से लोड कर चुके हैं। –

1

उस तालिका पर "analyze table_name" चलाएं - यह संभव है कि सूचकांक अब इष्टतम नहीं हैं।

आप इसे "show index from table_name" चलाकर अक्सर बता सकते हैं। यदि कार्डिनालिटी मान NULL है तो आपको पुनः विश्लेषण करने की आवश्यकता है।

+0

"तालिका परिवर्तन_विवेंट का विश्लेषण करें" प्रदर्शन पर कोई प्रभाव नहीं पड़ा। हालांकि धन्यवाद। – Ovid

+0

क्या यह सादा "चुनिंदा गिनती (*)" किसी भी तेज बनाता है? मैंने अभी 110 एम रिकॉर्ड माईसाम टेबल पर कोशिश की है। "गिनती चुनें (*)" तत्काल था।~ आधा टेबल के लिए गिनती का चयन पहली बार 2 एम 48 और दूसरी बार 27s लिया। – Alnitak

+2

माईसाम में InnoDB से मूल रूप से विभिन्न प्रदर्शन विशेषताएं हैं। ऐसा इसलिए है क्योंकि माईसाम तालिका स्तर लॉकिंग करता है और प्रभावी रूप से एक समय में केवल एक लेनदेन होता है। InnoDB कवर के तहत बहुत अलग व्यवहार करता है। – Ovid

3

यह देखने के लिए जांचें कि आपके इंडेक्स कितने खंडित हैं। मेरी कंपनी में हमारे पास एक रात्रि आयात प्रक्रिया है जो हमारी अनुक्रमणिका को मिटा देती है और समय के साथ डेटा पहुंच गति पर इसका गहरा असर पड़ सकता है। उदाहरण के लिए हमारे पास एक SQL प्रक्रिया थी जिसमें इंडेक्स को 3 मिनट लगने के बाद एक दिन चलाने के लिए 2 घंटे लग गए। हम एक स्क्रिप्ट के लिए SQL Server 2005 बीमार देखो का उपयोग करते हैं जो इसे MySQL पर देख सकता है।

अद्यतन: http://dev.mysql.com/doc/refman/5.0/en/innodb-file-defragmenting.html

+0

यहां एक लिंक है http://dev.mysql.com/doc/refman/5.0/en/innodb-file-defragmenting.html सब कुछ के साथ शुभकामनाएं –

+0

आप शायद उस लिंक को अपने उत्तर में रखना चाहेंगे? – MiniQuark

5

मैं IP भौगोलिक डेटाबेस के साथ पहले इस तरह व्यवहार किया है: इस लिंक की जाँच करें। पिछले कुछ रिकॉर्डों के पीछे, माईएसक्यूएल की रेंज-आधारित क्वेरी के लिए इंडेक्स से कोई फायदा पाने की क्षमता स्पष्ट रूप से वाष्पित हो जाती है। भौगोलिक स्थान डीबी के साथ, हमने डेटा को उन हिस्सों में विभाजित करके संभाला जो सूचकांक का उपयोग करने के लिए पर्याप्त उचित थे।

+0

क्या एक बुरा समाधान है। फिर भी, मैंने इसे पहले लाया और कुछ अजीब कॉन्फ़िगरेशन फ़िक्स या अन्य समाधान को छोड़कर, हमें इस मार्ग पर जाने के लिए मजबूर होना पड़ सकता है :( – Ovid

+0

यह एक अच्छा समाधान है जो कंप्यूटर समाधान के मूल सिद्धांत का सम्मान करता है: प्रोग्रामिंग में बड़े पैमाने पर गुणात्मक रूप से होता है प्रोग्रामिंग के मामले में अलग-अलग। डाटाबेस के मामले में, एक्सेस प्लान और इंडेक्स का उपयोग नाटकीय रूप से बदलता है क्योंकि कुछ थ्रेसहोल्ड के आकार में वृद्धि बढ़ जाती है। –

+0

मैं भौगोलिक स्थान डेटाबेस के साथ एक ही समस्या में आया, और इंडेक्सिंग जैसे विभिन्न ऑप्टिमाइज़ेशन प्रयासों के बाद , विभाजन आदि। मैंने छोटे टेबल को बड़े डेटासेट में विभाजित करने के लिए एक शॉट दिया, जो अंततः प्रदर्शन के संदर्भ में स्वीकार्य साबित हुआ। – shashi009

0

मैं एक "काउंटर" तालिका बनाउंगा और उस तालिका में "पंक्ति बनाएं"/"पंक्ति हटाएं" ट्रिगर्स जोड़ूंगा जो आप गिन रहे हैं। ट्रिगर्स को प्रत्येक डालने/हटाने पर "काउंटर" तालिका पर गिनती मानों को बढ़ाने/घटाना चाहिए, इसलिए आपको उन्हें हर बार उनकी गणना करने की आवश्यकता नहीं होगी।

आप इसे काउंटर को कैश करके एप्लिकेशन पक्ष पर भी पूरा कर सकते हैं लेकिन इसमें प्रत्येक सम्मिलन/हटाना पर "काउंटर कैश" को साफ़ करना शामिल होगा।

कुछ संदर्भ के लिए इस http://pure.rednoize.com/2007/04/03/mysql-performance-use-counter-tables/

+0

सिवाय इसके कि हमें श्रेणियों पर गिनती की आवश्यकता है, इसलिए ट्रिगर्स के माध्यम से एक गणना का प्रबंधन काम नहीं करता है (जब तक कि मैं आपको गलत समझा गया है) – Ovid

14

यहाँ कुछ चीजें मैं सुझाव दिए गए हैं:

  • बदलें स्तंभ एक से एक "int unsigned" के लिए "bigint"। क्या आप वास्तव में इस तालिका में 4.2 अरब से अधिक रिकॉर्ड होने की उम्मीद करते हैं? यदि नहीं, तो आप अंतरिक्ष (और समय) अतिरिक्त चौड़ा क्षेत्र बर्बाद कर रहे हैं। MySQL अनुक्रमणिका छोटे डेटा प्रकारों पर अधिक कुशल हैं।

  • "OPTIMIZE TABLE" आदेश चलाएं, और देखें कि आपकी क्वेरी बाद में तेज है या नहीं।

  • आईडी फ़ील्ड के अनुसार आप partitioning your table पर भी विचार कर सकते हैं, खासकर यदि पुराने रिकॉर्ड (कम आईडी मानों के साथ) समय के साथ कम प्रासंगिक हो जाते हैं। एक विभाजित तालिका अक्सर एक विशाल, अविभाजित तालिका की तुलना में कुल प्रश्नों को तेजी से निष्पादित कर सकती है।


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

इस तालिका में और अधिक बारीकी से देख रहे हैं, यह एक प्रवेश शैली मेज, जहां पंक्तियों डाला जाता है, लेकिन कभी संशोधित तरह दिखता है।

यदि यह सच है, तो आपको इनो डीबी स्टोरेज इंजन द्वारा प्रदान की जाने वाली सभी लेनदेन सुरक्षा की आवश्यकता नहीं हो सकती है, और आप switching to MyISAM से दूर हो सकते हैं, जो कुल प्रश्नों पर काफी अधिक कुशल है।

+1

यह देखते हुए कि हमारे पास "1212281603783397" जैसी संख्याएं हैं, मुझे लगता है कि पहले से ही "int unsigned" अतिप्रवाह है (यह एक उच्च-रेज टाइमस्टैम्प है)। "अनुकूलन टेबल" कोई प्रदर्शन प्रभाव :( "कहाँ" खंड एक मेज स्कैन के बाद से यह करने की जरूरत है? इसके अलावा, हम हमारे FK बाधा खो देंगे साथ बहुत धीमी MyISAM नहीं है था। – Ovid

+0

क्यों के लिए एक टाइमस्टैम्प का उपयोग आपकी प्राथमिक कुंजी, यदि आपके पास पहले से टाइमस्टैम्प फ़ील्ड है? इसके अलावा, क्या होता है यदि दो घटनाएं एक ही पल में होती हैं? अगर मैं आप थे, तो मैं पाकी के लिए एक सरल ऑटो-वृद्धि फ़ील्ड का उपयोग करूंगा। – benjismith

+0

WHERE क्लॉज ' टी एक पूर्ण तालिका स्कैन का कारण बनता है। अनुक्रमित कॉलम पर एक साधारण क्वेरी (बराबर, कम से कम, अधिक से अधिक, आदि) के लिए, क्वेरी ऑप्टिमाइज़र संबंधित पृष्ठों को खोजने के लिए अनुक्रमणिका का उपयोग करता है, और उसके बाद केवल उन पृष्ठों को स्कैन करता है। यदि आप डेट-मैथ या सबस्ट्रिंग कर रहे थे तो 0 की आवश्यकता होगी। – benjismith

1

MySQL "कहां उपयोग कर रहा है" कहता है, क्योंकि इसे इंडेक्स डेटा से सभी रिकॉर्ड्स/मानों को वास्तव में गिनने के लिए पढ़ने की आवश्यकता होती है। इनो डीबी के साथ यह गिनने के लिए 4 मिलियन रिकॉर्ड रेंज को "पकड़ने" की भी कोशिश करता है।

आप अलग अलग लेन-देन अलगाव के स्तर के साथ प्रयोग करने के लिए आवश्यकता हो सकती है: http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html#isolevel_read-uncommitted

और देखो, जो एक बेहतर है।

माईसाम के साथ यह तेज़ होगा, लेकिन गहन लेखन मॉडल के साथ लॉक मुद्दे होंगे।

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