2012-03-01 5 views
20

मेरे पास लगभग 120k पंक्तियों वाली एक तालिका है, जिसमें एक बीएलओबी वाला क्षेत्र होता है (आकार में प्रत्येक प्रविष्टि में 1 एमबी से अधिक नहीं, आमतौर पर बहुत कम)। मेरी समस्या यह है कि जब भी मैं इस तालिका पर किसी भी कॉलम से पूछताछ करता हूं ( बीएलओबी एक सहित), यदि फाइल सिस्टम कैश खाली है, तो इसे पूरा करने में लगभग 40 'लगते हैं। उसी तालिका पर आने वाले सभी प्रश्नों को 1 से कम '(' कमांड लाइन क्लाइंट से परीक्षण, सर्वर पर ही) की आवश्यकता होती है। प्रश्नों में लौटाई गई पंक्तियों की संख्या एक खाली सेट से 60k +ब्लॉब युक्त टेबल पर mysql क्वेरी की गति फाइल सिस्टम कैश पर निर्भर करती है

मैंने क्वेरी कैश को समाप्त कर दिया है, इसलिए इसका इसके साथ कुछ लेना देना नहीं है। तालिका मायिसम है लेकिन मैंने इसे innodb (और ROW_FORMAT = COMPACT सेट करने) में बदलने की भी कोशिश की, लेकिन बिना किसी किस्मत के।

यदि मैं बीएलओबी कॉलम को हटा देता हूं, तो क्वेरी हमेशा तेज होती है।

तो मुझे लगता है कि सर्वर डिस्क (या उनमें से कुछ हिस्सों) से ब्लब्स पढ़ता है और फाइल सिस्टम उन्हें कैश करता है। समस्या यह है कि उच्च यातायात और सीमित स्मृति वाले सर्वर पर, फाइल सिस्टम कैश को थोड़ी देर में हर बार ताज़ा किया जाता है, इसलिए यह विशेष क्वेरी मुझे परेशानी पैदा करती है।

तो मेरा सवाल यह है कि, तालिका से ब्लॉब कॉलम को हटाए बिना चीजों को काफी तेज करने का कोई तरीका है?

mysql> SELECT ct.score FROM completed_tests ct where ct.status != 'deleted' and ct.status != 'failed' and score < 100; 
Empty set (48.21 sec) 
mysql> SELECT ct.score FROM completed_tests ct where ct.status != 'deleted' and ct.status != 'failed' and score < 99; 
Empty set (1.16 sec) 

mysql> explain SELECT ct.score FROM completed_tests ct where ct.status != 'deleted' and ct.status != 'failed' and score < 99; 
+----+-------------+-------+-------+---------------+--------+---------+------+-------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+--------+---------+------+-------+-------------+ 
| 1 | SIMPLE  | ct | range | status,score | status | 768  | NULL | 82096 | Using where | 
+----+-------------+-------+-------+---------------+--------+---------+------+-------+-------------+ 
1 row in set (0.00 sec) 


mysql> show indexes from completed_tests; 
+-----------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-----------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| completed_tests |   0 | PRIMARY  |   1 | id   | A   |  583938 |  NULL | NULL |  | BTREE  |   | 
| completed_tests |   1 | users_login |   1 | users_LOGIN | A   |  11449 |  NULL | NULL | YES | BTREE  |   | 
| completed_tests |   1 | tests_ID |   1 | tests_ID | A   |   140 |  NULL | NULL |  | BTREE  |   | 
| completed_tests |   1 | status  |   1 | status  | A   |   3 |  NULL | NULL | YES | BTREE  |   | 
| completed_tests |   1 | timestamp |   1 | timestamp | A   |  291969 |  NULL | NULL |  | BTREE  |   | 
| completed_tests |   1 | archive  |   1 | archive  | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| completed_tests |   1 | score  |   1 | score  | A   |   783 |  NULL | NULL | YES | BTREE  |   | 
| completed_tests |   1 | pending  |   1 | pending  | A   |   1 |  NULL | NULL |  | BTREE  |   | 
+-----------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

mysql> show create table completed_tests; 
+-----------------+-------------------------------------- 
| Table   | Create Table                                                                                                                                                                                                                      | 
+-----------------+-------------------------------------- 
| completed_tests | CREATE TABLE `completed_tests` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, 
    `users_LOGIN` varchar(100) DEFAULT NULL, 
    `tests_ID` mediumint(8) unsigned NOT NULL DEFAULT '0', 
    `test` longblob, 
    `status` varchar(255) DEFAULT NULL, 
    `timestamp` int(10) unsigned NOT NULL DEFAULT '0', 
    `archive` tinyint(1) NOT NULL DEFAULT '0', 
    `time_start` int(10) unsigned DEFAULT NULL, 
    `time_end` int(10) unsigned DEFAULT NULL, 
    `time_spent` int(10) unsigned DEFAULT NULL, 
    `score` float DEFAULT NULL, 
    `pending` tinyint(1) NOT NULL DEFAULT '0', 
    PRIMARY KEY (`id`), 
    KEY `users_login` (`users_LOGIN`), 
    KEY `tests_ID` (`tests_ID`), 
    KEY `status` (`status`), 
    KEY `timestamp` (`timestamp`), 
    KEY `archive` (`archive`), 
    KEY `score` (`score`), 
    KEY `pending` (`pending`) 
) ENGINE=InnoDB AUTO_INCREMENT=117996 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED 
1 row in set (0.00 sec) 

