Mysql

wp_postmeta lento: índice em meta_key e meta_value para meta_query

A listagem do seu custom post type com meta_query rodava em 200 ms e hoje leva oito segundos. No slow query log é sempre a mesma coisa: JOIN em wp_postmeta varrendo dezenas de milhares de linhas. O motivo está no esquema do WordPress: o índice padrão é KEY meta_key (meta_key(191)), e meta_value fica de fora.

Primeiro olhe o EXPLAIN da query real

Não adivinhe o SQL: pegue o que o WP_Query montou. Salve num arquivo com a tag de abertura do PHP — sem ela o eval-file só imprime o texto:

<?php
$q = new WP_Query([
    'post_type'      => 'imovel',
    'posts_per_page' => 12,
    'meta_query'     => [['key' => 'cidade', 'value' => 'Curitiba']],
]);
echo $q->request;

# sudo -u www-data wp eval-file /tmp/q.php

Copie a saída, ponha EXPLAIN na frente e rode com sudo -u www-data wp db query. Na linha de wp_postmeta o key vem como meta_key, com número gordo em rows e “Using where”: o MySQL pega toda linha cuja meta_key é cidade e só depois compara o valor, uma a uma.

O índice composto

ALTER TABLE wp_postmeta
  ADD INDEX meta_key_value (meta_key(191), meta_value(20)),
  ALGORITHM=INPLACE, LOCK=NONE;

Os 191 caracteres são o mesmo prefixo que o núcleo já usa em meta_key, e vinte de meta_value bastam para separar os candidatos antes da comparação final. O ALGORITHM=INPLACE com LOCK=NONE mantém a tabela gravável durante a criação, mas só no InnoDB: em MyISAM o MySQL copia a tabela e trava escrita o tempo todo. Confira a engine e tenha um dump antes.

O preço que ninguém conta

Índice não é de graça. wp_postmeta é a tabela mais escrita num WordPress com campos customizados, e agora todo INSERT mantém mais um índice: importação de catálogo e cron que grava meta em massa ficam mais lentos. Meça:

SELECT ROUND(data_length/1048576) AS dados_mb, ROUND(index_length/1048576) AS indice_mb
FROM information_schema.TABLES
WHERE table_schema = DATABASE() AND table_name = 'wp_postmeta';

E não espere milagre em ordenação: prefixo de índice não ordena nada. Se a meta é numérica e o orderby usa meta_value_num, o ganho é pequeno; aí o caminho é tirar o campo quente do postmeta para uma coluna própria.

Post Recentes

Criar swapfile no Ubuntu: parar o OOM killer de matar o MySQL

VPS de 2 GB onde o mysqld morre de madrugada e o site volta com erro de conexão. Como confirmar…

7 horas atrás

Grupos nomeados no preg_match: pare de contar índice de $matches

Regex com cinco parênteses e ninguém lembra se a data é $matches[2] ou $matches[3]. Nomeie os grupos e leia o…

22 horas atrás

Token de integração da API do Magento 2: gerar e usar no header Bearer

Erro The consumer isn't authorized to access resources ao chamar a REST do Magento 2.4.8? Ative a integração, libere os…

1 dia atrás

Rate limit no wp-login.php com nginx: barrar força bruta antes do PHP

Bots batendo centenas de POST por minuto no login derrubam a CPU. Configure limit_req_zone no nginx e devolva 429 antes…

2 dias atrás

Anonimizar IP no nginx: mascarar o último octeto do log para a LGPD

Seu access.log guarda o IP inteiro dos visitantes há meses sem necessidade. Mascare o último octeto com map e log_format…

3 dias atrás

Prompt injection em campo de usuário: isole a observação do pedido

A observação do pedido chegou com ordem embutida para gerar cupom. Delimite o texto do cliente e trate a resposta…

3 dias atrás