MySQL इस आदेश के लिए बल पर भी सूचकांक की उपेक्षा क्यों करता है?


14

मैं एक चलाता हूं EXPLAIN:

mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+  
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows  | Extra          |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+  
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+  
1 row in set (0.00 sec)  

मेरी तालिका में अनुक्रमणिकाएँ:

mysql> show index from employees;  
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+  
| Table     | Non_unique | Key_name      | Seq_in_index | Column_name   | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |  
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+  
| employees |          0 | PRIMARY       |            1 | subsidiary_id | A         |           6 |     NULL | NULL   |      | BTREE      |         |               |  
| employees |          0 | PRIMARY       |            2 | employee_id   | A         |       10031 |     NULL | NULL   |      | BTREE      |         |               |  
| employees |          1 | idx_last_name |            1 | last_name     | A         |       10031 |      700 | NULL   |      | BTREE      |         |               |  
| employees |          1 | date_of_birth |            1 | date_of_birth | A         |       10031 |     NULL | NULL   | YES  | BTREE      |         |               |  
| employees |          1 | date_of_birth |            2 | subsidiary_id | A         |       10031 |     NULL | NULL   |      | BTREE      |         |               |  
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+  
5 rows in set (0.02 sec)  

Last_name पर एक इंडेक्स है लेकिन ऑप्टिमाइज़र इसका उपयोग नहीं करता है।
इसलिए मैं करता हूँ:

mysql> explain select last_name from employees force index(idx_last_name) order by last_name;  
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+  
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows  | Extra          |  
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+  
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 10031 | Using filesort |  
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+  
1 row in set (0.00 sec)  

लेकिन फिर भी सूचकांक का उपयोग नहीं किया जाता है! मुझसे यहां क्या गलत हो रहा है?
क्या इस तथ्य के साथ यह करना है कि सूचकांक क्या है NON_UNIQUE? BTW last_name हैVARCHAR(1000)

@RolandoMySQLDBA द्वारा अनुरोधित अपडेट

mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;  
+---------------+  
| DistinctCount |  
+---------------+  
|         10000 |  
+---------------+  
1 row in set (0.05 sec)  


mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;  
+----------+  
| COUNT(1) |  
+----------+  
|        0 |  
+----------+  
1 row in set (0.15 sec)  

कृपया इन दो प्रश्नों को चलाएं: 1) SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;2) SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;। प्रत्येक गणना का परिणाम क्या है?
RolandoMySQLDBA

@ रोलैंडम्यूसीडीडीबीए: मैंने आपके द्वारा पूछी गई जानकारी के साथ ओपी को अद्यतन किया।
क्राइटलस

दो और प्रश्न, कृपया: १) SELECT COUNT(1) FullTableCount FROM employees;और २) SELECT * FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A LIMIT 10;
RolandoMySQLDBA

कोई बात नहीं, मैं समझाता हूं कि मुझे क्या चाहिए।
RolandoMySQLDBA

2
@ कैरेटाइलस आपने गलत उत्तर स्वीकार कर लिया, आपको माइकल-स्क्लोटबॉट का
चमत्कारी

जवाबों:


6

समस्या # 1

क्वेरी को देखो

select last_name from employees order by last_name;

मुझे कोई सार्थक क्लॉज़ दिखाई नहीं देता है, और न ही MySQL क्वेरी ऑप्टिमाइज़र। सूचकांक का उपयोग करने के लिए कोई प्रोत्साहन नहीं है।

समस्या # 2

क्वेरी को देखो

select last_name from employees force index(idx_last_name) order by last_name; 

आपने इसे एक सूचकांक दिया, लेकिन क्वेरी ओपिटमाइज़र ने इसे संभाल लिया। मैंने इस व्यवहार को पहले देखा है ( मैं MySQL में एक विशिष्ट सूचकांक का उपयोग करने के लिए एक JOIN को कैसे मजबूर करूं? )