मैं मूल रूप से mysql query slow at first fast afterwards पर इस पोस्ट, लेकिन मैं अब और अधिक जानकारी नहीं है तो मैं एक अलग रूप में repost:

यहाँ

2 उदाहरण प्रश्न है, के साथ साथ समझाने, अनुक्रमित और तालिका परिभाषा, एक के बाद एक भाग गया सवाल मैं भी mysql forum पर इस पोस्ट, लेकिन मैं हमेशा

+4

+1 अच्छी तरह से लिखित, पूर्ण प्रश्न। मुझे आशा है कि आपको एक अच्छा जवाब मिलेगा (मुझे नहीं मिला ':-) अगर आपको MySQL फोरम पर कोई जवाब मिलता है और कोई भी यहां जवाब नहीं देता है, तो कृपया उत्तर पोस्ट करें (नीचे दिए गए उत्तर के रूप में), आवश्यक 48 घंटों तक प्रतीक्षा करें, और फिर इसे स्वीकार करें । आपको अंक नहीं मिलेगा लेकिन यह इस विषय पर खोज करने वाले अन्य लोगों के लिए एक उत्तर प्रश्न के रूप में दिखाई देगा। सौभाग्य। –

+0

"क्यों" भाग का जवाब नहीं दे सकता। मैं सुझाव दे सकता हूं कि आपको इसकी परवाह नहीं है। ठीक है, आप जानते हैं कि आपकी पहली क्वेरी धीमी है। तो क्या? जैसा कि मैंने समझा, कोई भी बाद के प्रश्न तेजी से हैं। तो यह जानकर कि इस तथ्य को जानने के लिए अपना आवेदन बनाएं। किसी बिंदु पर "वार्मअप" चरण जोड़ें, उदाहरण के लिए जब ग्राहक को लॉगिन फ़ॉर्म में अक्षर टाइप करना होगा। और इस तरह ... कैश सेटिंग्स के साथ खेलने से बेहतर है। –

+0

और यहां एक उदाहरण दिया गया है कि Google एक ही समस्या को हल करता है, [http://code.google.com/appengine/docs/adminconsole/instances.html#Warmup_Requests ](http://code.google.com/appengine/docs/ adminconsole/instances.html # Warmup_Requests)। –

उत्तर

13

मैं थोड़ी देर के लिए इस मुद्दे पर शोध कर रहा था। कई लोग ब्लॉब का उपयोग एक अलग तालिका में केवल एक प्राथमिक कुंजी के साथ करते हैं और ब्लॉब टेबल पर विदेशी कुंजी के साथ किसी अन्य तालिका में ब्लॉब्स मेटा डेटा संग्रहीत करते हैं। इसके साथ प्रदर्शन काफी अधिक होगा।

+0

हाँ, मैंने यही करने का फैसला किया है, लेकिन मैंने अभी तक असली दुनिया पर इसका परीक्षण नहीं किया है यहां पोस्ट करने के लिए परिदृश्य। – periklis

+0

हां, यही वह है जो मैंने अंततः किया था और प्रदर्शन समस्या तय की गई थी। मुझे अभी भी कोड की अनगिनत रेखाओं को संपादित करना पड़ा था, हालांकि, मैं एक शुद्ध डेटाबेस तालिका पुनर्गठन की उम्मीद कर रहा था, लेकिन यह प्रबंधित करने से कहीं अधिक कठिन हो गया। – periklis

1

दो प्रासंगिक स्तंभ पर एक समग्र सूचकांक जोड़ना रूप में पहले से कोई सूचना नहीं मिली

धन्यवाद करना चाहिए तालिका डेटा तक पहुंच के बिना इन प्रश्नों को निष्पादित करने की अनुमति दें।

CREATE INDEX `IX_score_status` ON `completed_tests` (`score`, `status`); 

यदि आप मारियाडीबी पर स्विच करने में सक्षम हैं तो आप तालिका का उन्मूलन अनुकूलन कर सकते हैं। यह आपको बीएलओबी फ़ील्ड को अपनी तालिका में विभाजित करने और बाएं जॉइन का उपयोग करके मौजूदा टेबल संरचना को फिर से बनाने के लिए एक दृश्य का उपयोग करने की अनुमति देगा। इस तरह यह केवल बीएलओबी डेटा तक पहुंच जाएगा यदि इसे निष्पादन क्वेरी के लिए स्पष्ट रूप से आवश्यक है।

+0

संयुक्त सूचकांक मदद करेगा, लेकिन मेरी वास्तविक क्वेरी मेरे द्वारा पोस्ट की गई एक से अधिक जटिल है (एक से अधिक तालिकाओं में शामिल हो रही है) इसलिए मैं ऐसे समाधान की उम्मीद कर रहा था जो इस समस्या को लक्षित करे। फिर भी, मैं इंडेक्स और क्वेरी रिफैक्टरिंग का एक उपयुक्त संयोजन खोजने पर ध्यान केंद्रित कर सकता हूं, अगर कुछ बदल जाता है तो मैं यहां पोस्ट करूंगा। मारियाडीबी के बारे में, मैंने इसके बारे में नहीं सुना था, अच्छा (लेकिन दुर्भाग्य से मेरे मामले में कोई विकल्प नहीं) +1 – periklis

14

MySQL में BLOB (= TEXT) संग्रहण का डिज़ाइन पूरी तरह से त्रुटिपूर्ण और काउंटर-अंतर्ज्ञानी प्रतीत होता है। मैं एक ही समस्या में दो बार भाग गया और कोई आधिकारिक स्पष्टीकरण खोजने में असमर्थ था। सबसे विस्तृत विश्लेषण मैं अंत में मिल गया है 2010 से इस पद है: http://www.mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb/

जनरल विश्वास और उम्मीद है कि BLOBs/ग्रंथों मुख्य पंक्ति का संग्रहण (जैसे, this answer देखें) के बाहर जमा हो जाती है है। हालांकि यह सच नहीं है। वहाँ कई मुद्दों यहाँ हैं (मैं लेख ऊपर दिए गए के आधार पर कर रहा हूँ):

  1. ब्लॉब आइटम का आकार है, तो कई KB, यह सीधे पंक्ति डेटा में शामिल है।नतीजतन, भले ही आप केवल गैर-बीएलओबी कॉलम चुनें, इंजन को अभी भी डिस्क से अपने सभी बीएलओबी लोड करना होगा। कहें, आपके पास 1 एम पंक्तियां हैं जिनमें 100 बाइट गैर-ब्लॉब डेटा और 5000 बाइट ब्लॉब डेटा हैं। आप सभी गैर-ब्लॉब कॉलम चुनें और उम्मीद करते हैं कि MySQL प्रति पंक्ति 100-120 बाइट्स डिस्क से पढ़ेगा, जो 100-120 एमबी कुल (+LOB पते के लिए +20) है। हालांकि, वास्तविकता यह है कि MySQL सभी बीएलओबी को पंक्तियों के समान डिस्क ब्लॉक में संग्रहीत करता है, इसलिए उन्हें का उपयोग नहीं किया जाना चाहिए, भले ही का उपयोग न किया जाए, और इसलिए डिस्क से पढ़ने वाले डेटा का आकार लगभग 5100 एमबी = 5 जीबी है - यह 50 गुना आपकी अपेक्षा से अधिक है और इसका मतलब है 50 गुना धीमी क्वेरी निष्पादन।

    बेशक, इस डिज़ाइन का लाभ है: जब आपको ब्लॉब समेत सभी कॉलम की आवश्यकता होती है, तो जब क्वेरी को बाहरी रूप से संग्रहीत किए जाने से पंक्ति के साथ ब्लॉब्स संग्रहीत किया जाता है तो चयन क्वेरी तेज होती है: आप (कभी-कभी) 1 अतिरिक्त पृष्ठ पहुंच से बचते हैं प्रति पंक्ति हालांकि, यह बीएलओबी और डीबी इंजन के लिए एक सामान्य उपयोग मामला नहीं है, इस मामले की ओर अनुकूलित नहीं किया जाना चाहिए। यदि आपका डेटा इतना छोटा है कि यह एक पंक्ति में फिट बैठता है और आप इसे प्रत्येक क्वेरी में लोड करने के साथ ठीक हैं, चाहे आवश्यक हो या नहीं - तो आप BLOB/टेक्स्ट के बजाय VARCHAR प्रकार का उपयोग करेंगे।

  2. यहां तक ​​कि अगर किसी कारण (लंबी पंक्ति या लंबे ब्लॉब) के लिए ब्लॉब मूल्य बाह्य संग्रहीत किया जाता है, इसके 768-बाइट उपसर्ग अभी भी पंक्ति अपने आप में रखा जाता है। आइए पिछले उदाहरण लें: आपके पास प्रत्येक पंक्ति में 100 बाइट गैर-ब्लॉब डेटा है, लेकिन अब ब्लॉब कॉलम में प्रत्येक 1 एमबी के आइटम हैं, इसलिए उन्हें बाहरी रूप से रखा जाना चाहिए। गैर-ब्लॉब कॉलम के चयन को 100-120 की बजाय लगभग 800 बाइट प्रति पंक्ति (गैर-ब्लॉब्स + ब्लॉब उपसर्ग) को पढ़ना होगा - यह फिर से 7 गुना बड़ा डिस्क स्थानांतरण आपके अपेक्षा से अधिक है, और 7x धीमा क्वेरी निष्पादन।

  3. बाहरी बीएलओबी स्टोरेज डिस्क स्पेस के उपयोग में अप्रभावी है: यह 16 केबी के ब्लॉक में स्थान आवंटित करता है और एकल ब्लॉक कई आइटम नहीं रख सकता है, इसलिए यदि आपके ब्लब्स छोटे हैं और लेते हैं, उदाहरण के लिए, 8 केबी प्रत्येक, आवंटित वास्तविक स्थान दो बार बड़ा है।

मुझे आशा है कि इस डिजाइन एक दिन तय हो जाएगी: MySQL सभी धब्बे स्टोर करेगा -, बाह्य भंडारण में बिना किसी भी उपसर्गों बाह्य भंडारण आवंटन सभी आकार के मदों के लिए कुशल होने के साथ डीबी में रखा, - बड़े और छोटे। ऐसा होने से पहले, को अलग करना BLOB/टेक्स्ट कॉलम एकमात्र उचित समाधान लगता है - किसी अन्य तालिका या फ़ाइल सिस्टम (प्रत्येक बीएलओबी मान को फ़ाइल के रूप में रखा जाता है) से अलग करना।

+0

कोई विचार अगर इस बिंदु पर उल्लिखित मुद्दों को ठीक किया गया है? –

+0

** यह सच नहीं है, हालांकि ** क्या यह इंजन विशिष्ट नहीं होगा? मुख्य दस्तावेज़/कोड केवल इंजन के भीतर आने वाले कुछ मानकों को रेखांकित करेगा। ऐसा लगता है कि 'बीएलओबी' इनलाइन डालने वाले SQL सर्वर के लिए भी संदिग्ध हो सकता है: https://dba.stackexchange.com/questions/174678/why-is-it-recommended-to-store-blobs-in-separate-sql- सर्वर-टेबल स्कीमा (जो तालिका में डेटाबेस है) को स्विच करने की क्षमता अकेले लगती है जब भी डेटा पूरी तरह से एसक्यूएल के बाहर संग्रहीत नहीं होता है। – ebyrob

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