Executando MySQL com InnoDB:
Tenho uma SELECT wide_table.*
consulta que desejo refinar, SELECT wide_table.id
pois é tudo o que o código de chamada precisa. Testando, descobri que o tempo de execução com *
é mais rápido do que com id
(embora o tempo para transferir o resultado pela rede seja mais rápido com a versão "refinada").
Por que isso seria o caso?
A consulta (com nomes alterados para proteger os inocentes) é:
SELECT
`things`.*
FROM
`things`
WHERE
`things`.`active` = 1
AND (owner_id IS NOT NULL
AND owner_id > 0)
AND ((`things`.`status` IN (0 , 1)
OR `things`.`status` IS NULL))
AND (date < '2015-07-11 00:00:00');
Há um índice composto em active
e date
, que está sendo usado em ambas as versões.
Para referência, a saída de SHOW CREATE TABLE
(com colunas irrelevantes omitidas):
CREATE TABLE `things` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`active` tinyint(1) DEFAULT '0',
`date` datetime DEFAULT NULL,
`owner_id` int(11) DEFAULT '0',
`status` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_things_on_active_and_date` (`active`,`date`),
KEY `index_things_on_date` (`date`),
KEY `index_things_on_owner_id` (`owner_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1862 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Limites
Depois de brincar um pouco com isso, descobri que a comparação é afetada pelo limite imposto à consulta. Com limites grandes, >200, a *
versão relata um tempo de execução mais rápido. À medida que o limite diminui <200, a id
versão ganha vantagem. Ainda não tenho certeza do que fazer com isso...
EXPLIQUE
A execução EXPLAIN
em ambas as versões da consulta gera uma saída idêntica, com select_type: SIMPLE
e key: index_things_on_active_and_date
.
O plano EXPLAIN para
SELECT *
eSELECT id
, que você disse serem idênticos, seria separado pelo número de linhas acessadas. Como as linhas estão sendo acessadas? Através doindex_things_on_active_and_date
índice. OSIMPLE
no EXPLAIN significa que é uma varredura. Em ambos os casos, é uma varredura de índice baseadaactive=1
edate < '2015-07-11 00:00:00'
Como ocorre a varredura de índice?
active
edate
, a varredura do intervalo ocorreria a partir de duas colunas.WHERE
cláusula. Você está recuperandoowner_id
estatus
verificando os valores. Isso requer que você acesse toda a linha.SELECT *
significa que você torna toda a linha parte do conjunto de resultadosSELECT id
significa que você torna o id parte do conjunto de resultadosPara onde isso está levando?
id
valores levaria mais tempo para ser construído do que para toda a linha que você lê de qualquer maneira. Você mesmo testou isso empiricamente e descobriu o seguinte:< 200 rows
->SELECT id
é mais rápido> 200 rows
->SELECT *
é mais rápido= 200 rows
->SELECT id
eSELECT *
são quase iguaisHá algo mais que você precisa saber sobre o InnoDB
Isso informa que o índice
index_things_on_active_and_date
realmente tem três colunas: 1)active
, 2)date
, 3)id
.Você provavelmente está dizendo: Por que
SELECT *
correr é melhor assimSELECT id
? (que é a pergunta original) Volta ao que eu disse: a cláusula WHERE está fazendo com que o Query Optimizer verifique colunas não indexadasstatus
eowner_id
. Você está criando um trabalho adicional para verificar uma entrada de índice e algo da linha que está indexada.Se você criar este índice
e execute as duas consultas, então você verá a vantagem,
SELECT id
não importa quantas linhas você esteja acessando. Por quê ? Porque todas as colunas na cláusula WHERE são verificadas apenas no índice . Esse tipo de índice é chamado de índice de cobertura.Aqui estão alguns bons links sobre como cobrir índices
Eu mencionei esses links em algumas das minhas respostas:
Feb 10, 2012
Tempo de consulta extremamente longo inesperado (cerca de 5 minutos usando WHEN-INs aninhados)Oct 17, 2012
: Combinando colunas no índiceJan 11, 2013
: MySQL: Para usar o mecanismo MYISAM ou INNODB? (reviravolta na história incluída)