Aprendiendo copiando
Hace unos días apareció en Digg una noticia titulada 10 tips for optimizing MySQL queries (artículo original), que si bien nos es mal artículo acabó eclipsado por dos replies en el propio Digg: How not to optimize a MySQL query (artículo original) y 10 Tips for Optimizing MySQL queries (That don’t suck) (artículo original). Este segundo es especialmente bueno.
De entre todos los tips me interesa el número 4 de la última página enlazada (y que es la solución al punto 6 de la primera página). Yo no tengo ni idea de como MySQL organiza sus filas en memoria o como trata sus campos de longitud variable (de hecho, ahora que lo pienso, soy el menos indicado para hablar de bases de datos, que llevo 5 suspensos seguidos), pero lo que sí sé es que esos campos de longitud variables se utilizan cuando los datos contenidos en ellos pueden ser desde muy pequeños a muy grandes, y también sé que mover por memoria, a través de un socket o a través de la red gran cantidad de datos lleva tiempo.
Lo que viene a decir el tip 4 es que si tienes una columna de datos (de muchos datos) que no utilizas demasiado lo mejor es separarla a una tabla “secundaria” referenciada desde la tabla “principal”. Cuando se necesiten los datos de la tabla “secundaria” se deberá hacer una nueva consulta (o hacer un JOIN), pero a cambio todos los accesos a la tabla “principal” serán mucho más rápidos al no incluir la problemática columna.
Todo esto viene a un “pequeño” problema que tuve en una situación parecida. La idea era que cada persona de la tabla people tuviera asociada una fotografía, como soy un SQLite-junkie no quería todas mis fotografías como archivos por el disco duro y decidí que lo mejor era almacenarlas en un campo BLOB de la base de datos junto con los demás datos de la persona. Un poco de caching en la mezcla para neutralizar una de las contraindicaciones de almacenar las fotos en la base de datos y todo listo (a pesar de esta “optimización” almacenar archivos en la base de datos no es la mejor solución, por varias razones, y posiblemente no volveré a elegir esta implementación en proyectos futuros).
¿Todo? Desgraciadamente no. Me dí cuenta de que la página que listaba a las personas tardaba un poquito más que todas las demás, incluso en local. En realidad era menos de un segundo, pero frente a la instanteneidad de las demás páginas era una eternidad. Por entonces había una veintena de personas en la base de datos, pero no podía ni pensar lo que tardaría con una centena. Entonces se me ocurrió la explicación de la “transferencia de datos” que he explicado más arriba, y comprobé que modificando los find para seleccionar únicamente los atributos necesarios era justo el empujón que la página necesitaba, pero haciendo unas cuantas pruebas más me dí cuenta de que era insignificante la diferencia entre pedir los campos justos y pedir todos menos el de la foto (cada foto eran unos 10KiB, que con 20 personas hace un total de 200KiB de “transferencia”).
Como modificar cada find del código me parecía muy anti-DRY y no veía la forma de hacerlo en plan “Zen”, lo único que se me ocurrió fué que al igual que había eager loading (carga prematura de datos) quizá Rails dispusiese de lazy loading (carga diferida de datos). Para mi desgracia ni en el core ni en forma de plugin parecía existir tal funcionalidad.
Mi último recurso fué mirar el código hecho por otros, Attachment Fu específicamente (que por entonces aún se llamaba acts_as_attachment). Y ahí entre todo el código estaba la solución más simple y más elegante: quitar el campo problemático de la tabla “principal” y moverlo a su propia tabla, referenciando los datos desde la primera.
Es una lastima que en vez de utilizar Attachment Fu, me decidí por reproducir esa parte de Attachment Fu, ya que más adelante surgió la necesidad de un sistema de “adjuntos” mucho más completo, para lo que Attachment Fu hubiese sido perfecto. Lección aprendida: no hay problema al que no se haya enfrentado alguién mucho más listo que tú.


13 de Abril de 2007 a las 09:17
Buen consejo, aunque lo que más me ha gustado ha sido la conclusión, no puede ser más acertada
. Quizá para estar en sintonía con los tiempos que corren la frase debería adaptarse añadiendo al final un y lo haya contado en su blog 
13 de Abril de 2007 a las 12:11
Sí, la frase (con tu anexo) se resume en “Google lo sabe todo (y si no lo sabe lo está aprendiendo)”.
13 de Abril de 2007 a las 13:59
[...] Daniel dijo Publicado por deigote 13 April 2007 en deigote. Etiquetas: Sin tags. Google lo sabe todo (y si no lo sabe lo está aprendiendo) Daniel en un comentario de su blog [...]