Hola Trinopoty,
Ejecuté pruebas en una configuración similar de MariaDB y ahora puedo explicar exactamente lo que está sucediendo. La estructura de consulta es idéntica
WordPress NO cambió la consulta entre versiones. Tanto 6.6.1 como 6.9.4 generan exactamente el mismo SQL:
SELECT wp_posts.ID
FROM wp_posts
LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE wp_term_relationships.term_taxonomy_id IN (43,89,114,...)
AND wp_posts.post_type="post"
AND wp_posts.post_status="publish"
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10Lo que realmente cambió
La diferencia es El optimizador de consultas de MySQL elige un orden de unión diferente en base a sus estadísticas de datos. En mis pruebas, reproduje ambos planes en la misma base de datos:
Plan A (Rápido – 0.01s) – Lo que tenías en 6.6.1
-> wp_posts (type_status_date index) → 38 rows
-> wp_term_relationships (PRIMARY key) → 10 rowsPor qué es rápido: Comienza con alta selectividad. wp_posts filtro (post_type=’post’, post_status=’publish’), ya ordenado por post_date del índice.
Plan B (Lento – 1.20s) – Lo que tienes ahora
-> wp_term_relationships (term_taxonomy_id index) → 196,836 rows
-> wp_posts (PRIMARY key lookup per row) → filter down to 10
-> Filesort required (using temporary table)Por qué es lento: Comienza con el término relaciones, busca ~200.000 filas y luego filtra. La clasificación de archivos en ese gran resultado intermedio acaba con el rendimiento. ¿Por qué sucedió esto?
Lo más probable es que uno de estos haya cambiado entre sus versiones:
- Tus datos crecieron – Se agregaron más publicaciones, lo que hizo que el optimizador pensara que el filtro term_taxonomy_id es más selectivo que el filtro post_type/post_status
- Estadísticas actualizadas – Correr
ANALYZE TABLEo el análisis automático activado después de la actualización de WordPress - Las estimaciones de cardinalidad del índice cambiaron – El modelo de costos del optimizador ahora estima incorrectamente que a partir de
term_relationshipses mas barato
Verifiqué esto usando FORCE INDEX sugerencias para hacer que MariaDB use el plan «malo»: la misma consulta pasó de 0,3 ms a 1,3 ms incluso en mi pequeña base de datos de prueba (105 publicaciones, 23 relaciones).
Soluciones (en orden de efectividad)
1. Almacenamiento en caché de objetos (Recomendado – solución inmediata)
Instale un complemento de caché de objetos. Hay muchos para elegir que usan Memcache o Redis. Me gusta el caché de expedientes, que solo usa archivos php y es el más fácil de instalar y configurar.
2. Habilitar split_the_query (WordPress integrado)
add_filter( 'split_the_query', '__return_true', 20 );Esto obliga a WordPress a ejecutar dos consultas más simples en lugar de un JOIN complejo, lo que a menudo evita problemas con el optimizador.
3. Consejos para el optimizador de MySQL (podría funcionar)
add_filter( 'posts_clauses', function( $clauses ) {
if ( strpos( $clauses['join'], 'term_relationships' ) !== false ) {
// Force join order: posts first
$clauses['join'] = 'STRAIGHT_JOIN ' . $clauses['join'];
}
return $clauses;
}, 10, 2 );Solución a largo plazo
Este es un problema central conocido de WordPress rastreado en Trazado #54346 (https://core.trac.wordpress.org/ticket/54346). La solución propuesta convierte LEFT JOIN en una subconsulta, lo que elimina la ambigüedad del orden de unión:
SELECT wp_posts.ID FROM wp_posts
WHERE wp_posts.ID IN (
SELECT object_id FROM wp_term_relationships
WHERE term_taxonomy_id IN (43,89,114,...)
)
AND wp_posts.post_type="post"
AND wp_posts.post_status="publish"
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10Recomiendo:
- Inmediato: Implementar el almacenamiento en caché de objetos (Solución 1)
- Corto plazo: Comente la Trac #54346 con su resultado EXPLAIN para ayudar a priorizar la solución.
- A largo plazo: Supervise ese ticket para la combinación de reescritura de subconsulta