Mysql
 sql >> Database >  >> RDS >> Mysql

Query MySQL MyISAM slow count() nonostante la copertura dell'indice

Ecco cosa sta succedendo.

The SELECT COUNT (...) icd_index where icd='25000'

utilizzerà l'indice, che è un BTree separato dai dati. Ma lo scansiona in questo modo:

  1. Trova la prima voce con icd='25000'. Questo è quasi istantaneo.
  2. Esegui la scansione fino a quando non trova una modifica in icd. Questo eseguirà la scansione solo nell'indice, senza toccare i dati. Secondo EXPLAIN, ci saranno circa 910.104 voci di indice su cui eseguire la scansione.

Ora diamo un'occhiata al BTree per quell'indice. In base ai campi nell'indice, ogni riga sarà esattamente 22 byte, più ci sarà un sovraccarico (stima 40%). Un blocco di indice MyISAM è di 1 KB (cfr. 16 KB di InnoDB). Stimerei 33 righe per blocco. 910.104/33 dice che è necessario leggere circa 27.000 blocchi per eseguire il COUNT. (Nota COUNT(core_id) deve controllare core_id per essere nullo, COUNT(*) non; questa è una piccola differenza.) La lettura di blocchi da 27K su un disco rigido normale richiede circa 270 secondi. Sei stato fortunato a farlo in 60 secondi.

La seconda esecuzione ha trovato tutti quei blocchi nel key_buffer (supponendo che key_buffer_size sia almeno 27 MB), quindi non ha dovuto aspettare il disco. Quindi è stato molto più veloce. (Questo ignorando la cache delle query, che hai avuto la saggezza di svuotare o utilizzare SQL_NO_CACHE.)

5.6 sembra essere irrilevante (ma grazie per averlo menzionato), poiché questo processo non è cambiato dalla 4.0 o prima (tranne che utf8 non esisteva; ne parleremo più avanti).

Il passaggio a InnoDB aiuterebbe in un paio di modi. La CHIAVE PRIMARIA verrebbe "raggruppata" con i dati, non archiviata come un Btree separato. Quindi, una volta che i dati o il PK sono memorizzati nella cache, l'altro è immediatamente disponibile. Il numero di blocchi sarebbe più simile a 5K, ma sarebbero blocchi di 16KB. Questi potrebbero essere caricabili più velocemente se la cache è fredda.

Chiedi "Ho bisogno di un indice solo su icd?" -- Bene, ciò ridurrebbe la dimensione di MyISAM BTree a circa 21 byte per riga, quindi BTree sarebbe circa 21/27 della dimensione, non molto miglioramento (almeno per il situazione di cold-cache).

Un altro pensiero è, se icd è sempre numerico e sempre numerico, per usare MEDIUMINT UNSIGNED e virare su ZEROFILL se può avere zeri iniziali.

Spiacenti, non ho notato il SET DI PERSONAGGI. (Ho corretto i numeri sopra, ma lasciami elaborare.)

  • CHAR(5) consente 5 caratteri .
  • ascii occupa 1 byte per carattere .
  • utf8 occupa fino a 3 byte per caratteri .
  • Quindi, CHAR(5) CHARACTER SET utf8 richiede 15 byte sempre .

Modifica della colonna in CHAR(5) CHARACTER SET ascii lo ridurrebbe a 5 byte.

Cambiarlo in MEDIUMINT UNSIGNED ZEROFILL lo ridurrebbe a 3 byte.

La riduzione dei dati accelererebbe l'I/O di una quantità approssimativamente proporzionale (dopo aver consentito altri 6 byte per gli altri due campi.