{"id":20930,"date":"2026-08-23T15:05:18","date_gmt":"2026-08-23T13:05:18","guid":{"rendered":"https:\/\/webhosting.de\/mariadb-query-optimizer-intern-erklaert-sql-tuning-insight\/"},"modified":"2026-08-23T15:05:18","modified_gmt":"2026-08-23T13:05:18","slug":"explicacion-interna-del-optimizador-de-consultas-de-mariadb-perspectivas-sobre-el-ajuste-de-sql","status":"publish","type":"post","link":"https:\/\/webhosting.de\/es\/mariadb-query-optimizer-intern-erklaert-sql-tuning-insight\/","title":{"rendered":"Explicaci\u00f3n interna del optimizador de consultas de MariaDB: conceptos b\u00e1sicos, planes y aplicaci\u00f3n pr\u00e1ctica"},"content":{"rendered":"<p>Voy a explicar el <strong>Optimizador de MariaDB<\/strong> Ejemplos pr\u00e1cticos: c\u00f3mo elabora planes, c\u00f3mo estima los costes y por qu\u00e9 a veces se equivoca. As\u00ed podr\u00e1s interpretar el plan de ejecuci\u00f3n de SQL de forma espec\u00edfica, utilizar los \u00edndices de manera adecuada y guiar al optimizador bas\u00e1ndote en datos, en lugar de en corazonadas.<\/p>\n\n<h2>Puntos centrales<\/h2>\n\n<p>Para empezar, voy a resumir brevemente los conceptos fundamentales, para que puedas contextualizar mejor los apartados siguientes y el <strong>Visi\u00f3n general<\/strong> conservas.<\/p>\n<ul>\n  <li><strong>Fases<\/strong>: El an\u00e1lisis, la preparaci\u00f3n, la optimizaci\u00f3n y la ejecuci\u00f3n conforman el ciclo de vida de cada consulta.<\/li>\n  <li><strong>Modelo de costes<\/strong>: Los valores basados en el tiempo, expresados en microsegundos, controlan la selecci\u00f3n de \u00edndices, los escaneos y el orden de las uniones.<\/li>\n  <li><strong>Estad\u00edsticas<\/strong>: La cardinalidad y los histogramas determinan la estimaci\u00f3n de la selectividad.<\/li>\n  <li><strong>Transparencia<\/strong>: EXPLAIN, EXPLAIN ANALYZE y Optimizer Trace permiten acceder al funcionamiento interno del sistema.<\/li>\n  <li><strong>Sintonizaci\u00f3n<\/strong>: Los \u00edndices, las reescrituras de consultas, ANALYZE TABLE y los par\u00e1metros de coste aumentan la velocidad.<\/li>\n<\/ul>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariaDB-query-plans-9842.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Ciclo de vida de una consulta en MariaDB<\/h2>\n\n<p>Antes de que se elabore un plan, una consulta pasa por cuatro etapas que compruebo de forma espec\u00edfica en mi d\u00eda a d\u00eda para <strong>Causas<\/strong> que provoquen lentitud. Durante el an\u00e1lisis sint\u00e1ctico, MariaDB convierte el SQL en una estructura interna; aqu\u00ed es donde se detectan los errores sint\u00e1cticos. En la fase de preparaci\u00f3n, el motor comprueba las tablas, las columnas y los posibles \u00edndices, y lleva a cabo transformaciones sencillas. A continuaci\u00f3n tiene lugar la optimizaci\u00f3n, en la que se calculan los planes candidatos y se eval\u00faan mediante un modelo de costes. Durante la ejecuci\u00f3n, el servidor aplica el plan seleccionado paso a paso: leer, unir, filtrar, devolver.<\/p>\n\n<p>Distingo claramente los errores de an\u00e1lisis por fase, porque as\u00ed los diagn\u00f3sticos surten efecto m\u00e1s r\u00e1pidamente y <strong>Medidas<\/strong> actuar de forma espec\u00edfica. En la mayor\u00eda de los casos, los problemas de rendimiento tienen su origen en la optimizaci\u00f3n: estimaciones err\u00f3neas, falta de \u00edndices o secuencias de uniones desfavorables. Los errores de an\u00e1lisis sint\u00e1ctico son triviales, pero la fase de preparaci\u00f3n ya puede incluir t\u00e9cnicas como la resoluci\u00f3n de vistas o la transformaci\u00f3n de subconsultas. En la ejecuci\u00f3n, las ineficiencias se hacen evidentes sin piedad si previamente se ha seleccionado un escaneo completo. Por eso, comienzo cada an\u00e1lisis con una revisi\u00f3n estructurada de las cuatro etapas.<\/p>\n\n<h2>C\u00f3mo toma decisiones internamente el optimizador<\/h2>\n\n<p>MariaDB funciona bas\u00e1ndose en costes y eval\u00faa las ejecuciones alternativas mediante una <strong>Funci\u00f3n de coste<\/strong>. Para cada variante, el servidor estima el n\u00famero de filas le\u00eddas, la selectividad de WHERE\/ON, los tipos de acceso (como Table Scan, Index Scan y Range Scan), as\u00ed como el tiempo que requieren las operaciones individuales. Internamente, el servidor distingue entre \u00abjoin_preparation\u00bb y \u00abjoin_optimization\u00bb. En \u00abjoin_preparation\u00bb se llevan a cabo reescrituras de consultas, simplificaciones de condiciones, transformaciones de subconsultas y resoluciones de vistas. En \u00abjoin_optimization\u00bb se calculan los \u00f3rdenes de uni\u00f3n, se comprueban los \u00edndices candidatos mediante \u00abref_optimizer_key_uses\u00bb, se estiman las filas mediante \u00abRange Scan\u00bb y se asignan las condiciones a tablas concretas lo antes posible.<\/p>\n\n<p>Este mecanismo explica por qu\u00e9 un filtro peque\u00f1o colocado en el lugar equivocado puede provocar costosas <strong>Consecuencias<\/strong> tiene. Si la operaci\u00f3n \u00abattaching_conditions_to_tables\u00bb se realiza tarde, el plan arrastra un n\u00famero innecesario de filas a trav\u00e9s de las uniones. Si las estad\u00edsticas est\u00e1n desactualizadas, \u00abrows_estimation\u00bb y \u00abSelectivity\u00bb dan resultados err\u00f3neos; en ese caso, el optimizador recurre a rutas de acceso que parecen favorables, pero que en realidad son lentas. Es precisamente en estos aspectos donde intervengo: mejores estad\u00edsticas, predicados m\u00e1s claros e \u00edndices compuestos ordenados correctamente. Tras ello, la elecci\u00f3n del plan suele cambiar de forma notable.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/MariaDBQueryOptKonferenz1234.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Modelo de costes a partir de MariaDB 11.0<\/h2>\n\n<p>Las versiones actuales ya no eval\u00faan el trabajo de forma aproximada mediante pesos, sino con <strong>microsegundos<\/strong> para operaciones concretas de almacenamiento. Par\u00e1metros como `optimizer_disk_read_cost`, `optimizer_disk_read_ratio` y `optimizer_where_cost` acercan el modelo a los tiempos de ejecuci\u00f3n reales. De este modo, el optimizador compara el escaneo de rango de \u00edndice con el escaneo completo bas\u00e1ndose en suposiciones de tiempo reales. LAST_QUERY_COST muestra el coste total estimado y, a menudo, se ajusta mucho mejor a la realidad que antes. En los sistemas con gran volumen de datos, esta mayor precisi\u00f3n da sus frutos de inmediato.<\/p>\n\n<p>Calibro el modelo con cuidado cuando las caracter\u00edsticas del hardware contradicen los supuestos predeterminados y, por lo tanto, la <strong>Selecci\u00f3n de planes<\/strong> distorsionar. Las unidades SSD NVMe, los sistemas de almacenamiento distribuido o las cach\u00e9s especiales pueden alterar notablemente la relaci\u00f3n de disco y los tiempos de lectura. Peque\u00f1os ajustes en los valores de \u00aboptimizer_costs\u00bb hacen que MariaDB d\u00e9 prioridad a las rutas m\u00e1s adecuadas. Documento cada cambio y, a continuaci\u00f3n, compruebo EXPLAIN ANALYZE para medir el impacto. Sin mediciones, el ajuste es una loter\u00eda.<\/p>\n\n<h2>Selectividad, estad\u00edsticas e histogramas<\/h2>\n\n<p>Las buenas estimaciones comienzan con datos limpios <strong>cardinalidad<\/strong> y una selectividad fiable. MariaDB mantiene estad\u00edsticas sobre los distintos valores de cada columna y, opcionalmente, puede utilizar histogramas para las distribuciones. Precisamente los datos irregulares \u2014puntos calientes, distribuciones de Zipf, patrones estacionales\u2014 se benefician de los histogramas. Tras cambios importantes en los datos, ejecuto ANALYZE TABLE para que la optimizaci\u00f3n vuelva a basarse en datos reales. Quien se olvide de hacerlo, se arriesga a que se realicen escaneos completos que, objetivamente, son err\u00f3neos.<\/p>\n\n<p>Voy a programar ANALYZE como una tarea peri\u00f3dica, adaptada a <strong>Cambios<\/strong> en el volumen de datos y en tablas cr\u00edticas. Cuando la distribuci\u00f3n de las columnas presenta una fuerte asimetr\u00eda, los histogramas ayudan a evaluar de forma realista la selectividad de los valores singulares. Esto reduce los errores de estimaci\u00f3n en los escaneos de rango y las estrategias de fusi\u00f3n. En combinaci\u00f3n con \u00edndices compuestos adecuados, la precisi\u00f3n de los resultados mejora dr\u00e1sticamente. Resultado: tiempos de ejecuci\u00f3n m\u00e1s cortos y menos E\/S.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadb-query-optimizer-guide-4729.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>EXPLAIN y c\u00f3mo leer los planes de ejecuci\u00f3n<\/h2>\n\n<p>Para hacer visibles las decisiones, utilizo EXPLAIN, EXPLAIN EXTENDED y <strong>FORMATO=JSON<\/strong>. Las columnas cl\u00e1sicas ofrecen una visi\u00f3n general r\u00e1pida: id, select_type, table, type, possible_keys, key, key_len, ref, rows y, en su caso, filtered. Un \u00abtype=ALL\u00bb indica un escaneo completo, algo que rara vez es deseable. \u00abFORMAT=JSON\u00bb muestra en detalle c\u00f3mo se han reubicado las condiciones y qu\u00e9 rutas ha evaluado el optimizador. En el contexto del alojamiento web, recomiendo la gu\u00eda sobre <a href=\"https:\/\/webhosting.de\/es\/planes-de-ejecucion-de-consultas-de-bases-de-datos-que-alojan-informacion-sobre-el-rendimiento-de-la-optimizacion\/\">Planes de ejecuci\u00f3n en el alojamiento web<\/a>, para vincular la informaci\u00f3n del plan con los efectos sobre las infraestructuras.<\/p>\n\n<p>Para interpretarlo r\u00e1pidamente, me resulta \u00fatil una peque\u00f1a tabla que resume brevemente los valores t\u00edpicos y, de este modo, <strong>Interpretaciones err\u00f3neas<\/strong> se evita.<\/p>\n\n<table>\n  <thead>\n    <tr>\n      <th>Campo EXPLAIN<\/th>\n      <th>Valor t\u00edpico<\/th>\n      <th>Importancia en la pr\u00e1ctica<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td>tipo<\/td>\n      <td>ALL, range, ref, eq_ref, const<\/td>\n      <td>Cuanto m\u00e1s a la derecha, m\u00e1s selectivo; \u00abALL\u00bb indica un escaneo completo.<\/td>\n    <\/tr>\n    <tr>\n      <td>possible_keys<\/td>\n      <td>Lista de \u00edndices<\/td>\n      <td>\u00cdndices que, en teor\u00eda, encajan; si aqu\u00ed faltan candidatos, falta estructura.<\/td>\n    <\/tr>\n    <tr>\n      <td>clave<\/td>\n      <td>Nombre del \u00edndice<\/td>\n      <td>\u00cdndice realmente utilizado; si est\u00e1 en blanco, significa que no se utiliza ning\u00fan \u00edndice.<\/td>\n    <\/tr>\n    <tr>\n      <td>filas<\/td>\n      <td>N\u00famero<\/td>\n      <td>N\u00famero estimado de l\u00edneas le\u00eddas; si difiere mucho de la realidad, significa que las estad\u00edsticas son poco fiables.<\/td>\n    <\/tr>\n    <tr>\n      <td>filtrado<\/td>\n      <td>Porcentaje<\/td>\n      <td>La cantidad que se pasa al filtro; suele ser mejor que sea poca.<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>Por qu\u00e9 el optimizador a veces se equivoca<\/h2>\n\n<p>Ning\u00fan modelo de costes se adapta a todas las situaciones, por lo que lo corrijo <strong>Errores<\/strong> De forma espec\u00edfica. Las estad\u00edsticas obsoletas dan lugar a estimaciones err\u00f3neas de las filas y a secuencias de uniones desfavorables. Los \u00edndices compuestos mal estructurados impiden el uso de \u00edndices en filtros de varias columnas. Las subconsultas muy anidadas dificultan las reescrituras efectivas y bloquean la materializaci\u00f3n. Los filtros ausentes o enga\u00f1osos obligan al motor a mover muchas filas antes de que se apliquen los predicados \u00fatiles.<\/p>\n\n<p>En primer lugar, compruebo si la formulaci\u00f3n de la consulta cumple con el <strong>\u00cdndice<\/strong> Lo que realmente funciona: regla de prefijos a la izquierda, orden de clasificaci\u00f3n adecuado, evitar funciones sobre columnas en la cl\u00e1usula WHERE. A continuaci\u00f3n, compruebo en EXPLAIN ANALYZE si la realidad respalda la estimaci\u00f3n. Si no es as\u00ed, ejecuto ANALYZE TABLE y, si es necesario, una reescritura. Solo como \u00faltimo recurso recurro a FORCE INDEX o a las indicaciones (hinting), ya que esto puede limitar las optimizaciones futuras.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadboptimizer_2219.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Utilizar el Optimizer Trace de forma selectiva<\/h2>\n\n<p>Si EXPLAIN no es suficiente, activo el rastreo del optimizador y hago un seguimiento de <strong>Decisiones<\/strong> en el registro JSON. En \u00e9l puedo ver qu\u00e9 planes se han barajado, descartado o aceptado. Entiendo por qu\u00e9 una condici\u00f3n se aplica tarde o por qu\u00e9 un \u00edndice no ha sido seleccionado. El registro tambi\u00e9n muestra c\u00f3mo se han reorganizado las condiciones. Esta perspectiva aclara la comprensi\u00f3n y ofrece puntos de actuaci\u00f3n concretos para el pr\u00f3ximo ajuste.<\/p>\n\n<p>Guardo los fragmentos relevantes del rastreo junto con el hash de la consulta y <strong>Par\u00e1metros<\/strong>valorar. As\u00ed podr\u00e9 comparar m\u00e1s adelante qu\u00e9 cambio ha tenido qu\u00e9 efecto. La documentaci\u00f3n del servidor MariaDB y diversas ponencias del ecosistema describen estos campos en detalle (fuente: documentaci\u00f3n del servidor MariaDB sobre el optimizador de consultas y el rastreo del optimizador). Con esta herramienta, detecto los supuestos err\u00f3neos m\u00e1s r\u00e1pido que con el m\u00e9todo de prueba y error. Ahorro tiempo sobre todo en uniones complejas.<\/p>\n\n<h2>Pr\u00e1ctica: Optimizaci\u00f3n de bases de datos paso a paso<\/h2>\n\n<p>Empiezo cada optimizaci\u00f3n con una clara <strong>Medici\u00f3n<\/strong>. Identifico los problemas mediante la supervisi\u00f3n y el <a href=\"https:\/\/webhosting.de\/es\/mysql-slow-query-log-hosting-analizar-queryperf\/\">Registro de consultas lentas<\/a>. A continuaci\u00f3n, comparo EXPLAIN con EXPLAIN ANALYZE para comparar el plan y el resultado real. Adapto la estrategia de indexaci\u00f3n a WHERE, JOIN y ORDER BY; los \u00edndices compuestos los oriento hacia los puntos de acceso m\u00e1s frecuentes. Solo utilizo FORCE INDEX cuando el optimizador, a pesar de contar con estad\u00edsticas correctas, elige el candidato equivocado.<\/p>\n\n<p>Cada paso conlleva el cuidado de la <strong>Estad\u00edsticas<\/strong>: ANALYZE TABLE en tablas con mucho tr\u00e1fico; histogramas para distribuciones asim\u00e9tricas. Simplifico las subconsultas innecesarias, materializo resultados intermedios cuando es necesario y elimino soluciones provisionales antiguas. En el caso de hardware especial, compruebo los optimizer_costs para garantizar que el modelo de microsegundos sea correcto. Documento cada cambio con valores \u00abantes\u00bb y \u00abdespu\u00e9s\u00bb, para que el efecto sea comprensible a largo plazo.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadb_query_optimizer_8390.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Problemas t\u00edpicos de los optimizadores y sus soluciones<\/h2>\n\n<p>Si EXPLAIN muestra type=ALL, aunque possible_keys est\u00e9 lleno, lo primero que miro es <strong>Selectividad<\/strong>. A menudo, el orden de las columnas en el \u00edndice compuesto no es el adecuado o alguna funci\u00f3n impide el uso del \u00edndice. En esos casos, invierto el orden, elimino las funciones que causan problemas o divido los predicados. Si el orden de las uniones es incorrecto, compruebo si es posible aplicar un filtrado previo, por ejemplo, dando prioridad a la tabla m\u00e1s selectiva. Cuando resulta conveniente, convierto las subconsultas en uniones o en tablas TEMPORARY.<\/p>\n\n<p>Tambi\u00e9n reconozco las decisiones err\u00f3neas por las que se desv\u00edan mucho <strong>filas<\/strong> entre lo previsto y la realidad. En ese caso, resulta \u00fatil utilizar la instrucci\u00f3n ANALYZE TABLE o generar un histograma de la columna en cuesti\u00f3n. Si ni siquiera unas estad\u00edsticas correctas permiten alcanzar el objetivo, me planteo utilizar hints expl\u00edcitos. Antes de nada, guardo una copia de seguridad de la comprobaci\u00f3n cruzada y los valores medidos, para que las versiones posteriores del optimizador no se vean frenadas por los datos almacenados. La disciplina a la hora de documentar da sus frutos en este caso.<\/p>\n\n<h2>Contexto del alojamiento web y aspectos operativos<\/h2>\n\n<p>La calidad de las consultas y la infraestructura deben ir de la mano; de lo contrario, la aplicaci\u00f3n no rinde al m\u00e1ximo. <strong>Posible<\/strong>. Los SSD r\u00e1pidos, las cach\u00e9s consistentes y una configuraci\u00f3n adecuada son la base sobre la que el optimizador toma buenas decisiones. Un tr\u00e1fico elevado no admite escaneos completos; unas pocas consultas deficientes pueden ralentizar sistemas enteros. Para entornos MySQL\/MariaDB en producci\u00f3n, ofrecemos consejos pr\u00e1cticos como <a href=\"https:\/\/webhosting.de\/es\/mysql-optimizer-query-hosting-optimizacion-serverboost\/\">Optimizador de MySQL<\/a> Reflexiones \u00fatiles sobre la combinaci\u00f3n entre plan y plataforma. Quien tenga en cuenta este aspecto evita los cuellos de botella antes de que se agraven.<\/p>\n\n<p>Siempre relaciono el an\u00e1lisis de planes con m\u00e9tricas sobre <strong>E\/S<\/strong>, latencia y concurrencia. Si los valores no se ajustan al modelo de costes previsto, compruebo los par\u00e1metros. A continuaci\u00f3n, analizo los tama\u00f1os de los b\u00faferes, las cargas de trabajo paralelas y la distribuci\u00f3n de los conjuntos m\u00e1s solicitados. Con este enfoque, se consigue gestionar de forma armoniosa las consultas y los recursos, y mantener bajo control los picos de actividad.<\/p>\n\n<h2>Las rutas de uni\u00f3n y acceso en la pr\u00e1ctica<\/h2>\n\n<p>Aclaro muchos malentendidos explicando que la <strong>Tipos de acceso<\/strong> los sopese de forma espec\u00edfica unos contra otros. Un <em>rango<\/em>- o <em>ref<\/em>-El acceso funciona casi siempre <em>TODO<\/em>. En el caso de las relaciones de igualdad sobre claves \u00fanicas (<em>eq_ref<\/em>) los planos son especialmente resistentes. Adem\u00e1s, compruebo si un <strong>\u00cdndice de cobertura<\/strong> que la consulta se procese \u00edntegramente: si todas las columnas necesarias est\u00e1n incluidas en el \u00edndice, MariaDB se ahorra costosos accesos a la tabla. <strong>Index Condition Pushdown (ICP)<\/strong> Ayuda a comprobar condiciones WHERE adicionales ya en el \u00edndice, lo que reduce el n\u00famero de filas devueltas y las operaciones de E\/S.<\/p>\n\n<p>Acerca de <strong>Fusi\u00f3n de \u00edndices<\/strong> MariaDB puede combinar varios \u00edndices (intersecci\u00f3n\/uni\u00f3n). Esto resulta \u00fatil con predicados \u00abOR\u00bb o con varias condiciones de selecci\u00f3n, pero suele ser m\u00e1s lento que un \u00edndice compuesto bien elegido. Adem\u00e1s, estoy evaluando <strong>MRR<\/strong> (Lectura multirango) y <strong>BKA<\/strong> (Batched Key Access). MRR ordena las claves primarias que se van a leer para suavizar las E\/S aleatorias; BKA agrupa las b\u00fasquedas de uniones y ofrece ventajas sobre todo en uniones sin superposici\u00f3n. En la pr\u00e1ctica, pruebo BKA\/MRR mediante optimizer_switch y compruebo con EXPLAIN ANALYZE si los patrones de E\/S disminuyen. Sin embargo, si MariaDB recurre a <strong>Bloque de bucles anidados<\/strong> (BNL), suele ser m\u00e1s recomendable aumentar el tama\u00f1o del b\u00fafer de uniones (join_buffer_size) o realizar una reescritura que permita uniones de \u00edndices reales.<\/p>\n\n<pre><code>-- Ejemplo: \u00edndice compuesto para uni\u00f3n + filtro + ordenaci\u00f3n\nCREATE INDEX ix_orders_cust_status_created\n  ON orders (customer_id, status, created_at);\n\n-- Acceso t\u00edpico\nSELECT *\nFROM orders o\nJOIN customers c ON c.id = o.customer_id\nWHERE o.status = 'open' AND o.created_at &gt;= '2026-01-01'\nORDER BY o.created_at DESC\nLIMIT 50;\n<\/code><\/pre>\n\n<p>Con el \u00edndice anterior, el optimizador puede elegir el orden m\u00e1s selectivo, evaluar los filtros desde el principio y, a menudo, realizar la ordenaci\u00f3n sin necesidad de una ordenaci\u00f3n de archivos adicional.<\/p>\n\n<h2>ORDER BY, GROUP BY, ordenaci\u00f3n por archivos y tablas temporales<\/h2>\n\n<p>Ordenar y agrupar lleva tiempo. Me encargo de que <strong>ORDER BY<\/strong> y <strong>GRUPO POR<\/strong> pueden ejecutarse seg\u00fan el orden del \u00edndice. Esto funciona si el prefijo y la direcci\u00f3n coinciden exactamente. De lo contrario, se aplica una <strong>Clasificaci\u00f3n de archivos<\/strong> con b\u00fafer de ordenaci\u00f3n (sort_buffer_size) y, si es necesario, una tabla temporal. Si el conjunto de resultados contiene columnas TEXT\/BLOB anchas, MariaDB es m\u00e1s r\u00e1pida. <em>en disco<\/em> Tablas TEMP (Aria). Lo evito seleccionando solo las columnas necesarias, cargando los campos grandes al final o utilizando prefijos de longitud limitada.<\/p>\n\n<p>En las agregaciones, siempre que sea posible, utilizo, <strong>Escaneo de \u00edndice libre<\/strong> (por ejemplo, GROUP BY en la parte principal del \u00edndice) y elijo \u00edndices compuestos a lo largo de la agrupaci\u00f3n. Cuando los resultados intermedios son muy grandes, una materializaci\u00f3n con claves adecuadas se adapta mejor que una \u00fanica mega-uni\u00f3n. Mido regularmente las m\u00e9tricas de los controladores y los contadores Created_tmp_* para detectar puntos cr\u00edticos de ordenaci\u00f3n y de tablas temporales.<\/p>\n\n<h2>Subconsultas, semi-join y materializaci\u00f3n<\/h2>\n\n<p>Muchas subconsultas pueden reformularse de manera eficiente durante la preparaci\u00f3n. Las construcciones IN\/EXISTS pueden expresarse como <strong>Semi-join<\/strong> funcionan con estrategias como la materializaci\u00f3n o LooseScan. Compruebo si el optimizador es un <strong>derived_merge<\/strong> pudo llevar a cabo: si se incluye una tabla derivada (o una CTE con \u00abWITH\u00bb) en el plan externo, sus \u00edndices est\u00e1n disponibles directamente. Si esto no es posible, la subconsulta acaba en una tabla temporal; en ese caso, si es factible, le asigno una clave (por ejemplo, mediante SELECT DISTINCT\/ORDER BY en columnas clave), para que las uniones no acaben en el limbo.<\/p>\n\n<pre><code>-- Ejemplo: EXISTS en lugar de IN y tabla derivada compatible con Merge\nSELECT o.id\nFROM orders o\nWHERE EXISTS (\n  SELECT 1 FROM payments p\n  WHERE p.order_id = o.id AND p.state = 'captured'\n);\n\n-- Tabla derivada con claves \u00fanicas\nWITH paid_orders AS (\n  SELECT DISTINCT order_id\n  FROM payments\n  WHERE state = 'captured'\n)\nSELECT o.*\nFROM orders o\nJOIN paid_orders po ON po.order_id = o.id;\n<\/code><\/pre>\n\n<p>Compruebo con EXPLAIN FORMAT=JSON si <strong>materializado<\/strong> o <strong>subconsulta dependiente<\/strong> se haya seleccionado y si hay condiciones (<strong>condici\u00f3n de pushdown<\/strong>) actuar con la suficiente antelaci\u00f3n.<\/p>\n\n<h2>Partici\u00f3n y poda<\/h2>\n\n<p>La partici\u00f3n no sustituye a los \u00edndices, pero puede <strong>Volumen de datos por acceso<\/strong> reducir dr\u00e1sticamente. El optimizador solo realiza una poda correcta si el predicado cumple la <strong>Clave de partici\u00f3n<\/strong> se identifique de forma inequ\u00edvoca y no quede enmascarado por funciones. Por eso evito expresiones como DATE(created_at) en la cl\u00e1usula WHERE de tablas particionadas y, en su lugar, trabajo con l\u00edmites de rango. EXPLAIN muestra qu\u00e9 particiones se leen; los rangos muy amplios indican una mala poda.<\/p>\n\n<p>Un n\u00famero excesivo de particiones peque\u00f1as aumenta la sobrecarga de planificaci\u00f3n. Por eso, elijo una granularidad adecuada (por ejemplo, mensual en lugar de diaria), mantengo actualizadas las estad\u00edsticas de cada partici\u00f3n (ANALYZE PARTITION) y compruebo si los \u00edndices importantes est\u00e1n disponibles localmente en las particiones. En los proyectos de migraci\u00f3n, tengo en cuenta el impacto en la replicaci\u00f3n y las copias de seguridad, ya que ambos factores influyen en el grado de segmentaci\u00f3n que aplico.<\/p>\n\n<h2>Sargabilidad y patrones de reescritura<\/h2>\n\n<p>La forma m\u00e1s sencilla sigue siendo <strong>Capacidad de embalaje en ata\u00fad<\/strong> \u2013 Condiciones que permiten utilizar \u00edndices. Evito las funciones en las columnas del WHERE, devuelvo constantes en el lado de las columnas y, si es necesario, desgloso las condiciones OR en <strong>UNI\u00d3N TODOS<\/strong>. Para b\u00fasquedas con el operador LIKE sin ancla inicial (\"%foo\"), un \u00edndice BTREE no sirve de nada; en este caso, tengo pensado utilizar la b\u00fasqueda de texto completo o un servicio de b\u00fasqueda adecuado. Para los c\u00e1lculos, utilizo <strong>columnas generadas indexadas<\/strong>, para que el optimizador pueda identificar la l\u00f3gica del \u00edndice.<\/p>\n\n<pre><code>-- Antipatr\u00f3n: funci\u00f3n sobre una columna\nWHERE DATE(created_at) = '2026-08-01'\n-- Mejor: rango sobre el valor sin procesar\nWHERE created_at &gt;= '2026-08-01' AND created_at &lt; &#039;2026-08-02&#039;\n\n-- Antipatr\u00f3n: el operador OR impide el uso del \u00edndice\nWHERE status = &#039;open&#039; OR customer_id = 42\n-- Mejor: dos b\u00fasquedas con UNION ALL y cada una con su propio \u00edndice\n(SELECT ... WHERE status = &#039;open&#039;)\nUNION ALL\n(SELECT ... WHERE customer_id = 42&#039;);\n<\/code><\/pre>\n\n<p>En cuanto a los \u00edndices compuestos, considero que la <strong>regla del prefijo de la izquierda<\/strong> Apl\u00edcalo estrictamente: ordena las columnas seg\u00fan su selectividad y seg\u00fan el orden de clasificaci\u00f3n que se vaya a necesitar posteriormente. Si necesito un ORDER BY descendente, lo tengo en cuenta en la estructura del \u00edndice; as\u00ed me ahorro la clasificaci\u00f3n de archivos.<\/p>\n\n<h2>Opciones del optimizador y ajuste preciso de los costes<\/h2>\n\n<p>Antes de empezar a trabajar en las consultas, compruebo <strong>optimizer_switch<\/strong> y memoria intermedia. Funciones como <em>mrr<\/em>, <em>batched_key_access<\/em>, <em>index_merge<\/em>, <em>semijoin<\/em>, <em>derived_merge<\/em> o <em>condition_pushdown_for_derived<\/em> se pueden ajustar por sesi\u00f3n. Activo candidatos de forma selectiva para una sesi\u00f3n de prueba, mido con EXPLAIN ANALYZE y revierto los cambios si no se observa ning\u00fan efecto. La ruta de uni\u00f3n se beneficia de una cantidad suficiente de <strong>join_buffer_size<\/strong>; grandes variedades de <strong>sort_buffer_size<\/strong>. Al mismo tiempo, vigilo los b\u00faferes en relaci\u00f3n con la concurrencia, para que el servidor no tenga que recurrir al intercambio de memoria bajo una carga paralela.<\/p>\n\n<p>En lo que respecta a los costes, ajusto, si es necesario, los ya mencionados <strong>costes_del_optimizador<\/strong> en microsegundos. Mi gu\u00eda: pasos peque\u00f1os y reversibles con puntos de medici\u00f3n documentados. Yo utilizo <strong>LAST_QUERY_COST<\/strong> para comprobar la plausibilidad y repetir las mediciones con valores de par\u00e1metros realistas, ya que los planes pueden depender en gran medida de literales concretos.<\/p>\n\n<h2>Estabilidad de los planes, regresiones y flujo de trabajo en equipo<\/h2>\n\n<p>Incluso un buen plan puede verse afectado por el aumento del volumen de datos o los cambios de versi\u00f3n <strong>volcar<\/strong>. Por eso me aseguro de disponer de informaci\u00f3n sobre los planes de ejecuci\u00f3n: hash de consultas, EXPLAIN-JSON, fragmentos de trazas del optimizador y tiempos de ejecuci\u00f3n de EXPLAIN ANALYZE. Los cambios en los \u00edndices y las reescrituras los realizo mediante pull requests con pruebas de antes y despu\u00e9s. En entornos de CI\/CD, compruebo autom\u00e1ticamente las consultas cr\u00edticas con conjuntos de datos representativos. As\u00ed es como empiezo <strong>Planes de regresi\u00f3n<\/strong> temprano.<\/p>\n\n<p>Para los casos delicados, creo que <strong>Consejos<\/strong> (FORCE INDEX, STRAIGHT_JOIN, optimizer_switch por consulta) est\u00e1n disponibles como \u00faltima opci\u00f3n, pero util\u00edzalas con moderaci\u00f3n y con una fecha de caducidad. Es mejor solucionar las causas: estad\u00edsticas, \u00edndices, formulaci\u00f3n. En los equipos, una gu\u00eda sencilla sobre la optimizaci\u00f3n, el dise\u00f1o de \u00edndices y la disciplina de medici\u00f3n garantiza que las nuevas funcionalidades no introduzcan problemas de rendimiento sin que nos demos cuenta.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadb-query-optimizer-7832.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Resumen: Del plan al resultado<\/h2>\n\n<p>\u00bfQui\u00e9n puede utilizar el <strong>Plan<\/strong> Comprenderlo permite controlar el rendimiento. Las fases de an\u00e1lisis sint\u00e1ctico, preparaci\u00f3n, optimizaci\u00f3n y ejecuci\u00f3n explican d\u00f3nde se pierde tiempo. El modelo de costes basado en el tiempo a partir de la versi\u00f3n 11.0, junto con estad\u00edsticas y histogramas actualizados, hacen que las estimaciones sean fiables. EXPLAIN, EXPLAIN ANALYZE y el rastreo del optimizador aportan transparencia, que traduzco en medidas concretas. Con una estrategia de \u00edndices bien definida, un dise\u00f1o claro de las consultas y una infraestructura adecuada, las consultas de MariaDB ofrecen respuestas r\u00e1pidas de forma constante.<\/p>","protected":false},"excerpt":{"rendered":"<p>Descubre c\u00f3mo funciona internamente el optimizador de consultas de MariaDB, c\u00f3mo analizar el plan de ejecuci\u00f3n SQL con EXPLAIN y c\u00f3mo llevar a cabo un ajuste pr\u00e1ctico de la base de datos, incluyendo consejos para aplicaciones web de alto rendimiento.<\/p>","protected":false},"author":1,"featured_media":20923,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[781],"tags":[],"class_list":["post-20930","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-datenbanken-administration-anleitungen"],"acf":[],"_wp_attached_file":null,"_wp_attachment_metadata":null,"litespeed-optimize-size":null,"litespeed-optimize-set":null,"_elementor_source_image_hash":null,"_wp_attachment_image_alt":null,"stockpack_author_name":null,"stockpack_author_url":null,"stockpack_provider":null,"stockpack_image_url":null,"stockpack_license":null,"stockpack_license_url":null,"stockpack_modification":null,"color":null,"original_id":null,"original_url":null,"original_link":null,"unsplash_location":null,"unsplash_sponsor":null,"unsplash_exif":null,"unsplash_attachment_metadata":null,"_elementor_is_screenshot":null,"surfer_file_name":null,"surfer_file_original_url":null,"envato_tk_source_kit":null,"envato_tk_source_index":null,"envato_tk_manifest":null,"envato_tk_folder_name":null,"envato_tk_builder":null,"envato_elements_download_event":null,"_menu_item_type":null,"_menu_item_menu_item_parent":null,"_menu_item_object_id":null,"_menu_item_object":null,"_menu_item_target":null,"_menu_item_classes":null,"_menu_item_xfn":null,"_menu_item_url":null,"_trp_menu_languages":null,"rank_math_primary_category":null,"rank_math_title":null,"inline_featured_image":null,"_yoast_wpseo_primary_category":null,"rank_math_schema_blogposting":null,"rank_math_schema_videoobject":null,"_oembed_049c719bc4a9f89deaead66a7da9fddc":null,"_oembed_time_049c719bc4a9f89deaead66a7da9fddc":null,"_yoast_wpseo_focuskw":null,"_yoast_wpseo_linkdex":null,"_oembed_27e3473bf8bec795fbeb3a9d38489348":null,"_oembed_c3b0f6959478faf92a1f343d8f96b19e":null,"_trp_translated_slug_en_us":null,"_wp_desired_post_slug":null,"_yoast_wpseo_title":null,"tldname":null,"tldpreis":null,"tldrubrik":null,"tldpolicylink":null,"tldsize":null,"tldregistrierungsdauer":null,"tldtransfer":null,"tldwhoisprivacy":null,"tldregistrarchange":null,"tldregistrantchange":null,"tldwhoisupdate":null,"tldnameserverupdate":null,"tlddeletesofort":null,"tlddeleteexpire":null,"tldumlaute":null,"tldrestore":null,"tldsubcategory":null,"tldbildname":null,"tldbildurl":null,"tldclean":null,"tldcategory":null,"tldpolicy":null,"tldbesonderheiten":null,"tld_bedeutung":null,"_oembed_d167040d816d8f94c072940c8009f5f8":null,"_oembed_b0a0fa59ef14f8870da2c63f2027d064":null,"_oembed_4792fa4dfb2a8f09ab950a73b7f313ba":null,"_oembed_33ceb1fe54a8ab775d9410abf699878d":null,"_oembed_fd7014d14d919b45ec004937c0db9335":null,"_oembed_21a029d076783ec3e8042698c351bd7e":null,"_oembed_be5ea8a0c7b18e658f08cc571a909452":null,"_oembed_a9ca7a298b19f9b48ec5914e010294d2":null,"_oembed_f8db6b27d08a2bb1f920e7647808899a":null,"_oembed_168ebde5096e77d8a89326519af9e022":null,"_oembed_cdb76f1b345b42743edfe25481b6f98f":null,"_oembed_87b0613611ae54e86e8864265404b0a1":null,"_oembed_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_oembed_time_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_tldname":null,"_tldclean":null,"_tldpreis":null,"_tldcategory":null,"_tldsubcategory":null,"_tldpolicy":null,"_tldpolicylink":null,"_tldsize":null,"_tldregistrierungsdauer":null,"_tldtransfer":null,"_tldwhoisprivacy":null,"_tldregistrarchange":null,"_tldregistrantchange":null,"_tldwhoisupdate":null,"_tldnameserverupdate":null,"_tlddeletesofort":null,"_tlddeleteexpire":null,"_tldumlaute":null,"_tldrestore":null,"_tldbildname":null,"_tldbildurl":null,"_tld_bedeutung":null,"_tldbesonderheiten":null,"_oembed_ad96e4112edb9f8ffa35731d4098bc6b":null,"_oembed_8357e2b8a2575c74ed5978f262a10126":null,"_oembed_3d5fea5103dd0d22ec5d6a33eff7f863":null,"_eael_widget_elements":null,"_oembed_0d8a206f09633e3d62b95a15a4dd0487":null,"_oembed_time_0d8a206f09633e3d62b95a15a4dd0487":null,"_aioseo_description":null,"_eb_attr":null,"_eb_data_table":null,"_oembed_819a879e7da16dd629cfd15a97334c8a":null,"_oembed_time_819a879e7da16dd629cfd15a97334c8a":null,"_acf_changed":null,"_wpcode_auto_insert":null,"_edit_last":null,"_edit_lock":null,"_oembed_e7b913c6c84084ed9702cb4feb012ddd":null,"_oembed_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_time_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_03514b67990db061d7c4672de26dc514":null,"_oembed_time_03514b67990db061d7c4672de26dc514":null,"rank_math_news_sitemap_robots":null,"rank_math_robots":null,"_eael_post_view_count":"132","_trp_automatically_translated_slug_ru_ru":null,"_trp_automatically_translated_slug_et":null,"_trp_automatically_translated_slug_lv":null,"_trp_automatically_translated_slug_fr_fr":null,"_trp_automatically_translated_slug_en_us":null,"_wp_old_slug":null,"_trp_automatically_translated_slug_da_dk":null,"_trp_automatically_translated_slug_pl_pl":null,"_trp_automatically_translated_slug_es_es":null,"_trp_automatically_translated_slug_hu_hu":null,"_trp_automatically_translated_slug_fi":null,"_trp_automatically_translated_slug_ja":null,"_trp_automatically_translated_slug_lt_lt":null,"_elementor_edit_mode":null,"_elementor_template_type":null,"_elementor_version":null,"_elementor_pro_version":null,"_wp_page_template":null,"_elementor_page_settings":null,"_elementor_data":null,"_elementor_css":null,"_elementor_conditions":null,"_happyaddons_elements_cache":null,"_oembed_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_time_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_time_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_59808117857ddf57e478a31d79f76e4d":null,"_oembed_time_59808117857ddf57e478a31d79f76e4d":null,"_oembed_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_time_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_81002f7ee3604f645db4ebcfd1912acf":null,"_oembed_time_81002f7ee3604f645db4ebcfd1912acf":null,"_elementor_screenshot":null,"_oembed_7ea3429961cf98fa85da9747683af827":null,"_oembed_time_7ea3429961cf98fa85da9747683af827":null,"_elementor_controls_usage":null,"_elementor_page_assets":[],"_elementor_screenshot_failed":null,"theplus_transient_widgets":null,"_eael_custom_js":null,"_wp_old_date":null,"_trp_automatically_translated_slug_it_it":null,"_trp_automatically_translated_slug_pt_pt":null,"_trp_automatically_translated_slug_zh_cn":null,"_trp_automatically_translated_slug_nl_nl":null,"_trp_automatically_translated_slug_pt_br":null,"_trp_automatically_translated_slug_sv_se":null,"rank_math_analytic_object_id":null,"rank_math_internal_links_processed":"1","_trp_automatically_translated_slug_ro_ro":null,"_trp_automatically_translated_slug_sk_sk":null,"_trp_automatically_translated_slug_bg_bg":null,"_trp_automatically_translated_slug_sl_si":null,"litespeed_vpi_list":null,"litespeed_vpi_list_mobile":null,"rank_math_seo_score":null,"rank_math_contentai_score":null,"ilj_limitincominglinks":null,"ilj_maxincominglinks":null,"ilj_limitoutgoinglinks":null,"ilj_maxoutgoinglinks":null,"ilj_limitlinksperparagraph":null,"ilj_linksperparagraph":null,"ilj_blacklistdefinition":null,"ilj_linkdefinition":null,"_eb_reusable_block_ids":null,"rank_math_focus_keyword":"MariaDB Optimizer","rank_math_og_content_image":null,"_yoast_wpseo_metadesc":null,"_yoast_wpseo_content_score":null,"_yoast_wpseo_focuskeywords":null,"_yoast_wpseo_keywordsynonyms":null,"_yoast_wpseo_estimated-reading-time-minutes":null,"rank_math_description":null,"surfer_last_post_update":null,"surfer_last_post_update_direction":null,"surfer_keywords":null,"surfer_location":null,"surfer_draft_id":null,"surfer_permalink_hash":null,"surfer_scrape_ready":null,"_thumbnail_id":"20923","footnotes":null,"_links":{"self":[{"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/posts\/20930","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/comments?post=20930"}],"version-history":[{"count":0,"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/posts\/20930\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/media\/20923"}],"wp:attachment":[{"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/media?parent=20930"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/categories?post=20930"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webhosting.de\/es\/wp-json\/wp\/v2\/tags?post=20930"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}