ऐसा क्यों होना चाहिए?

एक WHEREखंड के बिना , क्वेरी ऑप्टिमाइज़र खुद को निम्नलिखित कहता है:

  • यह एक InnoDB तालिका है
  • यह एक अनुक्रमित स्तंभ है
  • सूचकांक में gen_clust_index (उर्फ क्लस्टर इंडेक्स) की row_id है
  • मुझे सूचकांक को कब देखना चाहिए
    • कोई WHEREखंड नहीं है?
    • मुझे हमेशा टेबल पर वापस उछालना होगा?
  • चूंकि एक InnoDB तालिका में सभी पंक्तियां gen_clust_index के समान 16K ब्लॉक में रहती हैं, इसलिए मैं इसके बजाय एक पूर्ण तालिका स्कैन करूंगा।

क्वेरी ऑप्टिमाइज़र ने कम से कम प्रतिरोध का रास्ता चुना।

आप थोड़े से झटके के लिए जा रहे हैं, लेकिन यहाँ यह हो जाता है: क्या आप जानते हैं कि क्वेरी ऑप्टिमाइज़र MyISAM को अलग तरह से हैंडल करेगा?

आप शायद कह रहे हैं हुह ???? किस तरह ????

MyISAM .MYDफ़ाइल में डेटा और फ़ाइल में सभी अनुक्रमित संग्रहीत करता है .MYI

एक ही क्वेरी एक अलग EXPLAIN प्लान तैयार करेगी क्योंकि इंडेक्स डेटा से अलग फाइल में रहता है। क्यों ? यहाँ क्यों है:

  • आवश्यक डेटा ( last_nameस्तंभ) पहले से ही क्रम में है.MYI
  • सबसे खराब स्थिति में, आपके पास एक पूर्ण सूचकांक स्कैन होगा
  • आप केवल last_nameइंडेक्स से कॉलम एक्सेस करेंगे
  • आपको अवांछित के माध्यम से झारने की आवश्यकता नहीं है
  • आप छँटाई के लिए अस्थायी फ़ाइल निर्माण को ट्रिगर नहीं करेंगे

यह कैसे सुनिश्चित किया जा सकता है? मैंने इस कार्य सिद्धांत का परीक्षण किया है कि कैसे एक अलग भंडारण का उपयोग करने से एक अलग EXPLAIN योजना (कभी-कभी बेहतर होगी) उत्पन्न होगी: क्या इसके लिए ORDER BY द्वारा उपयोग किए जाने के लिए सभी चयनित स्तंभों को एक सूचकांक को कवर करना चाहिए?


1
-1 @ रोलैंड यह उत्तर माइकल-स्क्लबॉट के सही उत्तर से कम सटीक नहीं है, लेकिन यह गलत है, उदाहरण के लिए मैनुअल कहता है: "MySQL इन ऑपरेशन के लिए अनुक्रमित का उपयोग करता है: (...) सॉर्टिंग या यदि एक मेज को सॉर्ट या समूह करने के लिए" एक प्रयोग करने योग्य सूचकांक (...) के बाईं ओर उपसर्ग पर समूहीकरण किया जाता है। साथ ही आपके पोस्ट के कुछ अन्य स्टेटमेंट विवादित हैं। मैं आपको इस उत्तर को हटाने या इसे फिर से काम करने की सलाह दूंगा।
चमत्कारी

यह उत्तर सही नहीं है। यदि अभी भी कोई क्लॉज नहीं बचा है, तो एक इंडेक्स का उपयोग किया जा सकता है।
सीप

19

दरअसल, यहां समस्या यह है कि यह उपसर्ग सूचकांक जैसा दिखता है। मुझे प्रश्न में तालिका की परिभाषा नहीं दिख रही है, लेकिन sub_part= 700? आपने पूरे स्तंभ को अनुक्रमणित नहीं किया है, इसलिए अनुक्रमणिका को सॉर्ट करने के लिए उपयोग नहीं किया जा सकता है और यह एक कवरिंग इंडेक्स के रूप में उपयोगी नहीं है। इसका इस्तेमाल केवल उन पंक्तियों को खोजने के लिए किया जा सकता है जो "a" से मेल खा सकती हैं WHEREऔर सर्वर लेयर (स्टोरेज इंजन के ऊपर) से मिलान की गई पंक्तियों को और फ़िल्टर करना होगा। क्या आपको वास्तव में एक अंतिम नाम के लिए 1000 वर्णों की आवश्यकता है?


वर्णन करने के लिए अद्यतन : मेरे पास एक तालिका परीक्षण तालिका है जिसमें 500 से अधिक पंक्तियों की एक पंक्ति है, प्रत्येक में एक स्तंभ में एक वेब साइट का डोमेन नाम domain_name VARCHAR(254) NOT NULLऔर कोई अनुक्रमणिका नहीं है।

mysql> alter table keydemo add key(domain_name);
Query OK, 0 rows affected (0.17 sec)
Records: 0  Duplicates: 0  Warnings: 0

पूर्ण स्तंभ अनुक्रमणित के साथ, क्वेरी अनुक्रमणिका का उपयोग करती है:

mysql> explain select domain_name from keydemo order by domain_name;
+----+-------------+---------+-------+---------------+-------------+---------+------+------+-------------+
| id | select_type | table   | type  | possible_keys | key         | key_len | ref  | rows | Extra       |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+-------------+
|  1 | SIMPLE      | keydemo | index | NULL          | domain_name | 764     | NULL |  541 | Using index |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+-------------+
1 row in set (0.01 sec)

तो, अब, मैं उस इंडेक्स को छोड़ दूँगा, और domain_name के पहले 200 अक्षरों को इंडेक्स करूँगा।

mysql> alter table keydemo drop key domain_name;
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table keydemo add key(domain_name(200));
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select domain_name from keydemo order by domain_name;
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | keydemo | ALL  | NULL          | NULL | NULL    | NULL |  541 | Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

mysql>

देखा।

यह भी ध्यान दें, कि 200 अक्षरों का सूचकांक, कॉलम में सबसे लंबे मूल्य से अधिक है ...

mysql> select max(length(domain_name)) from keydemo;
+--------------------------+
| max(length(domain_name)) |
+--------------------------+
|                       43 |
+--------------------------+
1 row in set (0.04 sec)

... लेकिन इससे कोई फर्क नहीं पड़ता। एक उपसर्ग लंबाई के साथ घोषित एक सूचकांक केवल लुकअप के लिए इस्तेमाल किया जा सकता है, छंटाई के लिए नहीं, और एक कवरिंग इंडेक्स के रूप में नहीं, क्योंकि इसमें पूर्ण स्तंभ मान नहीं है, परिभाषा के अनुसार।

इसके अलावा, उपरोक्त प्रश्नों को एक InnoDB तालिका पर चलाया गया था, लेकिन उन्हें एक MyISAM तालिका पर चलाने से लगभग समान परिणाम मिलते हैं। केवल इस मामले में अंतर यह है कि InnoDB गिनती के लिए है rowsबंद (541) से थोड़ा है, जबकि MyISAM शो पंक्तियों की सही संख्या (563), जो सामान्य व्यवहार है के बाद से दो भंडारण इंजन सूचकांक गोते संभाल बहुत अलग।

मैं फिर भी यह दावा करूंगा कि last_name कॉलम जरूरत से ज्यादा बड़ा है, लेकिन यह अभी भी पूरे कॉलम को इंडेक्स करना संभव है, यदि आप InnoDB का उपयोग कर रहे हैं और MySQL 5.5 या 5.6 चला रहे हैं:

डिफ़ॉल्ट रूप से, एकल-स्तंभ अनुक्रमणिका के लिए एक इंडेक्स कुंजी 767 बाइट्स तक हो सकती है। समान लंबाई सीमा किसी भी सूचकांक कुंजी उपसर्ग पर लागू होती है। धारा 13.1.13, " CREATE INDEXसिंटेक्स" देखें। उदाहरण के लिए, यदि आप एक पर 255 से अधिक वर्ण का एक स्तंभ उपसर्ग सूचकांक के साथ इस सीमा तक पहुंच सकता है TEXTया VARCHARस्तंभ, एक संभालने UTF-8वर्ण सेट और हर किरदार के लिए 3 बाइट्स की अधिकतम। जब innodb_large_prefixकॉन्फ़िगरेशन विकल्प सक्षम होता है, तो यह लंबाई सीमा 3072 बाइट्स के लिए उठाई जाती है, जो InnoDBतालिकाओं DYNAMICऔर COMPRESSEDपंक्ति स्वरूपों का उपयोग करती है।

- http://dev.mysql.com/doc/refman/5.5/en/innodb-restrictions.html


देखने की दिलचस्प बात। स्तंभ है, varchar(1000)लेकिन यह सूचकांक के लिए अधिकतम अनुमति से परे है जो ~ 750
क्रैटाइलस

8
यह उत्तर स्वीकृत होना चाहिए।
ypercube y

1
@ypercube मेरा यह उत्तर मेरी तुलना में अधिक सटीक है। आपकी टिप्पणी के लिए +1 और इस उत्तर के लिए +1। हो सकता है कि यह खदान के बजाय स्वीकार किया जाए।
RolandoMySQLDBA

1
@ टिमो, यह एक दिलचस्प सवाल है ... जिसे मैं एक नए प्रश्न के रूप में पोस्ट करने का सुझाव दूंगा, यहाँ, शायद इस उत्तर के लिए, संदर्भ के लिए लिंक के साथ। से पूरा उत्पादन पोस्ट करें EXPLAIN SELECT ..., साथ ही SHOW CREATE TABLE ...और SELECT @@VERSION;संस्करणों में अनुकूलक में परिवर्तन के बाद से प्रासंगिक हो सकता है।
माइकल - साइक्लोबोट

1
अब तक मैं रिपोर्ट कर सकता हूं कि (कम से कम 5.7 के लिए) एक उपसर्ग सूचकांक अनुक्रमणिका अशक्त करने में मदद नहीं करता है , जैसा कि मैंने ऊपर अपनी टिप्पणी में पूछा था।
टिमो

2

मैंने इस बारे में एक उत्तर दिया क्योंकि एक टिप्पणी तैयार करने का समर्थन नहीं करेगी और RolandoMySQL DBA ने gen_clust_index और innodb के बारे में बात की। और यह एक निर्दोष आधारित तालिका पर बहुत महत्वपूर्ण है। यह सामान्य DBA ज्ञान से आगे जाता है क्योंकि आपको C कोड का विश्लेषण करने में सक्षम होना चाहिए।

यदि आप Innodb का उपयोग कर रहे हैं, तो आपको हमेशा हमेशा एक प्रमुख कुंजी या एक अद्वितीय कुंजी बनाना चाहिए। यदि आप निर्दोष नहीं हैं तो यह स्वयं उत्पन्न ROW_ID का उपयोग करेगा जो आपको अच्छे से अधिक नुकसान पहुंचा सकता है।

मैं इसे आसान तरीके से समझाने की कोशिश करूंगा क्योंकि प्रमाण सी कोड पर आधारित है।

/**********************************************************************//**
Returns a new row id.
@return the new id */
UNIV_INLINE
row_id_t
dict_sys_get_new_row_id(void)
/*=========================*/
{
    row_id_t    id;

    mutex_enter(&(dict_sys->mutex));

    id = dict_sys->row_id;

    if (0 == (id % DICT_HDR_ROW_ID_WRITE_MARGIN)) {
          dict_hdr_flush_row_id();
    }

    dict_sys->row_id++;
    mutex_exit(&(dict_sys->mutex));
    return(id);
}

