get_option() vs get_theme_mod(): ¿Por qué uno es más lento?
He estado usando get_theme_mod()
durante algún tiempo en varios de mis proyectos. Decidí aprovechar la API de Personalización de Temas en WordPress v3.4 una vez que estuvo disponible, ya que sentí que era una herramienta indispensable para mis clientes.
Después de un tiempo, comencé a notar que mis sitios se sentían un poco más lentos de lo habitual, y el Personalizador en particular tardaba bastante tiempo en cargar. A través de muchas pruebas de ensayo y error durante mi investigación, decidí intentar cambiar el type
al registrar mis ajustes (es decir, $wp_customize->add_setting()
) de theme_mod
a option
.
Una vez que hice esto y cambié todas mis llamadas de get_theme_mod()
a get_option()
, noté un aumento muy significativo en la velocidad usando la última configuración en comparación con la anterior, tanto en el frontend como especialmente en el Personalizador en el backend. He estado revisando el núcleo de WordPress en un esfuerzo por intentar encontrar una respuesta de por qué sucede esto, pero no logro discernir cuál es el problema particular en este escenario.
Cualquier información que la comunidad pueda tener con respecto a por qué get_option()
tiene un rendimiento significativamente más rápido que get_theme_mod()
sería muy apreciada.
La respuesta es que sí, las funciones theme_mod serán más lentas, pero no significativamente, y los beneficios superan las diferencias.
Los mods del tema se almacenan como opciones. Entonces, en esencia, las funciones theme_mod son envoltorios alrededor de las funciones de opciones.
Primero, entiende que los ajustes de theme_mod se almacenan como un array en una sola opción, vinculada al nombre específico del tema. Entonces, si hago esto:
set_theme_mod('aaa',123);
set_theme_mod('bbb',456);
Lo que realmente obtengo en la base de datos es una sola fila de opciones con el nombre de theme_mods_nombredeltema que contiene un array serializado con ('aaa'=>123, 'bbb'=>456) dentro.
Ahora, get_theme_mod
será más lento porque en realidad está haciendo dos llamadas a get_option
. Primero, obtiene el nombre del tema. Luego, obtiene la opción theme_mods_nombredeltema
. Así que ahí mismo hay una pérdida de velocidad del 50%. El resto del trabajo realizado se basa principalmente en filtros, en que hay una llamada de filtro adicional, pero a menos que tengas algo en ese filtro, esto es bastante insignificante.
Ten en cuenta que el sistema de opciones almacena los datos recuperados en la caché de objetos, por lo que no está haciendo múltiples llamadas a la base de datos aquí. Solo el primer uso resulta en un impacto en la base de datos.
La función set_theme_mod
será algo más lenta porque hace esas mismas dos llamadas get_option, luego hace otra llamada get_option
para obtener el nombre del tema nuevamente, y luego hace update_option
con el conjunto completo de opciones ahora modificadas. Esto causa una actualización en la base de datos, y el hecho de que esté enviando muchos más datos puede ser la causa de una ralentización notable. Actualizar unos pocos bytes es más rápido que actualizar una fila más grande. Pero no tanto como para que lo notes, por lo general. A menos que tengas un montón de ajustes...
Las funciones de theme_mod probablemente necesiten una optimización general, ciertamente, pero aún así deberías usarlas en lugar de get_option y similares debido a los temas hijos.
El problema con usar filas de opciones directamente es que las estás usando directamente y utilizando nombres de claves específicos para tus ajustes.
Si tengo un tema llamado "AAA" y hago un tema hijo de él llamado "BBB" para usar en otro sitio, entonces mi tema "AAA" podría usar una opción llamada "ejemplo". Cuando actualizo un sitio, y actualiza mi opción, entonces la misma opción ahora se aplicará a mi tema hijo. ¿Qué pasa si no quería que hiciera eso? ¿Qué pasa si quería que el tema hijo usara un conjunto diferente de ajustes de opciones?
Los theme_mods, al incluir el nombre real del tema (y no un valor codificado) como parte de la clave, aseguran que cada "tema" en el sitio use su propio conjunto de ajustes. Puedo cambiar entre ellos y los ajustes no se transfieren, se mantienen como los configuré. Más simple, más obvio, más intuitivo.
Y si algún cambio futuro del núcleo o un plugin modifica cómo funcionan los theme_mods, entonces obtendrás automáticamente los beneficios de eso sin ningún cambio. Los envoltorios siempre serán más lentos, eso es inevitable, es la naturaleza de los envoltorios. Sin embargo, todavía estás escribiendo código PHP, no lenguaje de máquina. Usamos envoltorios como este para simplificar las cosas y separar la funcionalidad. Los temas no deberían necesitar saber, ni preocuparse, cómo se almacenan sus opciones en la base de datos, o cómo funciona el nombrado. Las funciones theme_mod proporcionan una solución más simple que es más limpia.

