it-swarm.com.de

Wie entwerfe ich Indizes für Spalten mit NULL-Werten in MySQL?

Ich habe eine Datenbank mit 40 Millionen Einträgen und möchte Abfragen mit der folgenden WHERE -Klausel ausführen

...
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 ist eine Float-Spalte, die auch NULL sein kann. POP1 IS NOT NULL sollte ungefähr 50% der Einträge ausschließen, deshalb habe ich es am Anfang gesetzt. Alle anderen Begriffe reduzieren die Anzahl nur geringfügig.

Unter anderem habe ich einen Index entworfen pop1_vt_source, das anscheinend nicht verwendet wird, während ein Index mit vt als erster Spalte verwendet wird. EXPLAIN-Ausgabe:

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

Warum ist der Index mit pop1 als erste Spalte nicht verwendet? Wegen NOT oder wegen NULL im Allgemeinen. Wie kann ich das Design meiner Indizes und WHERE-Klauseln verbessern? Selbst wenn die Anzahl der Einträge auf 10 begrenzt ist, dauert die Abfrage länger als 30 Sekunden, obwohl die ersten 100 Einträge in der Tabelle die 10 Übereinstimmungen enthalten sollten.

11
Sven

Es ist der 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`;

gibt:

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

Erstellen Sie den Index:

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

SHOW INDEX FROM `myTab`;

gibt:

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

erklären Sie nun die Auswahl. Es scheint, dass MySQL den Index verwendet, auch wenn Sie NOT NULL Verwenden:

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

Beim Vergleich von NOT NULL Und NULL scheint MySQL jedoch andere Indizes zu bevorzugen, wenn NOT NULL Verwendet wird. Obwohl dies offensichtlich keine Informationen hinzufügt. Dies liegt daran, dass MySQL NOT NULL Als Bereich interpretiert, wie Sie in der Typspalte sehen können. Ich bin mir nicht sicher, ob es eine Problemumgehung gibt:

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

Ich denke, es könnte eine bessere Implementierung in MySQL geben, da NULL ein besonderer Wert ist. Wahrscheinlich interessieren sich die meisten Menschen für NOT NULL Werte.

10
John Garreth

Das Problem sind nicht die NULL-Werte. Dies ist die Selektivität des Index. In Ihrem Beispiel ist die Selektivität von source, pop1 Besser als die Selektivität von nur pop1. Es behandelt mehr der Bedingungen in der where -Klausel, sodass es wahrscheinlicher ist, dass Seitentreffer reduziert werden.

Sie denken vielleicht, dass es ausreicht, die Anzahl der Zeilen um 50% zu reduzieren, aber das ist es wirklich nicht. Der Vorteil von Indizes in einer where -Klausel besteht darin, die Anzahl der gelesenen Seiten zu verringern. Wenn eine Seite im Durchschnitt mindestens einen Datensatz mit einem Wert ungleich NULL enthält, hat die Verwendung des Index keinen Vorteil. Und wenn es 10 Datensätze pro Seite gibt, hat fast jede Seite einen dieser Datensätze.

Sie können einen Index für (pop1, vt, source) Versuchen. Der Optimierer sollte diesen aufheben.

Am Ende jedoch, wenn die where -Klausel keine Datensätze enthält - es gibt keine Regel, aber sagen wir 20% -, hilft der Index wahrscheinlich nicht weiter. Eine Ausnahme wäre, wenn der Index alle die von der Abfrage benötigten Spalten enthält. Dann kann es die Abfrage erfüllen, ohne die Datenseite für jeden Datensatz aufzurufen.

Und wenn ein Index verwendet wird und die Selektivität hoch ist, kann die Leistung mit dem Index schlechter sein als die Leistung ohne ihn.

3
Gordon Linoff