วิธีการออกแบบดัชนีสำหรับคอลัมน์ที่มีค่า NULL ใน MySQL


11

ฉันมีฐานข้อมูลที่มี 40 ล้านรายการและต้องการเรียกใช้แบบสอบถามด้วยWHEREประโยคต่อไปนี้

...
WHERE
  `POP1` IS NOT NULL 
  && `VT`='ABC'
  && (`SOURCE`='HOME')
  && (`alt` RLIKE '^[AaCcGgTt]$')
  && (`ref` RLIKE '^[AaCcGgTt]$')
  && (`AA` RLIKE '^[AaCcGgTt]$')
  && (`ref` = `AA` || `alt` = `AA`)
LIMIT 10 ;

POP1เป็นคอลัมน์ลอยที่ยังสามารถเป็น NULL POP1 IS NOT NULLควรแยกประมาณ 50% ของรายการนั่นเป็นเหตุผลที่ฉันใส่ไว้ในตอนเริ่มต้น เงื่อนไขอื่น ๆ ทั้งหมดลดจำนวนเพียงเล็กน้อย

ในบรรดาคนอื่น ๆ ฉันออกแบบดัชนีpop1_vt_sourceซึ่งดูเหมือนจะไม่ได้ใช้ในขณะที่ใช้ดัชนีที่vtเป็นคอลัมน์แรก อธิบายเอาท์พุท:

| id | select_type | table | type | possible_keys                          | key                 | key_len | ref         | rows     | Extra       |
|  1 | SIMPLE      | myTab | ref  | vt_source_pop1_pop2,pop1_vt_source,... | vt_source_pop1_pop2 | 206     | const,const | 20040021 | Using where |

ทำไมดัชนีที่pop1เป็นคอลัมน์แรกไม่ได้ใช้? เพราะเรื่องของNOTหรือเพราะNULLโดยทั่วไป ฉันจะปรับปรุงการออกแบบดัชนีและอนุประโยคของฉันได้อย่างไร แม้ว่าจะ จำกัด เพียง 10 รายการแบบสอบถามจะใช้เวลานานกว่า 30 วินาทีแม้ว่า 100 รายการแรกในตารางควรมี 10 รายการที่ตรงกัน

คำตอบ:


10

มันคือNOT NULL:

CREATE TEMPORARY TABLE `myTab` (`notnul` FLOAT, `nul` FLOAT);
INSERT INTO `myTab` VALUES (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2);
SELECT * FROM `myTab`;

ให้:

+--------+------+
| notnul | nul  |
+--------+------+
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
+--------+------+

สร้างดัชนี:

CREATE INDEX `notnul_nul` ON `myTab` (`notnul`, `nul`);
CREATE INDEX `nul_notnul` ON `myTab` (`nul`, `notnul`);

SHOW INDEX FROM `myTab`;

ให้:

+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| myTab |          1 | notnul_nul |            1 | notnul      | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
| myTab |          1 | notnul_nul |            2 | nul         | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
| myTab |          1 | nul_notnul |            1 | nul         | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
| myTab |          1 | nul_notnul |            2 | notnul      | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

ตอนนี้อธิบายการเลือก ดูเหมือนว่า MySQL จะใช้ดัชนีแม้ว่าคุณจะใช้NOT NULL:

EXPLAIN SELECT * FROM `myTab` WHERE `notnul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ 
| id | select_type | table | type  | possible_keys | key        | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ 
|  1 | SIMPLE      | myTab | index | notnul_nul    | notnul_nul | 10      | NULL |   12 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+


EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key        | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | myTab | range | nul_notnul    | nul_notnul | 5       | NULL |    6 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+

แต่เมื่อเปรียบเทียบNOT NULLและNULLมันก็ดูเหมือนว่า MySQL preferrs ดัชนีอื่น ๆ NOT NULLเมื่อใช้ แม้ว่าสิ่งนี้จะไม่เพิ่มข้อมูลใด ๆ นี่เป็นเพราะ MySQL ตีความNOT NULLว่าเป็นช่วงที่คุณเห็นในคอลัมน์ประเภท ฉันไม่แน่ใจว่ามีวิธีแก้ปัญหาหรือไม่:

EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NULL && notnul=2;
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
| id | select_type | table | type | possible_keys         | key        | key_len | ref         | rows | Extra                    |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
|  1 | SIMPLE      | myTab | ref  | notnul_nul,nul_notnul | notnul_nul | 10      | const,const |    1 | Using where; Using index |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+


EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL && notnul=2;
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys         | key        | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | myTab | range | notnul_nul,nul_notnul | notnul_nul | 10      | NULL |    1 | Using where; Using index |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+

ฉันคิดว่าอาจมีการใช้งานที่ดีขึ้นใน MySQL เพราะNULLเป็นค่าพิเศษ อาจคนส่วนใหญ่มีความสนใจในNOT NULLค่านิยม


3

ปัญหาไม่ได้เป็นค่า NULL มันคือการเลือกของดัชนี ในตัวอย่างของการคัดสรรของดีกว่าการเลือกแค่source, pop1 pop1ครอบคลุมเงื่อนไขเพิ่มเติมในwhereข้อดังนั้นจึงมีแนวโน้มที่จะลดจำนวนการเข้าชมหน้าเว็บ

คุณอาจคิดว่าการลดจำนวนแถวลง 50% นั้นเพียงพอ แต่จริงๆแล้วไม่ใช่ ประโยชน์ของดัชนีในส่วนwhereคำสั่งคือการลดจำนวนหน้าที่อ่าน หากหน้ามีค่าเฉลี่ยอย่างน้อยหนึ่งระเบียนที่มีค่าที่ไม่ใช่ค่า NULL แสดงว่าไม่มีประโยชน์ในการใช้ดัชนี และถ้ามี 10 ระเบียนต่อหน้าแล้วเกือบทุกหน้าจะมีหนึ่งในบันทึกเหล่านั้น

(pop1, vt, source)คุณอาจลองดัชนีใน เครื่องมือเพิ่มประสิทธิภาพควรเลือกอันนั้น

ในท้ายที่สุดแม้ว่าถ้าส่วนwhereคำสั่งที่หายไปของระเบียน - ไม่มีกฎ แต่สมมติว่า 20% - ดัชนีอาจจะไม่ช่วย หนึ่งข้อยกเว้นคือเมื่อดัชนีมีคอลัมน์ทั้งหมดที่จำเป็นสำหรับการสืบค้น จากนั้นมันสามารถตอบสนองแบบสอบถามโดยไม่ต้องนำหน้าข้อมูลสำหรับแต่ละระเบียน

และหากดัชนีได้รับการใช้งานและการเลือกสูงนั้นประสิทธิภาพการทำงานกับดัชนีอาจจะแย่กว่าประสิทธิภาพที่ไม่มีมัน


ฉันคิดว่ามันเป็นช่วงที่ทำให้เกิดความแตกต่าง (ดูคำตอบของฉัน) แม้ว่าฉันคิดว่ามันสามารถนำไปใช้ใน MySQL ได้ดีกว่าเนื่องจากคนส่วนใหญ่สนใจในNOT NULLคอลัมน์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.