Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Consulta de contagem lenta () do MySQL MyISAM, apesar de cobrir o índice


Aqui está o que está acontecendo.
The SELECT COUNT (...) icd_index where icd='25000'

usará o índice, que é um BTree separado dos dados. Mas ele escaneia desta forma:
  1. Encontre a primeira entrada com icd='25000'. Isso é quase instantâneo.
  2. Faça a varredura para frente até encontrar uma alteração no icd. Isso fará a varredura apenas no índice, sem tocar nos dados. De acordo com o EXPLAIN, haverá cerca de 910.104 entradas de índice para verificar.

Agora vamos olhar para o BTree para esse índice. Com base nos campos no índice, cada linha terá exatamente 22 bytes, além de haver alguma sobrecarga (estimativa de 40%). Um bloco de índice MyISAM tem 1 KB (cf. 16 KB do InnoDB). Eu estimaria 33 linhas por bloco. 910.104/33 diz que cerca de 27K blocos precisam ser lidos para fazer o COUNT. (Observe COUNT(core_id) precisa verificar core_id por ser nulo, COUNT(*) não; esta é uma pequena diferença.) Ler 27K blocos em um disco rígido normal leva cerca de 270 segundos. Você teve sorte de fazê-lo em 60 segundos.

A segunda execução encontrou todos esses blocos no key_buffer (assumindo que key_buffer_size tenha pelo menos 27 MB), então não precisou esperar pelo disco. Por isso foi muito mais rápido. (Isso ignorando o cache de consulta, que você teve a sabedoria de liberar ou usar SQL_NO_CACHE.)

5.6 passa a ser irrelevante (mas obrigado por mencioná-lo), já que esse processo não mudou desde 4.0 ou antes (exceto que utf8 não existia; mais sobre isso abaixo).

Mudar para o InnoDB ajudaria de duas maneiras. A PRIMARY KEY seria 'agrupada' com os dados, não armazenada como um BTree separado. Assim, uma vez que os dados ou o PK são armazenados em cache, o outro fica imediatamente disponível. O número de blocos seria mais como 5K, mas seriam blocos de 16KB. Eles podem ser carregados mais rapidamente se o cache estiver frio.

Você pergunta "Eu preciso de um índice apenas no icd?" -- Bem, isso reduziria o tamanho do MyISAM BTree para cerca de 21 bytes por linha, então o BTree teria cerca de 21/27 do tamanho, não muito melhor (pelo menos para o situação de cache frio).

Outro pensamento é, se icd é sempre numérico e sempre numérico, para usar MEDIUMINT UNSIGNED , e adicione ZEROFILL se pode ter zeros à esquerda.

Ops, não notei o CHARACTER SET. (Eu consertei os números acima, mas deixe-me elaborar.)
  • CHAR(5) permite 5 caracteres .
  • ascii ocupa 1 byte por caractere .
  • utf8 ocupa até 3 bytes por caracteres .
  • Então, CHAR(5) CHARACTER SET utf8 leva 15 bytes sempre .

Alterando a coluna para CHAR(5) CHARACTER SET ascii reduziria para 5 bytes.

Mudar para MEDIUMINT UNSIGNED ZEROFILL reduziria para 3 bytes.

A redução dos dados aceleraria a E/S em uma quantidade aproximadamente proporcional (depois de permitir outros 6 bytes para os outros dois campos.