it-swarm.it

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

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?

36
Alex

Molti database non supportano gli indici basati su hash affatto.

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 in RAM e non è probabile che siano enormi in ogni caso), ma la ricostruzione di un indice di grandi dimensioni su disco è un'operazione molto pesante (e IIRC mySQL non supporta le ricostruzioni di indici in tempo reale, 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 che impedisca di rendere disponibili gli indici hash per le tabelle basate su disco, senza dubbio alcuni database do supportano la funzione, ma presumibilmente non sono implementati nelle tabelle ISAM/InnoDB poiché i manutentori non considerano la funzione vale la pena aggiungere (poiché il codice aggiuntivo 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.

16
David Spillett

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. Allo stesso modo, 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 negli indici Gist.

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

6

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.

pagina MySQL della sintassi CREATE INDEX indica 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
RolandoMySQLDBA

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 una query basata su BTree deve raramente eseguire operazioni di I/O, che è il più grande consumatore di tempo in qualsiasi query.

2
Rick James