it-swarm.com.de

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

Ich habe eine Anwendung, die nur nach Gleichheit auswählt, und ich denke, ich sollte einen Hash-Index über einem Btree-Index verwenden. Zu meiner großen Enttäuschung werden Hash-Indizes von MyISAM oder InnoDB nicht unterstützt. Was ist damit?

36
Alex

Viele Datenbanken unterstützen keine Hash-basierten Indizes überhaupt.

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, Speicherplatzverschwendung und möglicherweise Festplatten-E/A) oder zu klein, was bedeutet Indirektion wird häufig verwendet (möglicherweise mehrere Indirektionsebenen oder noch schlimmer, wenn die Hash-Implementierung einstufig ist und Sie möglicherweise eine lineare Suche über eine angemessene Anzahl von Datensätzen durchführen). Zu diesem Zeitpunkt sind die Dinge wahrscheinlich nicht effizienter als baumbasiert Index sowieso.

Um allgemein nützlich zu sein (d. H. Normalerweise besser als die Alternative), muss der Index gelegentlich neu erstellt werden, wenn die Daten wachsen (und schrumpfen), was zu einem erheblichen zeitweiligen Overhead führen kann. Dies ist normalerweise in Ordnung mit speicherbasierten Tabellen, da die Neuerstellung wahrscheinlich ziemlich schnell sein wird (da die Daten immer in RAM und in keinem Fall wahrscheinlich massiv sein werden)). Das Wiederherstellen eines großen Index auf der Festplatte ist jedoch eine sehr schwere Operation (und IIRC mySQL unterstützt keine Live-Index-Neuerstellungen, sodass während des Vorgangs eine Tabellensperre vorhanden ist).

Daher werden Hash-Indizes in Speichertabellen verwendet, da sie im Allgemeinen bessere Ergebnisse erzielen. Festplattenbasierte Tabellen unterstützen sie jedoch nicht, da sie die Leistung beeinträchtigen und keinen Bonus darstellen können. Es gibt natürlich nichts zu verhindern, dass Hash-Indizes für festplattenbasierte Tabellen verfügbar gemacht werden. Zweifellos unterstützen einige Datenbanken do die Funktion, aber vermutlich sind sie nicht in ISAM/InnoDB-Tabellen implementiert, da die Betreuer dies nicht berücksichtigen Die Funktion, die es wert ist, hinzugefügt zu werden (da der zusätzliche Code zum Schreiben und Verwalten unter den wenigen Umständen, die einen signifikanten Unterschied bewirken, den Vorteil nicht wert ist). Wenn Sie nicht einverstanden sind, können Sie möglicherweise mit ihnen sprechen und die Implementierung der Funktion gut begründen.

Wenn Sie große Zeichenfolgen indizieren, funktioniert die Implementierung Ihres eigenen Pseudo-Hash-Index (durch Speichern eines Hashs des Werts sowie des tatsächlichen Werts und Indizieren mit einer Spalte) möglicherweise. Dies ist jedoch nur für große Zeichenfolgen (wo) definitiv effizienter Das Berechnen des Hash-Werts und das Durchsuchen des Baumindex anhand dieses Werts ist wahrscheinlich immer schneller als das Durchsuchen eines Baumindex mit den größeren Vergleichswerten, und der zusätzliche verwendete Speicher wird nicht von Bedeutung sein. Führen Sie daher vor der Implementierung eine Leistungsanalyse durch dies in der Produktion.

16
David Spillett

In einem ähnlichen Zusammenhang könnte die Diskussion über Indextypen aus den PostgreSQL-Dokumenten interessant sein. Es ist in neueren Versionen der Dokumente nicht mehr vorhanden (aufgrund späterer Optimierungen nehme ich an), aber das Mitnehmen 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 abschneiden als B-Tree-Indizes, und die Indexgröße und Erstellungszeit für Hash-Indizes ist viel schlechter. Darüber hinaus werden Hash-Index-Operationen derzeit nicht 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 keine Leistungsvorteile im Vergleich zu den entsprechenden Operationen von Gist-Indizes zu haben. Wie Hash-Indizes sind sie nicht WAL-protokolliert und müssen nach einem Datenbankabsturz möglicherweise neu indiziert werden. Während die Probleme mit Hash-Indizes möglicherweise behoben werden, ist es wahrscheinlich, dass der R-Tree-Indextyp in einer zukünftigen Version eingestellt wird. Benutzer werden aufgefordert, Anwendungen, die R-Tree-Indizes verwenden, in Gist-Indizes zu migrieren.

Auch hier ist es (veraltete Version von) PostgreSQL-spezifisch, aber es sollte darauf hinweisen, dass der "natürliche" Indextyp nicht unbedingt eine optimale Leistung erbringt.

6

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 Lachen 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 besagt, 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 zu folgen auf den Seiten 102-105 des Buches " Hochleistungs-MySQL: Optimierungen, Backups, Replikation und mehr ", um den Hash-Algorithmus zu emulieren.

Seite 105 enthält diesen schnellen und schmutzigen Algorithmus, den ich mag:

SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;

Machen Sie eine Spalte dafür in einer beliebigen Tabelle und indizieren Sie diesen Wert.

Versuche es !!!

5
RolandoMySQLDBA

BTree ist für die einzeilige Suche nicht viel langsamer als Hash. Da BTree sehr effiziente Bereichsabfragen bietet, sollten Sie sich mit etwas anderem als BTree beschäftigen.

MySQL kann BTree-Blöcke sehr gut zwischenspeichern, sodass eine BTree-basierte Abfrage selten E/A-Vorgänge ausführen muss. Dies ist der größte Zeitverbraucher bei Abfragen.

2
Rick James