Como usar Query Monitor para detectar consultas lentas en WordPress

Contents

Introducción

Query Monitor es una de las herramientas de depuración más potentes para WordPress. Permite detectar consultas a la base de datos lentas, identificar su origen (plugin, tema o núcleo), ver pilas de llamadas, duplicados, tiempos por consulta y muchos otros puntos críticos de rendimiento. Este tutorial profundiza en cómo usar Query Monitor para localizar y arreglar consultas lentas, con ejemplos prácticos, técnicas de diagnóstico y soluciones aplicables en producción.

Qué muestra Query Monitor (resumen funcional)

  • Tiempo total de consultas por petición y por componente.
  • Lista de consultas con tiempo, número de filas afectadas, si son duplicadas y qué archivo/función las originó.
  • Pila de llamadas (call stack) para cada consulta, permitiendo localizar la línea exacta en PHP que ejecutó la consulta.
  • Consultas agrupadas por componente (tema, plugin, núcleo) y por caller (función/archivo).
  • Otras secciones útiles: peticiones HTTP, hooks, peticiones AJAX/REST, transients, errores PHP y cargas de scripts/styles.

Instalación y configuración básica

  1. Instala y activa el plugin Query Monitor desde el repositorio oficial o subiendo el zip.
  2. Accede a una página del frontend o backend en la barra de administración verás el icono de Query Monitor con datos resumidos (tiempo, memoria, consultas).
  3. Abre el menú de Query Monitor para ver las secciones. Si necesitas, ajusta el umbral de consulta lenta en las opciones del plugin (opciones del plugin o el pequeño enlace de configuración en la UI de Query Monitor) para que destaque las consultas que consideres relevantes.

Reproducción del problema

Siempre reproduce la petición lenta mientras tienes Query Monitor activo. Si es en frontend, carga la misma URL si es una acción AJAX o REST, reproduce la llamada con los mismos parámetros. Query Monitor captura los datos de la petición actual, por lo que solo verás lo que sucede en esa ejecución.

Flujo de trabajo para identificar consultas lentas

  1. Reproduce la petición problemática y abre Query Monitor.
  2. En la sección Queries observa: tiempo total de consultas y la lista ordenada por tiempo. Identifica la(s) consulta(s) con mayor tiempo.
  3. Para la consulta más pesada, haz click en el enlace del Caller o en la propia consulta para ver la pila de llamadas y localizar el archivo/línea PHP responsable.
  4. Revisa si la consulta es duplicada (Query Monitor marca duplicados). Las duplicaciones frecuentes significan potencial para cachear o juntar llamadas.
  5. Si la consulta involucra meta keys, joins complejos o subconsultas, considera optimizaciones (índices, reducción de JOINs, limitar campos).
  6. Aplica cambios (ej.: modificar consulta, añadir índice, aplicar cache) y vuelve a reproducir para comprobar mejora con Query Monitor.

¿Qué fijarse en la lista de consultas?

  • Tiempo (ms): consulta con mayor coste.
  • Caller: archivo y función que la generó (pila de llamadas).
  • Component: si vino desde un plugin, tema o núcleo.
  • Tipo de consulta: SELECT, INSERT, UPDATE, DELETE.
  • Duplicados: mismas queries ejecutadas varias veces en la misma petición.
  • Filas devueltas: si devuelve muchas filas puede indicar necesidad de paginación o filtros.

Ejemplos concretos y cómo resolverlos

Ejemplo 1: Consulta lenta por meta_query con JOINs (antes)

Un WP_Query con meta_query que causa joins en la tabla wp_postmeta y devuelve muchos resultados:

args = array(
  post_type => producto,
  meta_query => array(
    array(
      key => precio,
      value => array(100, 500),
      compare => BETWEEN,
      type => NUMERIC
    ),
  ),
  posts_per_page => 10,
)
query = new WP_Query(args)

Query Monitor mostrará la consulta SQL resultante con JOIN a wp_postmeta y posiblemente un tiempo elevado si la tabla wp_postmeta es grande.

Soluciones recomendadas

  • Si solo necesitas IDs, usa fields => ids para evitar SELECT .
  • Usa índices en meta_key o añade una tabla o estructura adaptada a consultas frecuentes (cuando wp_postmeta crece mucho).
  • Aplica caching (transients u object cache) para consultas que no cambian con frecuencia.

Ejemplo 1 (optimización: solicitar solo IDs y cachear)

cache_key = productos_precio_100_500
ids = get_transient( cache_key )

if ( false === ids ) {
  args = array(
    post_type => producto,
    meta_query => array(
      array(
        key => precio,
        value => array(100, 500),
        compare => BETWEEN,
        type => NUMERIC
      ),
    ),
    fields => ids,
    posts_per_page => 10,
  )
  query = new WP_Query(args)
  ids = query->posts
  set_transient( cache_key, ids, HOUR_IN_SECONDS )
}
// Usar ids al renderizar

Con Query Monitor comprobarás reducción importante del tiempo al cargar la misma página repetidas veces porque la consulta se cachea.

Ejemplo 2: Consulta SELECT sin índices (solución con índice)

Si Query Monitor muestra una consulta SELECT sobre una columna sin índice y tarda, agregar un índice puede ser la solución.

-- Añadir índice en la columna meta_key/price en una tabla personalizada:
ALTER TABLE wp_postmeta ADD INDEX idx_postmeta_meta_key (meta_key(191))
-- O si tienes una tabla propia con columna price:
CREATE INDEX idx_products_price ON wp_products(price)

Tras crear el índice, reproduce la página y revisa con Query Monitor la reducción del tiempo de ejecución.

Ejemplo 3: Detección de consultas duplicadas

