¿Por qué MySQL no tiene índices hash en MyISAM o InnoDB?


35

Tengo una aplicación que solo seleccionará la igualdad, y creo que debería usar un índice hash sobre un índice btree. Para mi consternación, los índices hash no son compatibles con MyISAM o InnoDB. ¿Que pasa con eso?


2
Mysql tampoco admite índices basados ​​en funciones, índices de mapas de bits, etc. Solo porque es mysql ;-)

1
Me di cuenta de que los índices hash eran tan ... fundamentales ... supongo que hay una razón específica relacionada con la implementación.

1
@Alex: apuesto a que esa razón es "pereza" y "burocracia", pero esperemos las respuestas))


Agregué un buen algoritmo HASH del Libro MySQL de alto rendimiento al final de mi respuesta.
RolandoMySQLDBA

Respuestas:


16

Muchas bases de datos no admiten índices basados en hash en absoluto .

Para que una tabla hash sea eficiente, necesita saber el número de filas que probablemente estén presentes; de lo contrario, la tabla hash base será demasiado grande (muchas entradas vacías, desperdicio de espacio y potencialmente IO de disco) o demasiado pequeño, lo que significa que a menudo se usa la indirección (posiblemente múltiples niveles de indirección, o peor aún si la implementación de hash es de un solo nivel, podría terminar realizando una búsqueda lineal en un número considerable de registros) en cuyo punto las cosas probablemente no sean más eficientes que un árbol índice de todos modos.

Por lo tanto, para ser generalmente útil (es decir, generalmente mejor que la alternativa), el índice debe reconstruirse ocasionalmente a medida que los datos crecen (y se reducen), lo que podría agregar una sobrecarga intermitente significativa. Esto generalmente está bien con las tablas basadas en memoria, ya que la reconstrucción probablemente será bastante rápida (ya que los datos siempre estarán en RAM y no es probable que sean masivos en cualquier caso), pero reconstruir un índice grande en el disco es un operación muy pesada (y IIRC mySQL no admite reconstrucciones de índice en vivo, por lo que mantiene un bloqueo de tabla durante la operación).

Por lo tanto, los índices hash se usan en las tablas de memoria, ya que generalmente tienen un mejor rendimiento, pero las tablas basadas en disco no los admiten, ya que podrían ser en detrimento del rendimiento, no una ventaja. No hay nada que índices hash dejar de ser puesto a disposición de las tablas de base de disco, por supuesto, sin duda algunas bases de datos hacen compatible con la función, pero se supone que no se implementa en ISAM / tablas InnoDB como los mantenedores no consideran el valor de función de la adición (como el código adicional para escribir y mantener no vale la pena el beneficio en esas pocas circunstancias que hace una diferencia significativa). Quizás si no está de acuerdo, podría hablar con ellos y presentar un buen caso para la implementación de la función.

Si está indexando cadenas grandes, entonces puede implementar su propio índice pseudo-hash (almacenando un hash del valor, así como el valor real, y la indexación que tiene una columna), pero esto definitivamente es más eficiente para cadenas grandes (donde calcular el valor hash y buscar en el índice de árbol por este valor siempre es más rápido que simplemente buscar un índice de árbol usando los valores más grandes para comparar, y el almacenamiento adicional utilizado no será significativo), así que haga un análisis de rendimiento antes de implementar esta en produccion.


¿Hay alguna manera de permitir que se vuelva a hacer el hash (reconstrucción) de lado a lado sin bloquear toda la mesa?
Pacerier

@Pacerier: no lo sé con MySQL (aunque podrían haber agregado la función desde la última vez que la usé, así que revise la documentación). Incluso cuando un DBMS admite la creación / reconstrucción de índices en línea, no es la opción predeterminada. Lo que se bloquea variará según: algunos mantendrán un bloqueo de escritura en la tabla para que otras transacciones no se retrasen si solo están leyendo, algunos DMBS eliminarán un bloqueo de tabla completo. Si necesita una reconstrucción en línea, verifique la documentación de cada DBMS antes de elegir cuál usar.
David Spillett

Por lo general, la reconstrucción solo es necesaria cuando la longitud de los datos se duplica. ¿Realmente tienen que preocuparse de que la longitud de los datos se duplique cada minuto? (normalmente ocurre muy raramente cuando la base de datos crece lo suficiente como para que esto sea una preocupación)
SOFe

