it-swarm.com.de

Warum kann MySQL keinen partiellen Primärschlüsselindex verwenden?

Die MySQL-Dokumentation, die die Verwendung von Indexerweiterungen beschreibt, enthält die folgende Tabelle als Beispiel, gefolgt von der folgenden Abfrage:

CREATE TABLE t1 (
    i1 INT NOT NULL DEFAULT 0,
    i2 INT NOT NULL DEFAULT 0,
    d DATE DEFAULT NULL,
    PRIMARY KEY (i1, i2),
    INDEX k_d (d)
) ENGINE = InnoDB;

SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';

InnoDB konvertiert intern den Index k_d so, dass er am Ende den Primärschlüssel enthält. Das heißt, der tatsächliche Index k_d befindet sich in (d, i1, i2), drei Spalten.

Die Dokumentation erklärt weiter, dass (Schwerpunkt meiner):

In diesem Fall kann der Optimierer nicht den Primärschlüssel verwenden, da dieser Spalten (i1, i2) enthält und die Abfrage nicht auf i2 verweist. Stattdessen verwendet der Optimierer can den Sekundärindex k_d on (d), und der Ausführungsplan hängt davon ab, ob der erweiterte Index verwendet wird.

Die obige Aussage verwirrt mich. Zuerst heißt es, dass i1nicht genug ist, um den Primärschlüsselindex von zwei Spalten (i1, i2) zu verwenden. Im zweiten Satz heißt es dann, dass der Index k_d für (d, i1, i2)kann verwendet werden kann, obwohl nur d und i1 verwendet werden, wobei i2 fehlt.

Mein allgemeines Verständnis von Indizes in MySQL und anderen SQL-Varianten ist, dass ein linker Teil eines Index verwendet werden kann, wenn eine Teilmenge aller Spalten im Index vorhanden ist, beginnend von links.

Was ist anders an einem Primärschlüsselindex (Clustered-Index) und einem nicht-Clustered-Sekundärindex, bei denen der letztere einen Teilindex verwenden kann, der erstere jedoch nicht?

13
Tim Biegeleisen

Die Dokumentation auf der Seite, auf die Sie verlinkt haben, ist teilweise ungenau.

Demo, laufe auf MySQL 5.7.21:

mysql [localhost] {msandbox} (test) > CREATE TABLE t1 (
    ->     i1 INT NOT NULL DEFAULT 0,
    ->     i2 INT NOT NULL DEFAULT 0,
    ->     d DATE DEFAULT NULL,
    ->     PRIMARY KEY (i1, i2),
    ->     INDEX k_d (d)
    -> ) ENGINE = InnoDB;

mysql [localhost] {msandbox} (test) > explain SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | ref  | PRIMARY,k_d   | PRIMARY | 4       | const |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+

Diese Abfrage wählt den PRIMARY-Index und Sie können sehen, dass der key_len 4 ist, was beweist, dass nur eine der 32-Bit-INT-Spalten verwendet wird.

Bei der Verwendung von InnoDB-Tabellen bevorzugt MySQL häufig den PRIMARY-Index (den Clustered-Index), da dieser effizienter ist als die Verwendung eines Sekundärindex.

9
Bill Karwin

In einem solchen Fall

WHERE i1 = 3 AND d = '2000-01-01';

Ich ziehe es vor, explizit INDEX(d, i1) (oder INDEX(i1, d)) zu sagen. Meine Begründung ist, dass ich dem Leser sage, dass ich über die Indizes nachgedacht und festgestellt habe, dass dies für die Abfrage am besten geeignet ist. Und es wäre ein "abdeckender" Index, daher etwas schneller.

Zugegeben, die INDEX(d), die Sie haben sollten haben, entspricht INDEX(d, i1, i2), die effektiv und effizient eingesetzt werden sollte. Was den Grund angeht, würde ich einen Fehler im Optimierer vermuten.

Was die Dokumentation angeht, so gibt es einige Stellen, die schlecht formuliert sind. Sie begrüßen Kritik an der Dokumentation auf bugs.mysql.com.

0
Rick James