<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>TecnoRetales &#187; Caché</title>
	<atom:link href="http://www.tecnoretales.com/tag/cache/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.tecnoretales.com</link>
	<description>La experiencia no se olvida</description>
	<lastBuildDate>Wed, 07 Jul 2010 21:23:33 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<!-- google ad injected by adsense-optimizer http://www.adsenseoptimizer.de -->
			<div  style="padding:7px; float: right; padding-right: 0; margin: 3px;"><!-- Ad number: 1 --><script type="text/javascript"><!--
    	 
    	google_ad_client = "pub-7180773421652966"; google_alternate_color = "FFFFFF";
		google_ad_width = 468; google_ad_height = 60;
		google_ad_format = "468x60_as"; google_ad_type = "text";
		google_ad_channel =""; google_color_border = "FE8B00";
		google_color_link = "FE8B00"; google_color_bg = "FFFFFF";
		google_color_text = "000000"; google_color_url = "D9D9D9";
		google_ui_features = "rc:6"; //--></script>
		<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script></div>	<item>
		<title>Implementando un sistema de caché en PHP</title>
		<link>http://www.tecnoretales.com/programacion/implementando-un-sistema-de-cache-en-php/</link>
		<comments>http://www.tecnoretales.com/programacion/implementando-un-sistema-de-cache-en-php/#comments</comments>
		<pubDate>Mon, 21 Sep 2009 09:40:24 +0000</pubDate>
		<dc:creator>Manel Pérez Mata</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Caché]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.tecnoretales.com/?p=658</guid>
		<description><![CDATA[Hace unos días, os comentaba que es una caché y como entenderla en el marco de la programación web. Para seguir con el aprendizaje de este sistema, he desarrollado una pequeña clase que permite cachear información (strings, arrays, resultados de base de datos, objetos&#8230;) en nuestra máquina y descongestionar así el motor de base de [...]]]></description>
			<content:encoded><![CDATA[<!-- google_ad_section_start --><p>Hace unos días, os comentaba <a title="Qué es una caché?" href="http://www.tecnoretales.com/programacion/que-es-una-cache-y-como-aplicarla-a-la-programacion-web/" target="_blank">que es una caché</a> y como entenderla en el marco de la programación web. Para seguir con el aprendizaje de este sistema, he desarrollado una pequeña <strong>clase que permite cachear información</strong> (strings, arrays, resultados de base de datos, objetos&#8230;) en nuestra máquina y descongestionar así el motor de base de datos, por poner un ejemplo.</p>
<p>Lo primero que debemos hacer es <strong>pensar en que necesitamos</strong> para su correcto funcionamiento, así que, manos a la obra.</p>
<p>Como sabemos, una caché es un sistema al que le pasamos un objeto X y él lo almacenará en su sistema de una manera determinada, por lo tanto, necesitaremos una función que dado un identificador único y unos datos, guardará la información. Además de esto, necesitamos indicarle un <strong>tiempo de vida</strong> (ttl) a esa información cacheada, sino estaremos devolviendo eternamente el valor almacenado.<span id="more-658"></span></p>
<p>Un concepto que nos puede resultar interesante es el de <strong>caché de grupo</strong>. Imaginemos que tenemos un sistema que va cacheando información de nuestra base de datos; tenemos una tabla user y una tabla user_profile y cacheamos los resultados de ambas tablas por separado. Imaginemos también que por temas de LOPD el usuario solicita que eliminemos todos sus datos de nuestras máquinas, así que eliminaremos el registro de la tabla user y user_profile y procederemos a eliminar el cacheo de los datos de cada tabla por separado. Si tuviesemos agrupada la información de usuario, con una única llamada a la función remove y pasándole el identificador de grupo, eliminará la caché de ambas tablas.</p>
<p>A medida que vaya explicando cada función, os pondré el resultado final para que os hagais una idea de como va quedando el código:</p>
<pre class="brush: php;">
    public function save ( $id, $group_id = 'default', $data, $ttl = self::CACHE_TTL )
    {
        if ( $this-&gt;caching )
        {
            $this-&gt;set_file_routes( $id, $group_id );

            // If the directory don't exists, I create.
            if ( !is_dir ( $this-&gt;file_path ) )
            {
                if ( !mkdir ( $this-&gt;file_path, 0777, true ) )
                {
                    return false;
                }
            }

            if ( !is_array ( $data ) &amp;amp;&amp;amp; !is_object( $data ) )
            {
                $data = array ( $data );
            }

            // Serialize data for caching.
            $data = serialize ( $data );

            // I get a hash to check integrity in the future.
            $hash = md5 ( $data );

            $meta['expiration_time']    = time () + $ttl;
            $meta['integrity']          = $hash;
            $meta['data']               = $data;

            // Serialize meta info to put in a file.
            $data    = serialize ( $meta );

            return file_put_contents ( $this-&gt;file, $data, LOCK_EX );
        }

        return false;
    }</pre>
<p>A su vez, debemos poder recuperar los objetos almacenados en un futuro, así que también programaremos una función que dado un identificador y un identificador de grupo, devolverá unos datos <strong>get($id, $group_id)</strong>. El tener que pasar el identificador de grupo en esta función es debido a que las rutas de almacenamiento se generan a partir de ambos identificadores.</p>
<pre class="brush: php;">
    public function get ( $id, $group_id = 'default' )
    {
        if ( $this-&gt;caching )
        {
            $this-&gt;set_file_routes ( $id, $group_id );

            if ( !file_exists ( $this-&gt;file ) )
            {
                return false;
            }

            $meta    = file_get_contents ( $this-&gt;file_path . $this-&gt;file_name );
            $meta    = unserialize ( $meta );

            $check_expiration   = $this-&gt;check_expiration ( $meta['expiration_time'] );
            $check_integrity    = $this-&gt;check_integrity ( $meta['integrity'], $meta['data'] );

            // Expiration and integrity control.
            if ( $check_expiration &amp;amp;&amp;amp; $check_integrity )
            {
                $data = unserialize ( $meta['data'] );

                return $data;
            }
            else
            // Clean the expired or not correct caché.
            {
                $this-&gt;remove ( $id, $group_id );

                return false;
            }
        }

        return false;
    }
</pre>
<p>Como último elemento fundamental, necesitaremos poder borrar unos datos de la caché en caso de que ya no sean válidos porque se han modificado o borrado, así que crearemos una función que dado un identificador y un identificador de grupo, elimine el objeto cacheado. A esta función la llamaremos <strong>remove($id, $group_id)</strong>.</p>
<pre class="brush: php;">
    public function remove( $id, $group_id = 'default', $group_level = false )
    {
        $this-&gt;set_file_routes( $id, $group_id );

        // Don't remove the 'default' group
        if ( $group_level &amp;amp;&amp;amp; $group_id != 'default' )
        {
            if ( !$this-&gt;group_path || empty ( $this-&gt;group_path ) )
            {
                return false;
            }

            return $this-&gt;del_tree ( $this-&gt;group_path );
        }
        // Check that the file exists and delete the file cached folder
        elseif ( $this-&gt;file_path &amp;amp;&amp;amp; !empty ( $this-&gt;file_path ) )
        {
            return $this-&gt;del_tree ( $this-&gt;file_path );
        }
        else
        {
            return false;
        }
    }
</pre>
<p>Como sabemos, la caché guarda los objetos durante un tiempo determinado y una vez ha pasado ese tiempo, el objeto se considera inservible, por lo tanto añadiremos una función <strong>check_expiration()</strong> que nos dirá si el objeto está caducado.</p>
<pre class="brush: php;">
private function check_expiration ( $expiration_time )
{
return ( time() &lt; $expiration_time );
}
</pre>
<p>¿Qué pasa si cuando estoy almacenando los datos hay un problema de escritura y los datos se almacenan mal? Podría suceder que los <strong>datos almacenados esten corruptos</strong>, y todas las devoluciones de dicho objeto fueran defectuosas. Para comprobar que los datos guardados no están corruptos, crearemos una función <strong>check_integrity()</strong>.</p>
<pre class="brush: php;">
    private function check_integrity ( $read_hash, $serialized_data )
    {
        $hash = md5 ( $serialized_data );

        return ( $read_hash == $hash );
    }
</pre>
<p>Además de estas funciones, crearemos varias constantes que definirán los valores de configuración del sistema como pueden ser el tiempo de vida (ttl) de un objeto cacheado por defecto o el directorio en el que se guardará la caché.</p>
<pre class="brush: php;">
    // Path to cache dir.
    const CACHE_DIR = 'cache/';

    // Max. time to life of this cache.
    const CACHE_TTL = '3600';

    // Extension to use in cache files.
    const CACHE_EXT = '.cache';
</pre>
<p>En el código se llama a las funciones <strong>get_status()</strong> y <strong>set_status()</strong> que sirven comprobar si está activo el sistema de cacheo y habilitar/deshabilitar respectivamente. <strong>set_file_routes()</strong> crea las rutas en las que se almacenará los datos cacheados y <strong>get_key()</strong> crea hashes a partir de un identificador. Por último, <strong>del_tree()</strong> nos ayudará a eliminar un directorio de manera recursiva.</p>
<pre class="brush: php;">
    public function get_status ()
    {
        return $this-&gt;caching;
    }

    public function set_status ( $status )
    {
        $this-&gt;caching = $status;
    }

    private function set_file_routes ( $id, $group_id )
    {
        $key_name    = $this-&gt;get_key ( $id );
        $key_group   = $this-&gt;get_key ( $group_id );

        $level_one = $key_group;
        $level_two = substr ( $key_name, 0, 4 );

        $this-&gt;group_path   = self::CACHE_DIR . $level_one . '/';
        $this-&gt;file_path    = self::CACHE_DIR . $level_one . '/' . $level_two . '/';
        $this-&gt;file_name    = $key_name . self::CACHE_EXT;
        $this-&gt;file         = $this-&gt;file_path . $this-&gt;file_name;
    }

    private function get_key ( $id )
    {
        return md5 ( $id );
    }

    private function del_tree ($dir)
    {
        if ( empty ( $dir ) )
        {
            return false;
        }

        if ( !file_exists ( $dir ) )
        {
            return true;
        }

        if ( !is_dir ( $dir ) || is_link ( $dir ) )
        {
            return unlink ( $dir );
        }

        foreach ( scandir ( $dir ) as $item )
        {
            if ( $item == '.' || $item == '..' )
            {
                continue;
            }

            if( is_dir ( $dir . $item ) )
            {
                $this-&gt;del_tree ( $dir . $item . '/' );
            }
            else
            {
                unlink ( $dir . $item );
            }
        }

        return rmdir ( $dir );
    }
</pre>
<p>Puedes descargarte el archivo final desde aquí <a href="http://www.tecnoretales.com/wp-content/uploads/2009/09/cache.class.php.txt" target="_blank">cache.class.php</a>.</p>
<p>Aunque esta caché es perfectamente funcional, este ejercicio nos sirve para <strong>entender un poco más el funcionamiento de un sistema de caché</strong>. Si queremos implementar uno en nuestra aplicación, os recomiendo mirar <a title="CacheLite" href="http://pear.php.net/package/Cache_Lite" target="_blank">CacheLite</a> (si nuestra aplicación es pequeña), <a title="APC" href="http://es2.php.net/apc" target="_blank">APC</a> o <a title="Memcached" href="http://www.danga.com/memcached/" target="_blank">Memcached</a> (si nuestra aplicación es grande y/o con servidores balanceados).</p>
<!-- google_ad_section_end -->]]></content:encoded>
			<wfw:commentRss>http://www.tecnoretales.com/programacion/implementando-un-sistema-de-cache-en-php/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Qué es una Caché y como aplicarla a la programación Web</title>
		<link>http://www.tecnoretales.com/programacion/que-es-una-cache-y-como-aplicarla-a-la-programacion-web/</link>
		<comments>http://www.tecnoretales.com/programacion/que-es-una-cache-y-como-aplicarla-a-la-programacion-web/#comments</comments>
		<pubDate>Mon, 14 Sep 2009 08:15:27 +0000</pubDate>
		<dc:creator>Manel Pérez Mata</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Caché]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Optimización]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.tecnoretales.com/?p=642</guid>
		<description><![CDATA[Vamos a ver que es una caché con un ejemplo muy ilustrativo que leí hace algún tiempo. Supongamos que estamos trabajando en una centralita que facilita números de teléfono de un pueblo relativamente pequeño; para ello, tenemos un listín telefónico común con todos los teléfonos del pais.
Cada vez que recibimos una llamada, cogemos el listín, [...]]]></description>
			<content:encoded><![CDATA[<!-- google_ad_section_start --><p>Vamos a ver <strong>que es una caché</strong> con un ejemplo muy ilustrativo que leí hace algún tiempo. Supongamos que estamos trabajando en una centralita que facilita números de teléfono de un pueblo relativamente pequeño; para ello, tenemos un listín telefónico común con todos los teléfonos del pais.</p>
<p>Cada vez que recibimos una llamada, cogemos el listín, realizamos la búsqueda y facilitamos el número encontrado al usuario. Al poco tiempo, nos damos cuenta que la gente suele pedir los teléfonos de los mismos restaurantes y comercios una y otra vez, así que cogemos una libreta pequeña y vamos anotando los números de teléfono que nos solicitan ordenados alfabéticamente para poder <strong>acceder a ellos de manera más rápida</strong> la próxima vez&#8230; felicidades, acabas de crear una caché!<span id="more-642"></span></p>
<p>Sin embargo, el uso de una <strong>caché tiene un pequeño handicap</strong>; imaginemos que nos llama un usuario y nos solicita el teléfono de un comercio. Cogeremos nuestra libreta, lo buscaremos y si no lo encontramos recurriremos al listín, realizaremos la búsqueda en él y facilitaremos el número al usuario. En este momento estamos aumentando ligeramente el tiempo de búsqueda de un número, por ese motivo, la información debe almacenarse en la caché de manera eficiente, y a partir de aquí hay mil teorías sobre como y cuando almacenar la información en una caché.</p>
<p>En resumen y dicho de una manera más técnica un sistema de caché es una zona de memoria en la que se almacenan copias de un objeto original pero al que es mucho más rápido acceder.</p>
<h3><span style="text-decoration: underline;">La Caché en la programación</span>:</h3>
<p>A la hora de construir una aplicación medianamente grande, se hace imprescindible el cachear cierta información para conseguir un rendimiento óptimo. Un sistema de caché correctamente implementado puede <strong>reducir el número de servidores y evitar la sobrecarga</strong> de las máquinas.</p>
<p>Se pueden utilizar diferentes tipos de caché en función del tipo de datos que queramos guardar. Centrándome en la programación web, podemos encontrar:</p>
<ul>
<li><span style="text-decoration: underline;">Caché SQL</span>: Para almacenar en memoria el resultado de ciertas consultas recurrentes y poder así descargar la base de datos. MySQL 5.0 Server tiene soporte nativo para este tipo de caché pero se debe activar expresamente.</li>
<li><span style="text-decoration: underline;">Cachéo de Objetos</span>: En este tipo de caché podemos almacenar objetos, arrays, textos, resultados de funciones, resultados de base de datos&#8230; Podremos almacenar cualquier tipo de información soportada por el sistema de cacheo e indicarle el tiempo de expiración. Ejemplos de este sistema de caché para PHP son APC (Alternative PHP Cache) o Memcached.</li>
<li><span style="text-decoration: underline;">Cacheo de templates</span>: Puede ser de gran utilidad el cachear los resultados HTML al invocar diferentes URL, por ejemplo, el sistema de templates Smarty integra un sistema de caché que permite devolver el HTML de manera eficiente.</li>
</ul>
<h3><span style="text-decoration: underline;">Por qué no debo usar SQLCaché y APC o Memcached?</span></h3>
<p>La caché implementada por MySQL tiene una política en la gestión de su caché muy estricta. Cada vez que hay una escritura en una tabla, elimina toda la caché asociada a dicha tabla y a partir de ese momento, se debe volver a regenerar, por tanto, la entrada de un registro en la tabla implica insertar el registro, regenerar los indices, cachear los datos y por último devolverlos (aumentamos el tiempo de la consulta). Dada esta premisa, podemos extraer la conclusión de que es interesante <strong>activar SQLCaché en una base de datos con muchas lecturas y pocas escrituras</strong>.</p>
<p>A su vez, implementando un sistema de cacheo como APC o Memcached para almacenar la información devuelta por la base de datos dada una consulta, reduce considerablemente el número de lecturas a base de datos.</p>
<p>Por ese motivo, el tener activados estos dos tipos de caché simultaneamente, no solo no mejora el rendimiento de nuestra aplicación sino que lo empeora.</p>
<h3><span style="text-decoration: underline;">Es contraproducente usar APC y Memcached de manera simultanea?</span></h3>
<p>APC proporciona dos tipos de cacheo. Por un lado, evita el continuo compilado de un script PHP, pues a pesar de ser un lenguaje interpretado, PHP pasa todo script a un lenguaje entendible por la máquina antes de ejecutarlo ¿por qué recompilar cada vez el script si no ha cambiado desde la última ejecución? A partir de la versión 6 de PHP parece ser que APC estará activado de forma nativa. Por otra parte, podemos almacenar objetos con el uso de las funciones <a title="APC_add" href="http://es.php.net/manual/en/function.apc-add.php" target="_blank">apc_add</a> y <a title="APC_store" href="http://es.php.net/manual/en/function.apc-store.php" target="_blank">apc_store</a>.</p>
<p>Memcached proporciona un servicio web desacoplado y altamente escalable. <strong>El 95% del top ten y el 80% del top 50 de Alexa utiliza Memcached</strong> precisamente por la facilidad de implementación y de añadir nuevas máquinas para cachear información. APC tiene un rendimiento ligeramente superior, pero si tenemos un sistema con servidores balanceados no podemos compartir la caché entre todas las máquinas porque está vinculada al host, por ese motivo la balanza se ha ido decantando hacia el uso de Memcached.</p>
<p>Si bien, puede ser interesante <strong>utilizar una combinación de ambas</strong>, APC para el <strong>precompilado</strong> de nuestro código y <strong>Memcached para almacenar</strong> la información de base de datos y descargar así nuestro motor de bd.</p>
<!-- google_ad_section_end -->]]></content:encoded>
			<wfw:commentRss>http://www.tecnoretales.com/programacion/que-es-una-cache-y-como-aplicarla-a-la-programacion-web/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
