web-dev-qa-db-fra.com

Pourquoi MySQL n'a-t-il pas d'indices de hachage sur MyISAM ou InnoDB?

J'ai une application qui ne sélectionnera que sur l'égalité, et je pense que je devrais utiliser un index de hachage sur un index btree. À ma grande consternation, les indices de hachage ne sont pas pris en charge sur MyISAM ou InnoDB. Qu'est-ce qui se passe avec ça?

36
Alex

De nombreuses bases de données ne prennent pas en charge les index basés sur le hachage pas du tout.

Pour qu'une table de hachage soit efficace, vous devez connaître le nombre de lignes susceptibles d'être présentes, sinon la table de hachage de base sera beaucoup trop grande (beaucoup d'entrées vides, gaspillage d'espace et potentiellement d'E/S disque) ou trop petite, ce qui signifie que l'indirection est souvent utilisée (peut-être plusieurs niveaux d'indirection, ou pire encore si l'implémentation de hachage est à un seul niveau, vous pourriez finir par effectuer une recherche linéaire sur un bon nombre d'enregistrements), à quel point les choses ne sont probablement pas plus efficaces qu'un arbre basé sur index quand même.

Donc, pour être généralement utile (c'est-à-dire généralement meilleur que l'alternative), l'indice doit être reconstruit de temps en temps à mesure que les données augmentent (et rétrécissent), ce qui pourrait ajouter un surcoût intermittent significatif. C'est généralement bien avec les tables basées sur la mémoire car la reconstruction va probablement être assez rapide (car les données seront toujours en RAM et ne seront probablement pas massives de toute façon), mais la reconstruction d'un index volumineux sur le disque est une opération très lourde (et l'IIRC mySQL ne prend pas en charge les reconstructions d'index en direct, donc détient un verrou de table pendant l'opération).

Par conséquent, les index de hachage sont utilisés dans les tables de mémoire car ils sont généralement plus performants, mais les tables basées sur disque ne les prennent pas en charge car elles pourraient nuire aux performances et non pas être un bonus. Il n'y a rien pour empêcher les index de hachage d'être disponibles pour les tables sur disque, bien sûr, certaines bases de données do prennent en charge la fonctionnalité, mais elles ne sont probablement pas implémentées dans les tables ISAM/InnoDB car les responsables ne considèrent pas la fonctionnalité mérite d'être ajoutée (car le code supplémentaire à écrire et à maintenir ne vaut pas l'avantage dans ces quelques circonstances qu'il fait une différence significative). Peut-être que si vous êtes fortement en désaccord, vous pourriez leur parler et faire un bon argument pour la mise en œuvre de la fonctionnalité.

Si vous indexez de grandes chaînes, l'implémentation de votre propre index de pseudo-hachage (en stockant un hachage de la valeur ainsi que la valeur réelle et une indexation qui a une colonne) peut fonctionner, mais cela n'est certainement plus efficace pour les grandes chaînes (où le calcul de la valeur de hachage et la recherche de l'index de l'arborescence par cette valeur sont toujours plus rapides que la simple recherche d'un index de l'arborescence en utilisant les plus grandes valeurs à des fins de comparaison, et le stockage supplémentaire utilisé ne sera pas significatif), alors faites une analyse des performances avant d'implémenter ceci en production.

16
David Spillett

Sur une note connexe, vous pourriez trouver la discussion sur les types d'index des documents PostgreSQL intéressante. Il n'est plus présent dans les versions récentes de la documentation (en raison des optimisations ultérieures, je suppose), mais les plats à emporter pourraient être similaires pour MySQL (et la raison pour laquelle les index de hachage ne sont utilisés que pour les tables de tas):

http://www.postgresql.org/docs/8.1/static/indexes-types.html

Remarque: Les tests ont montré que les index de hachage de PostgreSQL ne fonctionnent pas mieux que les index B-tree, et la taille d'index et le temps de construction pour les index de hachage sont bien pires. De plus, les opérations d'index de hachage ne sont pas actuellement enregistrées en WAL, il peut donc être nécessaire de reconstruire les index de hachage avec REINDEX après un crash de la base de données. Pour ces raisons, l'utilisation de l'indice de hachage est actuellement déconseillée. De même, les index R-tree ne semblent pas avoir d'avantages en termes de performances par rapport aux opérations équivalentes des index Gist. Comme les index de hachage, ils ne sont pas journalisés WAL et peuvent nécessiter une réindexation après un crash de base de données. Bien que les problèmes avec les index de hachage puissent être résolus à terme, il est probable que le type d'index R-tree sera retiré dans une future version. Les utilisateurs sont encouragés à migrer les applications qui utilisent les index R-tree vers les index Gist.

Encore une fois, il est (version obsolète) spécifique à PostgreSQL, mais il devrait laisser entendre que le type d'index "naturel" ne donnera pas nécessairement des performances optimales.

6

Voici quelque chose d'intéressant:

Selon le livre MySQL 5.0 Certification Study Guide , Page 433, Section 29.5.1

Le moteur MEMORY utilise HASH par algorithme d'indexation par défaut.

Pour rire, j'ai essayé de créer une table InnoDB et une table MyISAM avec une clé primaire en utilisant HASH dans MySQL 5.5.12

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 ne s'est pas plaint.

MISE À JOUR

Mauvaises nouvelles !!! J'ai utilisé SHOW INDEXES FROM. Il dit que l'indice est BTREE.

La page CREATE INDEX de la syntaxe MySQL indique que seuls les moteurs de stockage MEMORY et NDB peuvent accueillir le HASH INDEX.

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)

Certaines personnes ont suggéré de suivre l'idée dans les pages 102-105 du livre " MySQL haute performance: optimisations, sauvegardes, réplication et plus " pour émuler l'algorithme de hachage.

La page 105 présente cet algorithme rapide et sale que j'aime:

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

Créez une colonne pour cela dans n'importe quelle table et indexez cette valeur.

Essaie !!!

5
RolandoMySQLDBA

BTree n'est pas beaucoup plus lent que Hash pour la recherche sur une seule ligne. Étant donné que BTree fournit des requêtes de plage très efficaces, pourquoi s'embêter avec autre chose que BTree.

MySQL fait un très bon travail de mise en cache des blocs BTree, donc une requête basée sur BTree doit rarement faire des E/S, ce qui est le plus gros consommateur de temps dans n'importe quelle requête.

2
Rick James