get_theme_mod
es simplemente un envoltorio alrededor de get_option
. En teoría, al ser otra capa de abstracción, funcionará más lento pero en la práctica la diferencia no debería ser lo suficientemente grande como para ser percibida por un humano.
Las diferencias reales de velocidad pueden ser causadas si tienes algún código lento enganchado a los hooks de theme_mod.

No es un envoltorio. get_theme_mod almacena el valor en un array serializado junto con todas las opciones del tema en una fila de la tabla wp_options, con la clave predeterminada "theme_mods_<nombre del tema>". get_option almacena el valor como el campo option_value de una fila en la tabla wp_options, con una clave que tú especificas. Dicho de otra manera, get_theme_mod agrupa el valor en un array serializado con todas las demás modificaciones del tema; get_option no lo hace.

¿Podría estar ocurriendo algo en el Personalizador entonces? Estoy viendo lo mismo que el OP aquí.
Puedo confirmar que con alrededor de 30 opciones, mi tiempo de carga del Personalizador bajó de aproximadamente 3s a alrededor de 0.5s al cambiar a get_option
en lugar de get_theme_mod
.
Llamando a los métodos directamente veo una diferencia de 2ms.
(https://gist.github.com/anonymous/d98a46d00d52d40e7dec)
Puede que no sea perceptible cuando comparas las APIs directamente, pero debe haber algo con cómo se utilizan en el Personalizador.

Puedes PROBAR EL TIEMPO de get_option
(100 iteraciones) usando este código (colócalo en functions.php
o en algún lugar):
add_action('wp','My_Test');
function My_Test(){
var_dump(microtime(true));
for ($i=1; $i<100; $i++) { get_option('blogdescription'); }
var_dump(microtime(true));
for ($i=1; $i<100; $i++) { get_theme_mod('blogdescription'); }
var_dump(microtime(true));
exit;
}
Otras Reflexiones
No sé si marcará una diferencia (quizás los desarrolladores de WordPress lo saben mejor), pero pensé que si un sitio web tiene ALTO tráfico, y en cada carga de página necesita obtener cientos de opciones, entonces ¿qué pasaría si combino muchas opciones en un solo get_option
? Algo así:
update_option('my_extra_optss', array(
'myNAME' => 'George',
'myAGE' => 43 ));
Luego:
$x = get_option('my_extra_optss');
$x['myNAME'];
$x['myAGE'];
................
¿Hará esto que el sitio sea un poco más rápido?

TL;DR: Si eres un desarrollador de temas, deberías usar get_theme_mod
Respuesta completa:
Si tienes 100 llamadas a get_option, se realizan 100 consultas a tu base de datos.
Si tienes 100 llamadas a get_theme_mod, solo se realiza 1 consulta a tu base de datos.
¿Por qué? Porque todas las modificaciones del tema se almacenan en una sola fila de la base de datos y se llamarán solo una vez, mientras que cada opción es una fila y 100 llamadas a get_option resultarán en 100 consultas a la base de datos y, por supuesto, ralentizarán tu sitio.
Si tu tema tiene muchas opciones, usar get_theme_mod reducirá significativamente el número de consultas a la base de datos.
Puedes verificar el rendimiento y el número de consultas con el Plugin Query Monitor

Esto es ligeramente incorrecto. La función get_theme_mod utiliza get_theme_mods - https://developer.wordpress.org/reference/functions/get_theme_mods/ - que en realidad ejecuta 2 funciones get_option. Así que 100 llamadas a get_theme_mod en realidad hacen 2 solicitudes a la base de datos ;)