6

En una nota relacionada, puede encontrar interesante la discusión sobre los tipos de índice de los documentos de PostgreSQL. Ya no está presente en las versiones recientes de los documentos (debido a las optimizaciones posteriores, supongo), pero la conclusión podría ser similar para MySQL (y la razón por la cual los índices hash solo se usan para tablas de montón):

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

Nota: Las pruebas han demostrado que los índices hash de PostgreSQL no funcionan mejor que los índices B-tree, y el tamaño del índice y el tiempo de construcción de los índices hash es mucho peor. Además, las operaciones de índice hash no están actualmente registradas en WAL, por lo que es posible que sea necesario reconstruir los índices hash con REINDEX después de un bloqueo de la base de datos. Por estos motivos, actualmente no se recomienda el uso del índice hash. Del mismo modo, los índices del árbol R no parecen tener ventajas de rendimiento en comparación con las operaciones equivalentes de los índices GiST. Al igual que los índices hash, no están registrados en WAL y pueden necesitar reindexarse ​​después de un bloqueo de la base de datos. Si bien los problemas con los índices hash pueden corregirse eventualmente, es probable que el tipo de índice R-tree se retire en una versión futura. Se alienta a los usuarios a migrar aplicaciones que usan índices de árbol R a índices GiST.

Nuevamente, es (versión obsoleta) específica de PostgreSQL, pero debería indicar que el tipo de índice "natural" no necesariamente rendirá un rendimiento óptimo.


5

Aquí hay algo interesante:

De acuerdo con el libro MySQL 5.0 Certification Study Guide , página 433, sección 29.5.1

El motor MEMORY utiliza HASH por defecto el algoritmo de indexación.

Para reír, intenté crear una tabla InnoDB y una tabla MyISAM con una clave principal usando HASH en 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 no se quejó.

ACTUALIZAR

Malas noticias !!! Usé SHOW INDEXES FROM. Dice que el índice es BTREE.

La página MySQL de la sintaxis CREATE INDEX establece que solo los motores de almacenamiento MEMORY y NDB pueden acomodar el 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)

Algunas personas sugirieron seguir la idea en las páginas 102-105 del libro " MySQL de alto rendimiento: optimizaciones, copias de seguridad, replicación y más " para emular el algoritmo hash.

La página 105 presenta este algoritmo rápido y sucio que me gusta:

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

Haga una columna para esto en cualquier tabla e indexe este valor.

Darle una oportunidad !!!


55
Antes de usar la técnica de pseudo-hash-index en producción, realice un análisis de rendimiento en ella. Para cadenas grandes, puede hacer una gran diferencia, pero de todos modos termina navegando por un índice de árbol, y tiene que hacer comparaciones adicionales para encontrar la fila correcta de las que se encuentran que coinciden con el hash, por lo que para valores pequeños calculando los valores de hash y almacenarlos simplemente no vale la pena. Esto no es realmente un índice hash en absoluto, simplemente está reduciendo el trabajo realizado caminando por el árbol (ya que cada comparación considera menos bytes, por ejemplo, comparar INT de 8 bytes en lugar de cadenas de x00 bytes).
David Spillett

@David Spillett En esto, estoy totalmente de acuerdo con usted. También se sugieren otras estrategias de indexación en el mismo libro en el Capítulo 11 "Estrategias de indexación para un alto rendimiento". Como un impulso adicional a mi respuesta, el libro en realidad menciona el uso de un índice agrupado que almacena la fila y el índice BTree en la misma estructura. Esto puede acelerar el trabajo reducido que mencionó. Desafortunadamente, los aros por los que tienes que saltar y que acabas de mencionar son algo inevitables. A +1 de mi parte en su comentario, señor. De hecho, +1 por su respuesta también.
RolandoMySQLDBA

@RolandoMySQLDBA ¿Puede elaborar más sobre la parte de "hashing personalizado", el último párrafo no parece dar mucha pista ...
Pacerier

2

BTree no es mucho más lento que Hash para la búsqueda de una sola fila. Dado que BTree proporciona consultas de rango muy eficientes, ¿por qué molestarse con otra cosa que no sea BTree?

MySQL hace un muy buen trabajo al almacenar en caché los bloques BTree, por lo que una consulta basada en BTree rara vez tiene que hacer E / S, que es el mayor consumidor de tiempo en cualquier consulta.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.