Perché MySQL non ha indici hash su MyISAM o InnoDB?


35

Ho un'applicazione che selezionerà solo sull'uguaglianza e immagino che dovrei usare un indice hash su un indice btree. Con mio grande sgomento, gli indici hash non sono supportati su MyISAM o InnoDB. Cosa succede con quello?


2
Mysql inoltre non supporta indici basati su funzioni, indici bitmap, ecc. Ecc. Solo perché è mysql ;-)

1
ho appena pensato che gli indici di hash fossero così ... fondamentali ... suppongo che ci sia un motivo specifico relativo all'implementazione.

1
@Alex: Scommetto che la ragione è "pigrizia" e "burocrazia" ma aspettiamo le risposte))


Ho aggiunto un bel algoritmo HASH dal MySQL Book ad alte prestazioni alla fine della mia risposta.
RolandoMySQLDBA

Risposte:


16

Molti database non supportano indici basati hash a tutti .

Affinché una tabella di hash sia efficiente è necessario conoscere il numero di righe che potrebbero essere presenti altrimenti la tabella di hash di base sarà troppo grande (molte voci vuote, spreco di spazio e potenzialmente IO del disco) o significato troppo piccolo che viene spesso utilizzato il riferimento indiretto (possibilmente più livelli di riferimento indiretto, o peggio ancora se l'implementazione dell'hash è a livello singolo si potrebbe finire per eseguire una ricerca lineare su un discreto numero di record) a quel punto le cose probabilmente non sono più efficienti di un albero indice comunque.