Query Monitor marca consultas duplicadas. Dos fuentes comunes:

  • Llamadas a la misma función varias veces en la misma carga (p. ej. dentro de un loop template).
  • Falta de cacheo para resultados solicitados repetidamente por distintos componentes.

Solución: consolidar la llamada en una función y cachear el resultado con transients u object cache:

function obtener_datos_cara_costosa() {
  cache = wp_cache_get(datos_costosos, mi_grupo)
  if (cache !== false) {
    return cache
  }
  // Lógica que ejecuta consulta cara
  result = // ...
  wp_cache_set(datos_costosos, result, mi_grupo, 3600)
  return result
}

Interpretando la pila de llamadas (stack trace)

Cuando haces clic en el “Caller” de una consulta, Query Monitor muestra la pila de llamadas. Busca:

  • Archivo y línea en tema/plugin donde se ejecutó la consulta.
  • Si la llamada proviene de una función propia o de una función de terceros.
  • Si la pila incluye bucles (loops) inesperados que provocan repetición de consultas.

Optimizar según el tipo de consulta

Problema Solución típica
Muchas consultas cortas repetidas Batching / cache / reducir llamadas a get_option/get_posts
Consultas con joins a wp_postmeta Evitar meta_query intensivo, usar tablas personalizadas o índices
Consultas que devuelven muchas filas Paginación, limitar columnas (fields => ids o fields => post_title,ID), usar LIMIT
Consultas lentas en plugins de terceros Actualizar plugin, contactar autor o reemplazar por alternativa cachear resultado

Técnicas avanzadas

  • EXPLAIN: copia la consulta SQL que muestra Query Monitor y ejecútala con EXPLAIN para ver el plan de ejecución en MySQL. Esto ayuda a saber si un índice está siendo usado.
  • Profiling: usa herramientas externas (percona toolkit, slow query log de MySQL) para analizar a nivel de servidor si el tráfico es muy alto.
  • Cache de objeto: activa un object cache persistente (Redis, Memcached) para acelerar queries repetidas y transients.
  • Guardar resultados en tablas propias: para volúmenes grandes, normaliza datos o crea tablas con esquemas optimizados para lecturas frecuentes.

Ejemplo: uso de EXPLAIN

EXPLAIN SELECT p.ID, p.post_title
FROM wp_posts p
INNER JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = precio AND pm.meta_value BETWEEN 100 AND 500

Analiza la columna key (índice usado) y rows estimadas para decidir si añadir índice o reestructurar la consulta.

Buenas prácticas generales

  • Evitar consultas en loops: no ejecutar una consulta por cada item si puedes obtener todos los datos en una sola llamada.
  • Usar las APIs de WordPress correctamente: wp_query, get_posts, get_terms con argumentos adecuados para limitar datos.
  • Cachear respuestas complejas: transients o caché de objeto para resultados que no cambian a cada petición.
  • Revisar plugins de terceros: si provienen de plugins externos, consulta si hay versiones optimizadas o alternativas.
  • Monitorizar en producción: Query Monitor en ambientes con tráfico puede generar overhead puedes usarlo en staging o activar solo para usuarios administradores y durante ventanas de mantenimiento.

Checklist rápido para arreglar una consulta lenta detectada por Query Monitor

  1. Reproducir la petición y confirmar la consulta problemática en Query Monitor.
  2. Inspeccionar caller y pila de llamadas para localizar el código PHP que la ejecuta.
  3. Verificar si es duplicada si lo es, consolidar o cachear.
  4. Comprobar si la consulta devuelve muchas filas o columnas innecesarias (usar LIMIT/fields).
  5. Ejecutar EXPLAIN en la consulta para ver uso de índices.
  6. Si falta índice: añadir índices adecuados y medir mejora.
  7. Si la lógica no puede optimizarse fácilmente: aplicar cache (transients / object cache) con expiraciones controladas.
  8. Volver a probar con Query Monitor y comparar tiempos antes/después.

Notas finales (operativas)

Query Monitor es una herramienta de diagnóstico en tiempo real. Úsala para identificar y reproducir problemas, no como mecanismo de logging continuo en producción con alto tráfico, ya que añade overhead. Para entornos de alto tráfico, combina la inspección puntual con registros del servidor (slow query log) y soluciones de cache en capas (CDN, object cache, page cache).

Ejemplo final: patrón para cachear una consulta costosa con control de expiración y busting

function obtener_productos_filtrados( args ) {
  cache_key = prod_filter_ . md5( serialize( args ) )
  cached = get_transient( cache_key )
  if ( false !== cached ) {
    return cached
  }

  query = new WP_Query( args )
  result = query->posts
  // Guardar 30 minutos y bustear cuando sea necesario desde hooks de guardado/post change
  set_transient( cache_key, result, 30  MINUTE_IN_SECONDS )
  return result
}

// Ejemplo de busting al guardar producto
add_action( save_post_producto, function( post_id ) {
  // Aquí puedes invalidar transients relevantes para simplicidad, limpiar todos con prefijo conocido
  global wpdb
  like = wpdb->esc_like( prod_filter_ ) . %
  wpdb->query( wpdb->prepare( DELETE FROM wpdb->options WHERE option_name LIKE %s, _transient_ . like ) )
}, 10, 1 )

Con este enfoque, tras aplicar cambios verás en Query Monitor que la consulta cara solo se ejecuta cuando corresponde, y la mayoría de cargas toman datos desde caché.

Conclusión

Query Monitor no solo localiza consultas lentas sino que te proporciona la información necesaria (tiempos, stack traces, duplicados) para tomar decisiones técnicas: optimizar la consulta, añadir índices, reestructurar datos o aplicar cache. Sigue el flujo de reproducir, identificar, analizar y corregir, y valida cada cambio re-ejecutando la petición con Query Monitor para medir la mejora.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *