क्या वास्तव में सभी चयनित स्तंभों को अनुक्रमित करने के लिए MySQL के लिए अनुक्रमित किया जाना आवश्यक है?
यह एक लोडेड प्रश्न है क्योंकि ऐसे कारक हैं जो यह निर्धारित करते हैं कि कोई इंडेक्स इस्तेमाल करने लायक है या नहीं।
FACTOR # 1
किसी भी सूचकांक के लिए, प्रमुख जनसंख्या क्या है? दूसरे शब्दों में, सूचकांक में दर्ज सभी tuples की कार्डिनैलिटी (विशिष्ट गणना) क्या है?
फैक्टरी # 2
आप किस स्टोरेज इंजन का उपयोग कर रहे हैं? क्या एक सूचकांक से सभी आवश्यक कॉलम सुलभ हैं?
आगे क्या होगा ???
चलो एक सरल उदाहरण लेते हैं: एक तालिका जिसमें दो मान होते हैं (पुरुष और महिला)
इंडेक्स के उपयोग के लिए परीक्षण के साथ ऐसी तालिका बनाएं
USE test
DROP TABLE IF EXISTS mf;
CREATE TABLE mf
(
id int not null auto_increment,
gender char(1),
primary key (id),
key (gender)
) ENGINE=InnODB;
INSERT INTO mf (gender) VALUES
('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
('M'),('M'),('M'),('M'),('F'),('F'),('M'),('M'),
('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
('F'),('M'),('M'),('M'),('M'),('M'),('M'),('M');
ANALYZE TABLE mf;
EXPLAIN SELECT gender FROM mf WHERE gender='F';
EXPLAIN SELECT gender FROM mf WHERE gender='M';
EXPLAIN SELECT id FROM mf WHERE gender='F';
EXPLAIN SELECT id FROM mf WHERE gender='M';
परीक्षण InnoDB
mysql> USE test
Database changed
mysql> DROP TABLE IF EXISTS mf;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE mf
-> (
-> id int not null auto_increment,
-> gender char(1),
-> primary key (id),
-> key (gender)
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.07 sec)
mysql> INSERT INTO mf (gender) VALUES
-> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
-> ('M'),('M'),('M'),('M'),('F'),('F'),('M'),('M'),
-> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
-> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
-> ('F'),('M'),('M'),('M'),('M'),('M'),('M'),('M');
Query OK, 40 rows affected (0.06 sec)
Records: 40 Duplicates: 0 Warnings: 0
mysql> ANALYZE TABLE mf;
+---------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+---------+---------+----------+----------+
| test.mf | analyze | status | OK |
+---------+---------+----------+----------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT gender FROM mf WHERE gender='F';
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| 1 | SIMPLE | mf | ref | gender | gender | 2 | const | 3 | Using where; Using index |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT gender FROM mf WHERE gender='M';
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| 1 | SIMPLE | mf | ref | gender | gender | 2 | const | 37 | Using where; Using index |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT id FROM mf WHERE gender='F';
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| 1 | SIMPLE | mf | ref | gender | gender | 2 | const | 3 | Using where; Using index |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT id FROM mf WHERE gender='M';
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| 1 | SIMPLE | mf | ref | gender | gender | 2 | const | 37 | Using where; Using index |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
mysql>
टेस्ट मेरा
mysql> USE test
Database changed
mysql> DROP TABLE IF EXISTS mf;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE mf
-> (
-> id int not null auto_increment,
-> gender char(1),
-> primary key (id),
-> key (gender)
-> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.05 sec)
mysql> INSERT INTO mf (gender) VALUES
-> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
-> ('M'),('M'),('M'),('M'),('F'),('F'),('M'),('M'),
-> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
-> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
-> ('F'),('M'),('M'),('M'),('M'),('M'),('M'),('M');
Query OK, 40 rows affected (0.00 sec)
Records: 40 Duplicates: 0 Warnings: 0
mysql> ANALYZE TABLE mf;
+---------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+---------+---------+----------+----------+
| test.mf | analyze | status | OK |
+---------+---------+----------+----------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT gender FROM mf WHERE gender='F';
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| 1 | SIMPLE | mf | ref | gender | gender | 2 | const | 3 | Using where; Using index |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT gender FROM mf WHERE gender='M';
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
| 1 | SIMPLE | mf | ref | gender | gender | 2 | const | 36 | Using where; Using index |
+----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT id FROM mf WHERE gender='F';
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
| 1 | SIMPLE | mf | ref | gender | gender | 2 | const | 3 | Using where |
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT id FROM mf WHERE gender='M';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | mf | ALL | gender | NULL | NULL | NULL | 40 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql>
InnoDB के लिए विश्लेषण
जब डेटा को InnoDB के रूप में लोड किया गया था, तो कृपया ध्यान दें कि सभी चार EXPLAIN
योजनाओं में gender
सूचकांक का उपयोग किया गया था । तीसरी और चौथी EXPLAIN
योजनाओं में gender
सूचकांक का उपयोग किया गया था, भले ही अनुरोधित डेटा था id
। क्यों? क्योंकि id
में है PRIMARY KEY
और सभी माध्यमिक अनुक्रमों में संदर्भ बिंदुPRIMARY KEY
( gen_clust_index के माध्यम से ) वापस आ गए हैं ।
MyISAM के लिए विश्लेषण
जब डेटा को MyISAM के रूप में लोड किया गया था, तो कृपया ध्यान दें कि पहले तीन EXPLAIN
योजनाओं में gender
सूचकांक का उपयोग किया गया था । चौथी EXPLAIN
योजना में, क्वेरी ऑप्टिमाइज़र ने एक सूचकांक का उपयोग नहीं करने का निर्णय लिया। इसने इसके बजाय एक पूर्ण तालिका स्कैन का विकल्प चुना। क्यों?
DBMS की परवाह किए बिना, क्वेरी ऑप्टिमाइज़र एक बहुत ही सरल नियम-पर-अंगूठे पर काम करते हैं: यदि कोई इंडेक्स लुकअप करने के लिए उपयोग किए जाने वाले उम्मीदवार के रूप में प्रदर्शित किया जाता है और क्वेरी ऑप्टिमाइज़र गणना करता है कि उसे कुल संख्या का 5% से अधिक लुकअप करना होगा तालिका में पंक्तियाँ:
- यदि पूर्ण पुनर्प्राप्ति के लिए सभी आवश्यक कॉलम चयनित सूचकांक में हैं, तो एक पूर्ण सूचकांक स्कैन किया जाता है
- एक पूर्ण तालिका स्कैन अन्यथा
निष्कर्ष
यदि आपके पास उचित कवरिंग इंडेक्स नहीं हैं, या यदि किसी दिए गए टपल के लिए प्रमुख जनसंख्या तालिका का 5% से अधिक है, तो छह चीजें होनी चाहिए:
- इस अहसास के लिए आइए कि आपको प्रश्नों को प्रोफाइल करना होगा
- सभी का पता लगाएं
WHERE
, GROUP BY
और आदेश BY` उन प्रश्नों से खंड
- इस क्रम में अनुक्रमित करें
WHERE
स्थैतिक मूल्यों के साथ खंड कॉलम
GROUP BY
कॉलम
ORDER BY
कॉलम
- पूर्ण तालिका स्कैन से बचें (एक समझदार
WHERE
खंड का अभाव )
- खराब कुंजी आबादी से बचें (या उन खराब कुंजी आबादी को कम से कम कैश करें)
- टेबल्स के लिए सबसे अच्छा MySQL स्टोरेज इंजन ( InnoDB या MyISAM ) तय करें
मैंने अतीत में अंगूठे के इस 5% नियम के बारे में लिखा है:
UPDATE 2012-11-14 13:05 EDT
मैंने आपके प्रश्न पर और मूल SO पोस्ट पर एक नज़र डाला । फिर, मैंने Analysis for InnoDB
पहले अपने उल्लेख के बारे में सोचा । यह person
तालिका के साथ मेल खाता है । क्यों?
दोनों तालिकाओं के लिए mf
औरperson
- भंडारण इंजन InnoDB है
- प्राथमिक कुंजी है
id
- तालिका का उपयोग द्वितीयक सूचकांक द्वारा होता है
- यदि तालिका MyISAM थी, तो हम एक पूरी तरह से अलग
EXPLAIN
योजना देखेंगे
अब, SO प्रश्न से क्वेरी को देखें select * from person order by age\G
:। चूंकि कोई WHERE
खंड नहीं है, इसलिए आपने स्पष्ट रूप से एक पूर्ण तालिका स्कैन की मांग की है । तालिका का डिफ़ॉल्ट क्रम क्रमांक (id
PRIMARY KEY) होगा क्योंकि इसका स्वत: स्वरूप और gen_clust_index (उर्फ क्लस्टर इंडेक्स) आंतरिक पंक्ति द्वारा आदेशित है । जब आप सूचकांक द्वारा आदेश देते हैं, तो ध्यान रखें कि InnoDB द्वितीयक सूचकांक में प्रत्येक अनुक्रमणिका प्रविष्टि से जुड़ी हुई पंक्ति होती है। यह हर बार पूर्ण पंक्ति पहुंच की आंतरिक आवश्यकता का उत्पादन करता है।
ORDER BY
यदि आप InnoDB अनुक्रमणिका को व्यवस्थित करने के तरीके के बारे में इन तथ्यों को अनदेखा करते हैं, तो एक InnoDB टेबल पर सेट करना एक कठिन काम हो सकता है।
उस SO क्वेरी पर वापस जा रहे हैं, जब से आपने स्पष्ट रूप से एक पूर्ण तालिका स्कैन की मांग की थी , IMHO MySQL क्वेरी ऑप्टिमाइज़र ने सही काम किया (या कम से कम, कम से कम प्रतिरोध का रास्ता चुना)। जब यह InnoDB और SO क्वेरी की बात आती है, तो पूर्ण तालिका स्कैन करना और फिर filesort
पूर्ण अनुक्रमणिका स्कैन करने के बजाय कुछ करना और प्रत्येक द्वितीयक अनुक्रमणिका प्रविष्टि के लिए gen_clust_index के माध्यम से पंक्ति लुकअप करना आसान होता है।
मैं सूचकांक संकेतों का उपयोग करने का एक वकील नहीं हूं क्योंकि यह EXPLAIN योजना की उपेक्षा करता है। इसके बावजूद, यदि आप वास्तव में अपने डेटा को InnoDB से बेहतर जानते हैं, तो आपको सूचकांक संकेतों का सहारा लेना होगा, विशेषकर उन प्रश्नों के साथ जिनका कोई WHERE
खंड नहीं है।
अद्यतन 2012-11-14 14:21 EDT
अंडरस्टैंडिंग माईक्यूएक्स इंटर्नल बुक के अनुसार
पृष्ठ 202 अनुच्छेद 7 निम्नलिखित कहता है:
डेटा को एक विशेष संरचना में संग्रहीत किया जाता है जिसे क्लस्टर्ड इंडेक्स कहा जाता है , जो कि मुख्य मूल्य के रूप में प्राथमिक कुंजी अभिनय के साथ बी-ट्री है, और डेटा भाग में वास्तविक रिकॉर्ड (एक संकेतक के बजाय) है। इस प्रकार, प्रत्येक InnoDB तालिका में एक प्राथमिक कुंजी होनी चाहिए। यदि किसी को आपूर्ति नहीं की जाती है, तो एक विशेष पंक्ति आईडी स्तंभ जो सामान्य रूप से उपयोगकर्ता को दिखाई नहीं देता है, प्राथमिक कुंजी के रूप में कार्य करने के लिए जोड़ा जाता है। द्वितीयक कुंजी प्राथमिक कुंजी का मान संग्रहीत करेगी जो रिकॉर्ड की पहचान करती है। बी-ट्री कोड innobase / btr / btr0btr.c में पाया जा सकता है ।
यही कारण है कि मैंने पहले कहा था: एक पूर्ण अनुक्रमणिका स्कैन और प्रत्येक द्वितीयक अनुक्रमणिका प्रविष्टि के लिए gen_clust_index के माध्यम से पंक्ति लुकअप करने के बजाय कुछ पूर्ण तालिका स्कैन करना अधिक आसान है । InnoDB हर बार एक डबल इंडेक्स लुकअप करने जा रहा है । यह एक तरह का क्रूर लगता है, लेकिन यह सिर्फ तथ्य है। फिर, WHERE
क्लॉज की कमी को ध्यान में रखें । यह, अपने आप में, एक पूर्ण तालिका स्कैन करने के लिए MySQL क्वेरी ऑप्टिमाइज़र का संकेत है।
FOR ORDER BY
(जो इस प्रश्न में विशिष्ट मामला है) का चयन करने के लिए प्रासंगिक प्रतीत नहीं होता है । प्रश्न में कहा गया था कि इस मामले में भंडारण इंजन थाInnoDB
(और मूल एसओ सवाल दिखाता है कि 10k पंक्तियां समान रूप से 8 वस्तुओं में वितरित की जाती हैं, कार्डिनैलिटी यहां भी एक मुद्दा नहीं होनी चाहिए)। अफसोस की बात है, मुझे नहीं लगता कि यह सवाल का जवाब देता है।