Optimizar el código y las consultas a base de datos es algo fundamental para cualquier desarrollo web, sobretodo si estás desarrollando un proyecto para sitios con alto tráfico.
Un proyecto o desarrollo web donde se hagan una serie de consultas y/o peticiones optimizadas, consumirá menos recursos de servidor.
Por el contrario, un código desestructurado, poco organizado y con consultas poco optimizadas y con muchos usuarios concurrentes hará que la navegación sea lenta y pesada, incluso pudiendo echar abajo la aplicación.
Optimiza las consultas a base de datos
Aunque tenemos otras opciones, lo normal cuando vamos a hacer una consulta a la base de datos es utilizar WP_Query.
WP_Query es una clase del core de WordPress que nos permite hacer consultas a nuestros post, pages y post-types. Por defecto, internamente ejecuta 5 consultas incluyendo por ejemplo el cálculo de paginación y la comprobación/actualización de la caché de los metadatos.
Siempre que hagas una WP_Query ten en cuenta los siguientes parámetros:
- ‘no_found_rows’ => true, se utiliza para paginar los datos devueltos por una WP_Query. Si en tu proyecto vas a hacer una consulta y no la quieres paginar simplemente establece este parámetro a true y no se hará ese cálculo, ahorrándote una query a la base de datos.
- ‘update_post_term_cache’ => false, si no quieres actualizar la información que WordPress pueda tener en caché respecto a las taxonomías de los posts que estás consultado, evitando una consulta más.
- ‘update_post_meta_cache’ => false, lo mismo que antes pero relativo a los meta datos de un post (custom fields).
- ‘posts_per_page’ => -1, intenta evitar este argumento, ya que te devolvería todos los resultados y si tienes muchos contenidos puede ser un problema.
También podemos utilizar get_posts() para traernos información de nuestros posts, si nuestra consulta no es compleja y no implica filtrar por custom fields o terms.
Cachea las consultas a base de datos
En algunos casos puede resultar interesante cachear un dato o una consulta, para tenerla disponible durante el resto de la ejecución de la página y no tener que repetirla
Para ésto, WordPress nos facilita el Object Caching, a través de las siguientes funciones:
- wp_cache_add( $key, $data, $group, $expire );
- wp_cache_set( $key, $data, $group, $expire );
- wp_cache_get( $key, $group );
- wp_cache_delete( $key, $group );
- wp_cache_flush();
donde:
- $key, es la clave que almacenaremos en base de datos
- $data, es el valor que almacenaremos asociado a la $key
- $group (opcional), permite agrupar datos
- $expire, para establecer el tiempo que ese valor persiste en caché.
Mejor veamos un ejemplo:
<?php
$result = wp_cache_get( 'my_result' );
if ( false === $result ) {
$args = array(
'post_type' => 'post',
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'quotes' ),
),
array(
'relation' => 'AND',
array(
'taxonomy' => 'post_format',
'field' => 'slug',
'terms' => array( 'post-format-quote' ),
),
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'wisdom' ),
),
),
),
);
$query = new WP_Query( $args );
wp_cache_set( 'my_result', $query );
}
// Hacemos algo con $result;
En este ejemplo lo primero que hacemos es almacenar en la variable $result lo que tengamos en caché (si es que tenemos algo) de la clave my_result. Si obtenemos un false (porque es la primera vez que accedemos y no hemos cacheado nada aún) lo que hacemos es realizar la query y almacenar el resultado en $query. Y a continuación, seteamos la caché utilizando la función wp_cache_set. Si no existe la clave my_result la crea, y si existe la reescribe.
Tendremos este cacheo durante el resto de carga de la página, pero este tipo de caché no es persistente.
Cacheo persistente
Aunque suene a paradoja, cachear una consulta de manera persistente es almacenar en base de datos la respuesta de una consulta, para más adelante a través de otra consulta obtener los datos. Es decir, de hacer una consulta no te libras, pero si la consulta original es compleja y consume mucho tiempo, podemos almacenar el resultado en base de datos, y obtenerlo más adelante con una consulta simple a ese dato.
Me explico, imagina que tienes una consulta compleja con WP_Query, donde filtras por varios custom fields, varias categorías, y la cantidad de posts en base de datos es elevada. Podríamos enrevesarlo más aún, pero probablemente esta consulta sea lenta.
WordPress pone a nuestra disposición una serie de funciones para almacenar el resultado de la consulta lenta en base de datos (en un option), y después acceder a él simplemente obteniendo el valor de ese option. Ésta consulta es mucho más rápida, y podemos decidir por cuanto tiempo almacenamos ese resultado en caché, lo que nos da una maniobrabilidad tremenda.
Si queremos que el cacheo de una consulta persista en el tiempo, podemos utilizar los transients en lugar de las funciones mencionadas anteriormente. El mismo ejemplo de antes quedaría de este modo:
<?php
$result = get_transient( 'my_result' );
if ( false === $result ) {
$args = array(
'post_type' => 'post',
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'quotes' ),
),
array(
'relation' => 'AND',
array(
'taxonomy' => 'post_format',
'field' => 'slug',
'terms' => array( 'post-format-quote' ),
),
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'wisdom' ),
),
),
),
);
$query = new WP_Query( $args );
set_transient( 'my_result', $query, HOUR_IN_SECONDS );
}
// Hacemos algo con $result;
En este ejemplo, a través de transients almacenamos en la clave my_result el valor de $query, e indicamos un tiempo de expiración de 1 hora. ¿Esto que quiere decir? Que durante la próxima hora cada vez que se ejecute este código, podremos recuperar el valor de $query a través de get_transient(), no entraremos en el if, y evitaremos hacer la WP_Query.
Cachea las peticiones remotas
Podemos usar también la caché no sólo para consultas a base de datos. Si en tu web haces una petición a un servicio externo para obtener un dato y tienes mucho tráfico, quizá te interese cachear esa petición, aunque sea por un breve periodo de tiempo.
Ejemplo: Si en tu web consultas a facebook las veces que tu página ha sido compartida y tienes 600 visitas a la hora, quizá te interese cachear esa petición y establecer una caducidad de 5 minutos. Esto significa que las 50 visitas que recibes durante esos 5 minutos verán el dato cacheado, y en esas 50 visitas sólo harás la petición al servicio externo una vez. Una vez transcurridos esos 5 minutos, en la siguiente visita se volverá a hacer la petición y se actualizará el dato en base de datos:
<?php
$fb_count = get_transient('facebook_count');
if ( false === $fb_count ) {
$return = wp_remote_get( 'http://graph.facebook.com/?id=http://www.midominio.com', array( 'timeout' => 10 ) );
$json = json_decode($return["body"], true);
$fb_count = isset($json["shares"]) ? intval($json["shares"]) : 0;
set_transient('facebook_count', $fb_count, 5 * MINUTE_IN_SECONDS);
}
Utilizando la caché y optimizando las consultas conseguimos mejorar el rendimiento de la página. Realmente estamos hablando de milisegundos, es casi imperceptible. Cuando tienes muchas visitas, si esos milisegundos se multiplican por miles de visitas, es entonces cuando podemos notar la diferencia.
Optimiza el código
Es fácil decirlo pero no tan fácil hacerlo. Quizá un buen comienzo sea leer y repasar las buenas prácticas para desarrollo recogidas en el Codex de WordPress. También diría que la optimización del código no sólo es pensar en el performance, si no también en la robustez, escalabilidad, seguridad…
- Trata de programar orientado a objetos (POO), te permitirá estructurar bien el código.
- Personalmente me gusta mucho el patrón Singleton.
- Instancia las clases sólo allí donde vayan a utilizarse, por ejemplo si el código se ejecuta en el admin, o en la parte pública, o sólo en determinado contenido.
- Sanitiza y valida los datos que pueda introducir un usuario a través de formularios.
- Utiliza PHP 7, es capaz de procesar más del doble de peticiones por segundo que PHP 5.6
- Muy fan de los operadores ternarios.
- Si en un snippet llamas varias veces a una función que retorna un valor, mejor almacénalo en una variable y evitamos llamadas redundantes.
- KISS (Keep It Simple, Stupid), muchas veces nos liamos y nos pasamos de rosca. Menos es más.
- DRY (Don’t Repeat Yourself), intenta no repetir código. Si en dos clases haces cosas parecidas, quizá puedas crear una y en función de algún parámetro controlar cada necesidad.
- Piensa en el mañana, las prisas pasan y las mierdas quedan.
¡Happy coding!