Aqui está minha tabela com ~ 10.000.000 linhas de dados
CREATE TABLE `votes` (
`subject_name` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
`subject_id` int(11) NOT NULL,
`voter_id` int(11) NOT NULL,
`rate` int(11) NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`subject_name`,`subject_id`,`voter_id`),
KEY `IDX_518B7ACFEBB4B8AD` (`voter_id`),
KEY `subject_timestamp` (`subject_name`,`subject_id`,`updated_at`),
KEY `voter_timestamp` (`voter_id`,`updated_at`),
CONSTRAINT `FK_518B7ACFEBB4B8AD` FOREIGN KEY (`voter_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Aqui estão os índices de cardinalidades
Então, quando eu faço essa consulta:
SELECT SQL_NO_CACHE * FROM votes WHERE
voter_id = 1099 AND
rate = 1 AND
subject_name = 'medium'
ORDER BY updated_at DESC
LIMIT 20 OFFSET 100;
Eu estava esperando que ele usa índice, voter_timestamp
mas o mysql escolhe usar isso:
explain select SQL_NO_CACHE * from votes where subject_name = 'medium' and voter_id = 1001 and rate = 1 order by updated_at desc limit 20 offset 100;`
type:
index_merge
possible_keys:
PRIMARY,IDX_518B7ACFEBB4B8AD,subject_timestamp,voter_timestamp
key:
IDX_518B7ACFEBB4B8AD,PRIMARY
key_len:
102,98
ref:
NULL
rows:
9255
filtered:
10.00
Extra:
Using intersect(IDX_518B7ACFEBB4B8AD,PRIMARY); Using where; Using filesort
E eu tenho 200-400ms de tempo de consulta.
Se eu forçá-lo a usar o índice correto, como:
SELECT SQL_NO_CACHE * FROM votes USE INDEX (voter_timestamp) WHERE
voter_id = 1099 AND
rate = 1 AND
subject_name = 'medium'
ORDER BY updated_at DESC
LIMIT 20 OFFSET 100;
Mysql pode retornar os resultados em 1-2ms
e aqui está a explicação:
type:
ref
possible_keys:
voter_timestamp
key:
voter_timestamp
key_len:
4
ref:
const
rows:
18714
filtered:
1.00
Extra:
Using where
Então, por que o mysql não escolheu o voter_timestamp
índice para minha consulta original?
O que eu tinha tentado é analyze table votes
, optimize table votes
, queda nesse índice e adicioná-lo novamente, mas mysql ainda usa o índice errado. não entendo bem qual é o problema.
(voter_id, updated_at)
. Outro índice seria (voter_id, subject_name, updated_at)
ou (subject_name, voter_id, updated_at)
(sem a taxa).
subject_name='medium' and rate=1
)
LIMIT
mesmo, a ORDER BY
menos que o índice satisfaça primeiro toda a filtragem. Ou seja, sem as quatro colunas completas, ele coletará todas as linhas relevantes, classificará todas e depois selecionará a LIMIT
. Com o índice de 4 colunas, a consulta pode evitar a classificação e parar depois de ler apenas as LIMIT
linhas.
subject_name = "medium"
parte ele também pode escolher o indicador direito, sem necessidade de índicerate