पहली समस्या

mutex_enter (और (dict_sys-> म्युटेक्स));

यह रेखा सुनिश्चित करती है कि केवल एक धागा एक ही समय में तानाशाही -> म्यूटेक्स तक पहुंच सकता है। क्या होगा अगर पहले से ही मूल्य को म्यूट किया गया था ... हाँ एक धागे का इंतजार करना पड़ता है ताकि आपको थ्रेड लॉकिंग जैसी एक अच्छी यादृच्छिक सुविधा मिल जाए या यदि आपके पास अपनी खुद की प्राथमिक कुंजी या UNIQUE कुंजी के बिना अधिक टेबल हैं तो आपके पास एक अच्छी सुविधा होगी innodb ' टेबल लॉकिंग ' यह वह कारण नहीं है, जिसके कारण MyISAM को InnoDB द्वारा प्रतिस्थापित किया गया था, क्योंकि रिकॉर्ड / पंक्ति आधारित लॉकिंग नामक अच्छी सुविधा है।

दूसरी समस्या

(0 == (आईडी% DICT_HDR_ROW_ID_WRITE_MARGIN)

modulo (%) की गणना धीमी नहीं है यदि आप बैच सम्मिलित कर रहे हैं क्योंकि इसे हर बार पुनर्गणना करने की आवश्यकता है ..., और क्योंकि DICT_HDR_ROW_ID_WRITE_MARGIN (मान 256) दो की शक्ति है यह बहुत तेजी से बनाया जा सकता है ..

(0 == (आईडी और (DICT_HDR_ROW_ID_WRITE_MARGIN - 1))

साइड नोट यदि C कंपाइलर को ऑप्टिमाइज़ करने के लिए कॉन्फ़िगर किया गया था और यह एक अच्छा ऑप्टिमाइज़र है, तो C ऑप्टिमाइज़र हल्के संस्करण के लिए "भारी" कोड को ठीक कर देगा

कहानी का आदर्श वाक्य हमेशा अपनी खुद की प्राथमिक कुंजी बनाएँ या सुनिश्चित करें कि आपके पास शुरुआत से एक तालिका बनाते समय आपके पास एक अद्वितीय सूचकांक हो।


पंक्ति-आधारित प्रतिकृति और इस तथ्य को जोड़ें कि पंक्ति ID सर्वरों के अनुरूप नहीं है, और हमेशा एक प्राथमिक कुंजी बनाने के बारे में रेमंड की बात और भी महत्वपूर्ण है।

कृपया सुझाव न दें कि UNIQUEयह पर्याप्त है - इसमें पीके को पदोन्नत किए जाने वाले अद्वितीय सूचकांक के लिए केवल गैर-पूर्ण कॉलम शामिल करने की आवश्यकता है।
रिक जेम्स

"मोडुलो (%) की गणना धीमी है" - INSERTइस समारोह में खर्च होने वाले समय का प्रतिशत कितना महत्वपूर्ण है । मुझे संदेह है कि यह महत्वहीन है। स्तंभों को चारों ओर फावड़ा करने के प्रयास के विपरीत, कभी-कभी ब्लॉक-स्प्लिट, बफर_पुल पर विभिन्न म्यूटेक्स, परिवर्तन-बफर सामान, आदि सहित बीट्री संचालन करें
रिक जेम्स

सच @RJJames ओवरहेड बहुत छोटी संख्या हो सकती है, लेकिन कई छोटी संख्याएं भी जोड़ देती हैं (अभी भी एक माइक्रो ऑप्टिमाइज़ेशन होगा) .. पहली समस्या के अलावा सबसे ज्यादा परेशानी कुछ है
रेमंड निजलैंड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.