Ich habe eine Anwendung, die nur auf Gleichheit auswählt, und ich denke, ich sollte einen Hash-Index über einen Btree-Index verwenden. Zu meiner großen Enttäuschung werden Hash-Indizes in MyISAM oder InnoDB nicht unterstützt. Was ist damit?
Ich habe eine Anwendung, die nur auf Gleichheit auswählt, und ich denke, ich sollte einen Hash-Index über einen Btree-Index verwenden. Zu meiner großen Enttäuschung werden Hash-Indizes in MyISAM oder InnoDB nicht unterstützt. Was ist damit?
Antworten:
Viele Datenbanken unterstützen keine Hash-basierten Indizes überhaupt nicht .
Damit eine Hash-Tabelle effizient ist, müssen Sie die Anzahl der Zeilen kennen, die wahrscheinlich vorhanden sind. Andernfalls ist die Basis-Hash-Tabelle viel zu groß (viele leere Einträge, Platzverschwendung und möglicherweise Festplatten-E / A) oder zu klein Eine Indirektion wird häufig verwendet (möglicherweise mehrere Indirektionsebenen oder, noch schlimmer, wenn die Hash-Implementierung einstufig ist, kann dies dazu führen, dass eine lineare Suche über eine angemessene Anzahl von Datensätzen durchgeführt wird), und an diesem Punkt sind die Dinge wahrscheinlich nicht effizienter als baumbasiert Index sowieso.
Um im Allgemeinen nützlich zu sein (dh in der Regel besser als die Alternative), muss der Index gelegentlich neu erstellt werden, wenn Daten wachsen (und schrumpfen), was zu einem erheblichen zeitweiligen Overhead führen kann. Dies ist bei speicherbasierten Tabellen normalerweise in Ordnung, da die Neuerstellung wahrscheinlich ziemlich schnell sein wird (da sich die Daten immer im RAM befinden und in keinem Fall massiv sein werden), aber die Neuerstellung eines großen Index auf der Festplatte ist ein Problem Sehr schwere Operation (und IIRC mySQL unterstützt keine Live-Index-Neuerstellungen, so dass während der Operation eine Tabellensperre besteht).
Daher werden Hash-Indizes in Speichertabellen verwendet, da sie im Allgemeinen eine bessere Leistung erbringen, aber festplattenbasierte Tabellen unterstützen sie nicht, da sie die Leistung beeinträchtigen und keinen Bonus darstellen können. Es gibt nichts zu stoppen Hash - Indizes werden für Disk - basierten Tabellen natürlich zur Verfügung gestellt, zweifle nicht einige Datenbanken tun die diese Funktion unterstützen, aber vermutlich werden sie nicht in ISAM / InnoDB - Tabellen als Maintainer implementiert berücksichtigen nicht die Funktion noch hinzugefügt (wie die Zusätzlicher Code zum Schreiben und Verwalten ist unter den wenigen Umständen, die einen signifikanten Unterschied ausmachen, den Vorteil nicht wert. Wenn Sie dem nicht zustimmen, können Sie mit ihnen sprechen und sich für die Implementierung der Funktion einsetzen.
Wenn Sie große Zeichenfolgen indizieren, kann die Implementierung eines eigenen Pseudo-Hash-Index (durch Speichern eines Hashs des Werts sowie des tatsächlichen Werts und der Indizierung der Spalte) funktionieren, dies ist jedoch nur bei großen Zeichenfolgen (wo) definitiv effizienter Das Berechnen des Hash-Werts und das Durchsuchen des Baumindex anhand dieses Werts ist in der Regel schneller als das Durchsuchen eines Baumindex anhand der größeren Vergleichswerte, und der zusätzlich verwendete Speicher ist nicht signifikant.) Führen Sie daher vor der Implementierung eine Leistungsanalyse durch dies in der Produktion.
In einem verwandten Hinweis finden Sie möglicherweise die Diskussion über Indextypen in den PostgreSQL-Dokumenten interessant. Es ist in neueren Versionen der Dokumentation nicht mehr vorhanden (aufgrund späterer Optimierungen, nehme ich an), aber das Take-Away könnte für MySQL ähnlich sein (und der Grund, warum Hash-Indizes nur für Heap-Tabellen verwendet werden):
http://www.postgresql.org/docs/8.1/static/indexes-types.html
Hinweis: Tests haben gezeigt, dass die Hash-Indizes von PostgreSQL nicht besser sind als B-Tree-Indizes, und die Indexgröße und die Erstellungszeit für Hash-Indizes sind viel schlechter. Darüber hinaus werden Hash-Index-Operationen derzeit nicht von WAL protokolliert, sodass Hash-Indizes nach einem Datenbankabsturz möglicherweise mit REINDEX neu erstellt werden müssen. Aus diesen Gründen wird derzeit von der Verwendung von Hash-Indizes abgeraten. In ähnlicher Weise scheinen R-Tree-Indizes im Vergleich zu den entsprechenden Operationen von GiST-Indizes keine Leistungsvorteile zu haben. Wie Hash-Indizes sind sie nicht WAL-protokolliert und müssen möglicherweise nach einem Datenbankabsturz neu indiziert werden. Während die Probleme mit Hash-Indizes möglicherweise behoben werden, wird der R-Tree-Indextyp wahrscheinlich in einer zukünftigen Version eingestellt. Benutzer werden aufgefordert, Anwendungen, die R-Tree-Indizes verwenden, in GiST-Indizes zu migrieren.
Auch hier handelt es sich um eine (veraltete) PostgreSQL-spezifische Version, die jedoch darauf hinweisen sollte, dass der "natürliche" Indextyp nicht unbedingt eine optimale Leistung erbringt.
Hier ist etwas Interessantes:
Gemäß dem Buch MySQL 5.0 Certification Study Guide , Seite 433, Abschnitt 29.5.1
Die MEMORY-Engine verwendet standardmäßig den Indexierungsalgorithmus HASH.
Zum Spaß habe ich versucht, mit HASH in MySQL 5.5.12 eine InnoDB-Tabelle und eine MyISAM-Tabelle mit einem Primärschlüssel zu erstellen
mysql> use test
Database changed
mysql> create table rolando (num int not null, primary key (num) using hash);
Query OK, 0 rows affected (0.11 sec)
mysql> show create table rolando\G
*************************** 1. row ***************************
Table: rolando
Create Table: CREATE TABLE `rolando` (
`num` int(11) NOT NULL,
PRIMARY KEY (`num`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> create table rolando2 (num int not null, primary key (num) using hash) engine=MyISAM;
Query OK, 0 rows affected (0.05 sec)
mysql> show create table rolando2\G
*************************** 1. row ***************************
Table: rolando2
Create Table: CREATE TABLE `rolando2` (
`num` int(11) NOT NULL,
PRIMARY KEY (`num`) USING HASH
) ENGINE=MyISAM DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
MySQL hat sich nicht beschwert.
AKTUALISIEREN
Schlechte Nachrichten !!! Ich habe SHOW INDEXES FROM verwendet. Es heißt, der Index ist BTREE.
Die CREATE INDEX-Syntax MySQL Page gibt an, dass nur MEMORY- und NDB-Speicher-Engines den HASH INDEX aufnehmen können.
mysql> show indexes from rolando;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando | 0 | PRIMARY | 1 | num | A | 0 | NULL | NULL | | BTREE | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
mysql> show indexes from rolando2;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando2 | 0 | PRIMARY | 1 | num | A | 0 | NULL | NULL | | BTREE | | |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
mysql> create table rolando3 (num int not null, primary key (num)) ENGINE=MEMORY;
Query OK, 0 rows affected (0.03 sec)
mysql> show create table rolando3\G
*************************** 1. row ***************************
Table: rolando3
Create Table: CREATE TABLE `rolando3` (
`num` int(11) NOT NULL,
PRIMARY KEY (`num`)
) ENGINE=MEMORY DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> show indexes from rolando3;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando3 | 0 | PRIMARY | 1 | num | NULL | 0 | NULL | NULL | | HASH | | |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
Einige Leute schlugen vor, der Idee in den Seiten 102-105 des Buches " Hochleistungs-MySQL: Optimierungen, Backups, Replikation und mehr " zu folgen, um den Hash-Algorithmus zu emulieren.
Seite 105 enthält diesen Quick-and-Dirty-Algorithmus, den ich mag:
SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;
Machen Sie dazu eine Spalte in einer beliebigen Tabelle und indizieren Sie diesen Wert.
Versuche es !!!
BTree ist für die Suche nach einzelnen Zeilen nicht viel langsamer als Hash. Da BTree sehr effiziente Bereichsabfragen bietet, sollten Sie sich mit etwas anderem als BTree befassen.
Da MySQL BTree-Blöcke sehr gut zwischenspeichert, muss eine BTree-basierte Abfrage selten E / A-Vorgänge ausführen. Dies ist der höchste Zeitverbrauch in einer Abfrage.