MySQL'de NULL değerleri olan sütunlar için dizinler nasıl tasarlanır?


11

40 milyon girişli bir veritabanım var ve aşağıdaki WHEREfıkra ile sorguları çalıştırmak istiyorum

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

POP1aynı zamanda NULL olabilen bir kayan sütun. POP1 IS NOT NULLgirişlerin yaklaşık% 50'sini hariç tutmalı, bu yüzden başında koyuyorum. Diğer tüm terimler sayıyı sadece az miktarda azaltır.

Diğerlerinin yanı sıra, pop1_vt_sourcekullanılmamış gibi görünen bir dizin tasarlarken , vtilk sütun olarak bir dizin kullanıldı. EXPLAIN-çıkışı:

| 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 |

Neden pop1ilk sütun olarak olan dizin kullanılmıyor? Yüzünden NOTya nedeniyle NULLgenel olarak. Endekslerimin ve WHERE cümlelerin tasarımını nasıl geliştirebilirim? 10 girişle sınırlandırılsa bile, sorgu 30 saniyeden fazla sürer, ancak tablodaki ilk 100 giriş 10 eşleşmeyi içermelidir.

Yanıtlar:


10

Bu 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`;

verir:

+--------+------+
| 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 |
+--------+------+

Dizini oluşturun:

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

SHOW INDEX FROM `myTab`;

verir:

+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| 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      |         |               |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

Şimdi seçimleri açıklayalım. Görünüşe göre MySQL, dizini kullanıyor olsa bile 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 |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+

Ancak, karşılaştırırken NOT NULLve NULL, MySQL'in kullanırken diğer dizinleri tercih ettiği görülmektedir NOT NULL. Her ne kadar bu herhangi bir bilgi eklemese de. Bunun nedeni, MySQL'in NOT NULLtür sütununda görebileceğiniz bir aralık olarak yorumlanmasıdır . Bir çözüm varsa emin değilim:

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 |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+

NULLÖzel bir değer olduğu için MySQL'de daha iyi bir uygulama olabileceğini düşünüyorum . Muhtemelen çoğu insan değerlerle ilgilenir NOT NULL.


3

Sorun NULL değerleri değil. Endeksin seçiciliğidir. Örneğin, source, pop1seçiciliği sadece seçiciliğinden daha iyidir pop1. Maddedeki koşulların daha fazlasını kapsar where, bu nedenle sayfa isabetlerini azaltma olasılığı daha yüksektir.

Satır sayısını% 50 azaltmanın yeterli olduğunu düşünebilirsiniz, ancak gerçekten değil. Bir cümledeki dizinlerin yararı, whereokunan sayfa sayısını azaltmaktır. Bir sayfada ortalama olarak NULL olmayan bir değere sahip en az bir kayıt varsa, dizini kullanmanın bir kazancı yoktur. Ve sayfa başına 10 kayıt varsa, hemen hemen her sayfada bu kayıtlardan biri olur.

Tarihinde bir dizin deneyebilirsiniz (pop1, vt, source). Optimize edici bunu seçmelidir.

Sonunda, eğer wheremadde kayıtların kaybolmasını sağlıyorsa - kural yoktur, ancak% 20 diyelim - o zaman dizin muhtemelen yardımcı olmaz. Bir istisna, dizinin sorgu için gereken tüm sütunları içermesi olabilir . Daha sonra her kayıt için veri sayfasını getirmeden sorguyu tatmin edebilir.

Ve, bir dizin kullanılırsa ve seçicilik yüksekse, endeks ile performans, onsuz performanstan daha kötü olabilir.


Farkı yaratan aralıklar gerçekten bence (cevabıma bakın). Her ne kadar çoğu insan NOT NULLsütunlarla ilgilendiğinden, MySQL'de daha iyi uygulanabileceğini düşünüyorum .
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.