Quindi, per essere generalmente utile (cioè di solito migliore dell'alternativa), l'indice deve essere ricostruito occasionalmente man mano che i dati crescono (e si riducono), il che potrebbe aggiungere un significativo sovraccarico intermittente. Questo di solito va bene con le tabelle basate sulla memoria poiché la ricostruzione sarà probabilmente piuttosto veloce (poiché i dati saranno sempre nella RAM e non è probabile che siano enormi in ogni caso), ma ricostruire un indice di grandi dimensioni su disco è un operazione molto pesante (e IIRC mySQL non supporta ricostruzioni di indici live, quindi mantiene un blocco della tabella durante l'operazione).

Quindi gli indici di hash vengono utilizzati nelle tabelle di memoria in quanto in genere hanno prestazioni migliori, ma le tabelle basate su disco non li supportano in quanto potrebbero essere un danno per le prestazioni, non un bonus. Non c'è nulla per fermare gli indici hash vengono messi a disposizione per le tabelle basate su disco, naturalmente, senza dubbio alcuni database fanno supportare la funzionalità, ma presumibilmente non sono implementato in ISAM / tabelle InnoDB come i manutentori non considerano la caratteristica pena di aggiungere (come il codice extra da scrivere e mantenere non vale il vantaggio in quelle poche circostanze che fa una differenza significativa). Forse, se non sei d'accordo, potresti parlare con loro e fare un buon caso per l'implementazione della funzione.

Se stai indicizzando stringhe di grandi dimensioni, l'implementazione del tuo indice pseudo-hash (memorizzando un hash del valore, nonché il valore effettivo e l'indicizzazione con colonna) potrebbe funzionare, ma questo è sicuramente più efficiente per le stringhe di grandi dimensioni (dove calcolare il valore di hash e cercare l'indice dell'albero con questo valore è sempre probabilmente più veloce della semplice ricerca di un indice dell'albero usando i valori più grandi per il confronto e lo spazio di archiviazione aggiuntivo utilizzato non sarà significativo) quindi fare qualche analisi delle prestazioni prima dell'implementazione questo in produzione.


Esiste un modo per consentire il re-hashing (ricostruzione) da eseguire fianco a fianco senza bloccare l'intero tavolo?
Pacerier,

@Pacerier: non che io sappia con MySQL (anche se avrebbero potuto aggiungere la funzione dall'ultima volta che l'ho usata, quindi controlla la documentazione). Anche se un DBMS supporta la creazione / ricostruzione di indici online non è l'opzione predefinita. Ciò che viene bloccato varierà a: alcuni terranno un blocco di scrittura sul tavolo ad altre transazioni non verranno ritardati se stanno solo leggendo, alcuni DMBS elimineranno un blocco completo del tavolo. Se è necessario ricostruire online, consultare la documentazione di ciascun DBMS prima di scegliere quale utilizzare.
David Spillett,

Di solito la ricostruzione è necessaria solo quando la lunghezza dei dati è raddoppiata. Devono davvero preoccuparsi che la lunghezza dei dati venga raddoppiata ogni minuto? (normalmente succede molto raramente quando il database diventa abbastanza grande da essere
fonte di

6

In una nota correlata, potresti trovare interessante la discussione sui tipi di indice dai documenti PostgreSQL. Non è più presente nelle ultime versioni dei documenti (a causa di ottimizzazioni successive, lo prendo), ma il takeaway potrebbe essere simile per MySQL (e il motivo per cui gli indici hash vengono utilizzati solo per le tabelle heap):

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

Nota: i test hanno dimostrato che gli indici hash di PostgreSQL non funzionano meglio degli indici B-tree, e la dimensione dell'indice e il tempo di costruzione degli indici hash sono molto peggiori. Inoltre, le operazioni dell'indice hash non sono attualmente registrate da WAL, pertanto potrebbe essere necessario ricostruire gli indici hash con REINDEX dopo un arresto anomalo del database. Per questi motivi, l'utilizzo dell'indice hash è attualmente sconsigliato. Analogamente, gli indici R-tree non sembrano avere alcun vantaggio in termini di prestazioni rispetto alle operazioni equivalenti degli indici GiST. Come gli indici hash, non sono registrati WAL e potrebbe essere necessario reindicizzare dopo un arresto anomalo del database. Sebbene i problemi con gli indici hash possano eventualmente essere risolti, è probabile che il tipo di indice R-tree verrà ritirato in una versione futura. Gli utenti sono incoraggiati a migrare le applicazioni che utilizzano gli indici R-tree verso gli indici GiST.

Ancora una volta, è (versione obsoleta di) PostgreSQL specifico, ma dovrebbe suggerire che il tipo di indice "naturale" non produrrà necessariamente prestazioni ottimali.


5

Ecco qualcosa di interessante:

Secondo il libro MySQL 5.0 Guida allo studio di certificazione , Pagina 433, Sezione 29.5.1

Il motore MEMORY utilizza HASH in base all'algoritmo di indicizzazione predefinito.

Per ridere, ho provato a creare una tabella InnoDB e una tabella MyISAM con una chiave primaria usando HASH in 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 non si è lamentato.

AGGIORNARE

Cattive notizie !!! Ho usato MOSTRA INDICI DA. Dice che l'indice è BTREE.

La pagina MySQL della sintassi di CREATE INDEX afferma che solo i motori di archiviazione MEMORY e NDB possono ospitare 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)

Alcune persone hanno suggerito di seguire l'idea nelle pagine 102-105 del libro " MySQL ad alte prestazioni: ottimizzazioni, backup, replica e altro " per emulare l'algoritmo hash.

Pagina 105 presenta questo algoritmo rapido e sporco che mi piace:

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

Crea una colonna per questo in qualsiasi tabella e indicizza questo valore.

Provaci !!!


5
Prima di utilizzare la tecnica dell'indice pseudo-hash in produzione, eseguire alcune analisi delle prestazioni su di esso. Per stringhe di grandi dimensioni può fare una grande differenza, ma alla fine si naviga in un indice dell'albero alla fine, e si hanno ulteriori confronti da fare per trovare la riga giusta tra quelle trovate che corrispondono all'hash, quindi per i piccoli valori che calcolano i valori di hash e la loro memorizzazione non ne vale la pena. Questo non è affatto un indice di hash, stai semplicemente riducendo il lavoro fatto camminando sull'albero (poiché ogni confronto sta prendendo in considerazione meno byte, ad esempio confrontando INT a 8 byte anziché stringhe di x00 byte).
David Spillett,

@David Spillett In questo, sono assolutamente d'accordo con te. Altre strategie di indicizzazione sono anche suggerite nello stesso libro nel capitolo 11 "Strategie di indicizzazione per alte prestazioni". Come ulteriore impulso alla mia risposta, il libro menziona effettivamente l'utilizzo di un indice cluster che memorizza la riga e l'indice BTree nella stessa struttura. Questo potrebbe accelerare il lavoro ridotto che hai citato. Sfortunatamente, i cerchi che devi saltare che hai appena menzionato sono in qualche modo inevitabili. Un +1 da parte mia sul tuo commento, signore !!! In effetti, +1 anche per la tua risposta.
RolandoMySQLDBA

@RolandoMySQLDBA Puoi approfondire la parte su "hashing personalizzato", l'ultimo paragrafo non sembra dare molto indizio ...
Pacerier,

2

BTree non è molto più lento di Hash per la ricerca a riga singola. Poiché BTree fornisce query di portata molto efficienti, perché preoccuparsi di qualcosa di diverso da BTree.

MySQL fa un ottimo lavoro di memorizzazione nella cache dei blocchi BTree, quindi raramente una query basata su BTree deve eseguire operazioni di I / O, che è il più grande consumatore di tempo in qualsiasi query.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.