Warum hat MySQL keine Hash-Indizes für MyISAM oder InnoDB?


35

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?


2
MySQL unterstützt auch keine funktionsbasierten Indizes, Bitmap-Indizes usw. Nur weil es

1
Ich habe gerade herausgefunden, dass Hash-Indizes so ... grundlegend ... sind. Ich gehe davon aus, dass es einen bestimmten Grund für die Implementierung gibt.

1
@ Alex: Ich wette, der Grund ist "Faulheit" und "Bürokratie", aber warten wir auf Antworten))


Am Ende meiner Antwort habe ich einen netten HASH-Algorithmus aus dem High Performance MySQL Book hinzugefügt.
RolandoMySQLDBA

Antworten:


16

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.


Gibt es eine Möglichkeit, das erneute Hashing (Neuaufbau) nebeneinander durchzuführen, ohne die gesamte Tabelle zu sperren?
Pacerier

@Pacerier: Nicht dass ich etwas über MySQL gewusst hätte (obwohl sie die Funktion hinzugefügt haben könnten, seit ich sie das letzte Mal verwendet habe, lesen Sie die Dokumentation). Selbst wenn ein DBMS die Erstellung / Neuerstellung von Online-Indizes unterstützt, ist dies nicht die Standardoption. Was gesperrt wird, hängt davon ab, inwieweit einige Transaktionen eine Schreibsperre für die Tabelle haben, während andere Transaktionen nicht verzögert werden, wenn sie nur lesen. Einige DMBSs entfernen eine vollständige Tabellensperre. Wenn Sie eine Online-Neuerstellung benötigen , überprüfen Sie die Dokumentation jedes DBMS, bevor Sie auswählen, welches verwendet werden soll.
David Spillett

Normalerweise ist ein Neuaufbau nur erforderlich, wenn sich die Datenlänge verdoppelt hat. Müssen sie sich wirklich Sorgen machen, dass sich die Datenlänge jede Minute verdoppelt? (Normalerweise passiert es sehr selten, wenn die Datenbank so groß wird, dass dies ein
Problem darstellt.

6

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.


5

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


5
Führen Sie eine Leistungsanalyse durch, bevor Sie die Pseudo-Hash-Index-Technik in der Produktion verwenden. Für große Strings kann es einen großen Unterschied machen , aber Sie einen Baum - Index der Navigation am Ende sowieso am Ende, und Sie haben extra vergleichen die richtige Reihe von denen Anpassung der Hash gefunden zu tun zu finden, so dass für kleine Werte der Berechnung der Hash - Werte und es lohnt sich einfach nicht, sie zu lagern. Dies ist eigentlich kein Hash-Index, Sie reduzieren lediglich die Arbeit, die beim Durchlaufen des Baums anfällt (da bei jedem Vergleich weniger Bytes berücksichtigt werden, z. B. der Vergleich von 8-Byte-INTs anstelle von x00-Byte-Strings).
David Spillett

@ David Spillett Da muss ich dir voll und ganz zustimmen. Weitere Indizierungsstrategien werden im selben Buch in Kapitel 11 "Indizierungsstrategien für hohe Leistung" ebenfalls vorgeschlagen. Als zusätzlichen Schub für meine Antwort erwähnt das Buch die Verwendung eines Clustered-Index, in dem die Zeile und der BTree-Index in derselben Struktur gespeichert sind. Dies kann die von Ihnen erwähnte reduzierte Arbeit beschleunigen. Leider sind die Reifen, durch die Sie springen müssen, etwas unvermeidlich. Ein +1 von mir auf Ihren Kommentar, Sir !!! Tatsächlich auch +1 für Ihre Antwort.
RolandoMySQLDBA

@ RolandoMySQLDBA Können Sie mehr über den Teil "Custom Hashing" ausführen, der letzte Absatz scheint nicht viel Anhaltspunkt zu geben ...
Pacerier

2

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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.