Blog de Pichongol
Este Blog está orientado a todos los desarrolladores PHP que intentan ir un poco más allá con el lenguaje. Se basa un poco en mis experiencias personales, investigando el código fuente, creando extensiones y tratando con temas avanzados. Optimización, buenas prácticas de programación e innovación son topics íntimamamente relacionados con este Weblog. Una cosa más, ASPeros abstenerse :-). Hack Php! Open Source forever.
lunes 10 de marzo de 2008
Hello, world!
jeje, y probando el API de Blogger... muy interesante
http://code.google.com/apis/blogger/
miércoles 10 de octubre de 2007
Dilemas sobre Query Caché
Al publicarse el artículo "Memcached: un alivio para las bases de datos" en Maestros del Web he recibido algunas opiniones, entre las cuales, una de Manuel Lagar:
"Interesante artículo. Pero estas características ya deben ser implementadas nativamente por las bases de datos, me consta que Oracle y Sqlserver si lo hacen (se que son propietarias y caras), no se si mysql lo hace…", a lo que le respondo:
"Hola Manuel, como bien apuntas, las bases de datos deberian implementar un “Query cache” (me imagino que a esto te refieres), el tema es que no siempre es conveniente usarlo.
Un cacheo de los queries inyecta un overhead en el motor de las bases de datos, que muchas veces implica lockeos de tablas, y por ende ralentiza las IO (entradas/salidas).
Cuando se manejan volumenes de tráfico del orden de los millones, no es tan bueno tener este tipo de overhead.
Fijate que los ejemplos de sitios que usan memcached son sitios que manejan trafico por millones, rankeados en algunos casos Top 50, en algunos casos Top 10 en la escala mundial de manejo de trafico."
Es un buen pie como para hablar un poco más de los Query Cachés, y para hacerlo, voy a enfocarme en la implementación de MySQL.
Como conclusión, intento fundamentar el porqué no es necesario usar Query Caché junto con Memcached Server, y porqué es contraproducente.
Funcionamiento
Arrancamos diciendo que el query caché es una característica de MySQL a partir de la version 4.0, y que representó en su momento una mejora rotunda de la velocidad de respuesta del motor, muchas veces superando en 5 veces la ejecución de un simple SELECT en un MySQL3 comparado con un MySQL4.
En algunas instalaciones, el query caché de MySQL viene desactivado por default.
Podemos chequearlo en nuestro motor haciedo:
mysql> show variables like '%query%';
Variable_name Value
query_alloc_block_size 8192
query_cache_limit 1048576
query_cache_min_res_unit 4096
query_cache_size 0
query_cache_type ON
query_cache_wlock_invalidate OFF
query_prealloc_size 8192
De todas estas variables, las que nos definen si esta activo o no son "query_cache_type" y "query_cache_size".
query_cache_type
• Determina si el query caché esta activo, y el tipo de cacheo.
• 0: Cache inactivo. 1: Siempre se cachea, salvo que lo pidamos explicitamente con SQL_NO_CACHE. 2: Solo se cachea con la opcion SQL_CACHE
query_cache_size
• Indica el tamaño del set de datos cacheados. Si se establece en 0, se inhabilita el caché, independientemente de query_cache_type.
Ventajas y desventajas
La principal ventaja del cacheo de queries frente al esquema contrario, radica en el beneficio que implica el ahorro del tiempo de parseo frente a cada comando SELECT. El query ya no debe parsearse ni calcular su plan de ejecución, sino que ahora solo nos enfocamos a capturar el resultado cacheado.
A su vez, el cacheo de queries funciona para sentencias SELECT únicamente. Es decir, que no le sacamos el jugo con subselects, partes de UNION, stored procedures, comandos SHOW, etc.
Ahora bien, como en todo esquema computacional, rige el principio de costo-beneficio. El cacheo de queries no es gratis.
El hecho de almacenar el resultado de un SELECT le agrega un overhead al ciclo normal de ejecución:
Por ejemplo:
mysql> SELECT field1, field2 FROM table1 WHERE field1='value1';
El motor realiza el parseo, calcula los indices a utilizar, captura los resultados y "cachea los resultados".
Luego, si el cliente realiza el mismo query:
mysql> SELECT field1, field2 FROM table1 WHERE field1='value1';
El motor solo se remite a capturar el resultado del caché.
Pero bueno, aqui viene la parte triste de la historia. MySQL tiene una política de invalidacion de datos de cache muy "agresiva".
Ante cualquier accion de escritura sobre una tabla, invalida todos los datos cacheados en la misma, independientemente de los datos escritos...
Veamos un ejemplo de este concepto. Arrancamos con el caché vacio:
mysql> SELECT field1, field2 FROM table1 WHERE field1='value1';
Resultset1 calculado y cacheado.
mysql> SELECT field1, field2 FROM table1 WHERE field1='Pichongol';
Resultset1 entregado desde el caché.
mysql> INSERT INTO table1 (field1, field2) VALUES('1','Daniel Lopez');
Registro insertado y VACIADO DE TODOS LOS DATOS CACHEADOS PARA table1.
mysql> SELECT field1, field2 FROM table1 WHERE field1='value1';
Resultset1 calculado y cacheado.
Queda demostrado entonces la falta de "inteligencia" del motor para darse cuenta que el INSERT no afecta el resultado de "SELECT field1, field2 FROM table1 WHERE field1='Pichongol'", y por lo tanto no deberia ser invalidado.
De todas formas, no es una crítica sino una observacion. El hecho de dotar de esta logica al query cache quizas lo dejaria muy complejo, y la performance se veria mas afectada de lo necesario.
Sistemas intensivos en lecturas vs. Sistemas intensivos en escrituras
Entonces, teniendo en cuenta esta invalidación agresiva de resultados cacheados ante una acción de escritura, queda preguntarnos, cuando realmente nos conviene utilizar el query cache, y bajo que circunstancias.
Para responder esta pregunta, lo primero que deberiamos revisar es el sistema en cuestión.
Si nuestro sistema es intensivo en reads (lecturas), entonces el beneficio que podriamos sacar de un query caché, es claro. Los datos del caché se invalidarian muy esporádicamente, para luego volver a capturarse desde este último.
Si nuestro sistema es intensivo en writes (escrituras), seguramente deberiamos pensar en desactivarlo: Ante un flujo de INSERTS o UPDATES los datos seria invalidados constantemente, y los SELECT no solo sufrirían el overhead de almacenar el query en el caché, sino que no lo estarían utilizando ante dichas invalidaciones.
Query cache + Memcached Servers
Es frecuente en sistemas web de tráfico masivo, y a veces no tan masivo, encontrar esquemas que utilicen algún medio para persistir datos en disco (base de datos) + algun medio de acceso rápido a esos datos (Memcached Server por ejemplo).
Me ha tocado por razones laborales, ponerme a evaluar que tan necesario es el query caché cuando lo combinamos con el uso de caché en memoria.
La realidad es que con dichos esquemas de acceso rápido a los datos residentes en memoria, se disminuyen drásticamente los reads en la base de datos, llegando en la mayoria de los casos al orden del 90% de queries que ya no se efectuan.
De esta manera, nuestro sistema web termina convirtiéndose en un sistema crítico en writes (viéndolo desde el lado de la base de datos), y por tal motivo, se convierte en un sistema que constantemente invalida los datos del query caché.
Conclusión rapida y furiosa: Es contraproducente el uso de Memory Caché + Query Caché.
jueves 30 de agosto de 2007
Datetimes
a un par de desarrolladores de PHP :P.
Tiene que ver con el tratamiento de valores Datetimes de MySQL, cuyo formato más común de almacenamiento es YYYY-MM-DD HH:MM:SS.
Que pasaría si queremos traernos una fecha con este formato, y obtener algo como DD/MM/YYYY (un formato mas ... hispano?)
Podriamos hacer dos cosas:
1) Utilizar la MySQL function DATE_FORMAT haciendo algo como
mysql> SELECT DATE_FORMAT('2007-08-30 22:23:00', '$d/%m/%Y %H:%i:%s');
con lo cual estaríamos inyectando un pequeño overhead en el tratamiento del Query.
2) Traernos el Datetime y procesarlo con PHP. Todo viene a cuento de esta segunda opción. Se puede resolver todo en el procesamiento en una sola línea, anidando 5 funciones:
$date="2007-20-03 14:33:22";
$new_date = implode("/",array_reverse(explode("-",current(explode("",$date)))));
El desglose sería el siguiente:
> "2007-20-03 14:33:22"
$aux = explode("",$date);
> Array(0 => "2007-20-03", 1 => "14:33:22")
current($aux)
> "2007-20-03"
explode("-", $aux)
> Array(0 => "2007", 1 => "20", 2 => "03")
array_reverse($aux)
> Array(0 => "03", 1 => "20", 2 => "2007")
implode("/",$aux)
> "03/20/2007"
En el desglose es como que queda bastante mas claro, aunque cuando se ve toda la linea junta intimida un poco.
En fin, solo una curiosidad.
miércoles 18 de julio de 2007
Habemus Namespaces en PHP!
Profundizemos un poco sobre su uso:
namespace trabajo;
class Persona {
function getNombre(){
echo 'Pichongol';
}
static function saludo(){
echo 'Hola!, soy Pichongol';
}
}
// Entonces podemos hacer
$p = new trabajo::Persona();
echo $p->getNombre(); // Muestra 'Pichongol'
echo trabajo::Persona::saludo(); // Muestra 'Hola!, soy Pichongol'
?>
El uso de "import" se torna importante para definir las reglas de conversion de los operadores. En pocas palabras, define el/los namespaces a utilizar.
Por ejemplo
import trabajo as work; // defino un alias para el namespace trabajo
import trabajo; // defino al namespace trabajo como actual
Como punto a destacar, podemos mencionar la inclusion de la constante __NAMESPACE__, que indica el actual namespace en uso.
Cabe aclarar que aun hay ciertas cuestiones a definir, ademas de que actuamente esta siendo testeado en su funcionalidad basica. Sin embargo, la realidad es que no pude contenerme de contar la noticia :P. Los que venimos de larga data con php sabemos que los namespaces se hicieron desear bastante y lo interesante es que ya se dio el gran paso. En breve paso en limpio otros ejemplos de su futuro uso.
domingo 8 de julio de 2007
Memcached: Un alivio para las bases de datos
Memcached es definido por Danga Interactive, la empresa que lo desarrollo y mantiene el proyecto bajo licencia BSD como un “sistema distribuido de alta performance para el cacheo de objetos en memoria, genérico por naturaleza, pero pensado para incrementar la velocidad de aplicaciones Web dinámicas, aliviando la carga de las bases de datos”.
De esta manera, podríamos estar guardando en memoria una estructura serializada de PHP o Java, un string encodeado con Json, o un documento de cualquier formato (después de todo no dejan de ser cadenas de bytes).
http://jehiah.cz/projects/memcached-win32/
A grandes rasgos, el Memcached Server almacena los strings (internamente denominados ítems), en una gran tabla de hash, y los mapea según la clave que le asociemos a dicho item.
Dicha tabla de hash adopta una estructura de porciones de memoria de tamaño variable (denominadas slabs), con el objetivo de optimizar la asignación del espacio de memoria.
Un dato no menor es el slab de mayor tamaño (1 Mb), siendo este por consiguiente el tamaño máximo que un ítem puede tener si quiere ser almacenado en Memcached Server (aunque este valor puede ser modificado desde el código fuente).
Su arquitectura escalable nos permite mantener un pool de Memcached servers, característica que bien puede ser explotada en momentos donde la cantidad de conexiones no puede ser gestionada por un único Server. En ese caso, el pool optara por otro Server, balanceando la carga de conexiones.
La comunicación de clientes con servidor es muy simple, y basada en comandos.
ADD: Agrega el objeto solo si no existe.
REPLACE: Actualiza el objeto solo si existe.
Podemos eliminar un objeto mediante el comando DELETE.
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die("No podemos conectarnos");
$pichongol = new stdClass;
$pichongol->nombre = “Daniel”;
$pichongol->apellido = “Lopez”;
$memcache->set(“pichongol”, $pichongol, false, 10) or die ("No podemos guardar la estructura");
$result = $memcache->get(“pichongol”);
echo "Estructura recuperada:
\n";
print_r($result);
?>
Actualmente, su uso continúa expandiéndose, a medida que el proyecto toma mayor fuerza con la ayuda de varios desarrolladores de la comunidad Open Source que revisan y agregan nuevas capacidades al proyecto.
- LiveJournal { http://www.livejournal.com/ }
Memcached nace como un desarrollo para el backend de LiveJournal, y como tal, es el primer sitio que lo implementa.
- Slashdot { http://www.slashdot.org/ }
- Wikipedia { http://www.wikipedia.org/ }
- Fotolog { http://www.fotolog.com/ }
Luego de tener ciertas falencias de performance (producto de tener que manejar en promedio, de
- Hi5 { http://www.hi5.com }
- Facebook { http://www.facebook.com }
Contribuyó al proyecto, aportando muchas mejoras relacionadas con el manejo de memoria y los algoritmos de hashing, principalmente. Es actualmente la mayor implementación conocida, compuesta de alrededor de 200 servers de 1GB de memoria cada uno.
En el anterior ejemplo, podemos ver como la cantidad de ítems se va incrementando hasta la hora 8, momento en el cual se realizó un “restart” del Memcached Server. Luego de ese instante, los ítems vuelven a almacenarse progresivamente.
El “Hits Ratio” es el índice que nos indica la relacion de éxito entre las veces que solicitamos un item, y las veces que efectivamente esta cacheado y por ende recuperado.
Estadísticamente, y dependiendo del proyecto, es muy comun que el Hits Ratio se ubique en la franja del 85% - 98%. Claramente podemos imaginarnos la mejora al rendimiento general que esto representa, si tenemos en cuenta la merma en la carga de las Bases de datos, como así también el tiempo de respuesta de los Web servers.
El siguiente grafico nos demuestra lo anteriormente dicho.
Obviamente, en algún momento de “restart” del Memcached Server, todos los objetos cacheados se pierden, y por ende cae el Hit Ratio, para en poco tiempo ubicarse dentro de los niveles normales.
Conclusión
Memcached y escalabilidad van de la mano, y todos podemos sacar jugo de esta interesante herramienta.



