Top 100 mundial y optimización

Vía el blog de Emili Parreño, he descubierto un wiki con el top100
mundial de aplicaciones Rails:
http://www.eparreno.com/2008/09/05/las-100-aplicaciones-rails-con-mas-trafico

Y jugando con el Google Trends, descubro que mi verema.com está en el
cincuentaytantos… moooolaaaaa!!
http://www.verema.com
http://trends.google.com/websites?q=verema.com%2C+communitywalk.com%2C+howcast.com%2C+ma.gnolia.com%2C+basecamphq.com
Aunque dudo de su fiabilidad, porque lacoctelera nos da mil vueltas y
sin embargo sale más abajo…

Por cierto, del blog de Emili, me ha gustado mucho su último post sobre
optimización:
http://www.eparreno.com/2008/09/13/optimizacion-mejorando-las-consultas-mysql-en-rails-i/

Algunos de los consejos que da han quedado desfasados con la versión de
rails 2.1.1, que es mucho más eficiente construyendo SQLs que las
versiones anteriores (particularmente find_by_sql ha perdido mucho
interés), pero hay temas como los índices que son terriblemente
efectivos… y lo del cache de modelos es un descubrimiento para mí,
pero tiene muy pero que muy buena pinta, y tengo un caso perfecto para
probarlo con el gestor de banners de mi aplicación, que se usa en casi
todas las páginas pero cambia muy poco.

Por cierto, si alguien tiene que mejorar las prestaciones de una
aplicación:
1.- Si no está en Rails 2.1.1, es hora de migrarla.
2.- Buscad los puntos débiles con New Relic, http://www.newrelic.com/ .
Incluso la versión gratuita es una pasada.
3.- Cuidado con el will_paginate; si tenéis tablas muy grandes y os pone
un enlace a la última página, normalmente eso obligará a recorrer todos
los registros y será una consulta extremadamente pesada (y no hay forma
de optimizarla); y lo peor de todo es que, debido a ese enlace, Google
os indexara esas páginas, y tendréis tráfico en ellas… con sólo
limitar los enlaces a las 10 primeras páginas, y permitir acceder al
resto pero con NOINDEX, he rebajado el nivel de carga de la Base de
Datos a menos de la tercera parte!! Y sólo he tocado una sección, que
era el cuello de botella del sistema.

s2

2008/10/9 Fernando C. [email protected]

3.- Cuidado con el will_paginate; si tenéis tablas muy grandes y os pone
un enlace a la última página, normalmente eso obligará a recorrer todos
los registros y será una consulta extremadamente pesada (y no hay forma
de optimizarla); y lo peor de todo es que, debido a ese enlace, Google
os indexara esas páginas, y tendréis tráfico en ellas… con sólo
limitar los enlaces a las 10 primeras páginas, y permitir acceder al
resto pero con NOINDEX, he rebajado el nivel de carga de la Base de
Datos a menos de la tercera parte!! Y sólo he tocado una sección, que
era el cuello de botella del sistema.

Lo de que no hay forma de optimizarla… discrepo.

Un collection de will_paginate, tiene tres
camposhttp://github.com/mislav/will_paginate/tree/master/lib/will_paginate/collection.rb#L49
(per_page,page
y total). En la mayoría de los casos, una buena definición de modelo,
con
counter_cache debería bastar para proporcionar al usuario contadores y
al
paginador totales.

A veces me ha venido bien usar una tabla de contadores con
counter_cache. El
registro que trae el contador en la mayoría de los casos está
cargado/cacheado, por lo que mostrar no supone carga (efectivamente en
el
momento de crear el registro, implica tardar un poquito más).

Para resultados de búsqueda, muchas veces el valor es estimado (Como
hace
google, gmail o sphinx) y no interesa dar el esacto por el coste.

Para situaciones en las que counter cache no se puede usar, muestro
siguiente, y si tengo una mínima estimación, a veces me he planteado
usar la
filosofia de gmail de poner: cientos, miles, millones, etc…

Respecto a la indexación de google, me gusta desactivarla para todo
aquello
que no sea contenido, incluso limitando solo a acciones show mediante
plantilla, (if action != show => meta noindex) SEO o no, es como a mi me
gusta y la indexación va en gustos.

Espero a ver aportado algo.

Un Saludo.

Otra idea es para optimizar paginadores es cargar de golpe las 3
primeras páginas y guardarlas en memoria: el 90% de los usuarios no
pasarán de la 3ª página, así que te estás ahorrando 2 requests más
además de que para la base de datos, una vez tiene ordenados los
registros que te va a devolver, la de la mismo si te devuelve 10 o 30.

Esto es lo que hace la gente de Google, Facebook y demás…

2008/10/9 Guillermo [email protected]:

Fernando, gracias por la mención. En cuanto a lo que comentas de Google
Trends, este tipo de aplicaciones (alexa, compete…) no son muy
exactas, simplemente dan una orientación de por donde van los tiros.
Sobre Newrelic estuvimos hablando en la ultima reunión del BCN on Rails,
yo tengo una cuenta bronze desde hace tiempo ya que tengo una aplicación
en EY y nos la regalan, y la verdad es que vale la pena pagar los 40$
por lo menos durante un mes para optimizar al máximo la aplicación y
luego volver a la cuenta lite. Te da una visión global sobre el
comportamiento de la aplicación que no tienes con otras herramientas ni
mirando los logs.

Respecto a lo que estáis comentando de la paginación, otra detalle a
tener en cuenta es utilizar indices decrementales si estamos ordenando
por fecha, en una tabla muy grande se nota la diferencia entre ordenar
ascendentemente y ordenar descendentemente.

Saludos

Emili Parreño
www.eparreno.com

Os cuento con detalle mi caso concreto, por si a alguien le sirve,
porque en mi caso la diferencia de rendimiento ha sido muy importante.

El will_paginate, en una página “normal”, pone enlaces a las primeras 9
páginas y a las 2 últimas:

Eso no es problema en una página índice del foro, que además sólo hay
una; pero fijaos que tengo una página con “lo que han escrito mis
usuarios favoritos”, y esa no es sólo una, sino que es una para cada
usuario, lo que acaba siendo muchas:
http://www.verema.com/usuarios/favoritos/fernan2

No necesito caché de contadores para saber el nº de registros; contarlos
es sencillo, pues puedo filtrar por el conjunto de usuarios y es una
consulta rápida. Acceder a los datos recientes también es sencillo,
ordenando por fecha es rápido encontrar los 25 primeros de mis usuarios
(quiza estén entre los 1000 primeros totales), pero si me tengo que ir a
históricos con números de página elevados… la consulta es una pasada,
porque no puedo filtrar por un único campo (ni es sólo mensajes de foro,
o sólo notas de cata, ni es sólo el usuario X); dado que filtro por un
conjunto de usuarios (no por un sólo usuario) y ordeno por fecha, me
tiene que recorrer la tabla de contenidos entera:
http://www.verema.com/usuarios/favoritos/fernan2?page=634
SELECT xx FROM contenidos FORCE INDEX (cnt_fecha_ix) inner join
favoritismos f on contenidos.usuario_id = f.favorito_id WHERE
(f.usuario_id = 12256) ORDER BY contenidos.created_at desc LIMIT 15825,
25;
Rows_examined: 460340

¿Habéis visto? Para encontrar el registro 15.825 de mis usuarios
favoritos, se tiene que recorrer casi la tabla de contenidos entera, que
es el núcleo de la aplicación con cientos de miles de registros (sin el
FORCE INDEX es aun peor). Algún acceso esporádico no es problema, pero
si Google indexa esto… entre las consultas del robot y las búsquedas
de la gente, me hacen polvo, para una cosa que total ya está accesible
por otras vías (el foro, las catas, etc). Por eso, quitar los enlaces a
las últimas páginas y añadir el NOINDEX para registros de la página 100
en adelante ha sido una mejora de prestaciones crucial.

Así que, si alguna vez os encontráis con una consulta que no podéis
optimizar, y que recorre muchísimas filas accediendo a la última página,
ya sabéis: se pone el will_paginate con sólo 10 páginas, y NOINDEX si
(params[:page] and params[:page] > 100)…

s2

El día 10 de octubre de 2008 20:14, Fernando C.
[email protected]
escribió:> usuario, lo que acaba siendo muchas:

conjunto de usuarios (no por un sólo usuario) y ordeno por fecha, me
es el núcleo de la aplicación con cientos de miles de registros (sin el
(params[:page] and params[:page] > 100)…
Hola Fernando.
Disculpá que te haga una pregunta totalmente offtopic: ¿Está el índice
sobre el campo contenidos.usuario_id y favoritos.usuario_id?
Te hago la pregunta, porque el forzado de índice te puede servir para
algunos casos y para otros no. Para el caso de un usuario con pocos
“favoritos” y pocos “contenidos” para dichos favoritos, es
infinitamente mejor que el motor primero filtre los contenidos para
luego ordenar por el campo fecha, antes de recorrer la tabla completa.
Yo revisaría los índices para ver porqué el motor no elige la mejor
forma antes de forzar.

Saludos
Silvio

Hola, Silvio, el índice es por contenidos.created_at; si vamos a la
primera página, ese índice da muy buenos resultados, porque la gente
tiene indefectiblemente entre sus usuarios favoritos a alguno que
participa mucho, y por tanto podemos razonablemente suponer que los 25
últimos contenidos de mis favoritos estarán entre los 1000 últimos
contenidos. El supuesto que tú planteas, de que alguien tenga pocos y
malos favoritos, es muy improbable en la práctica… la gente a quien se
guarda es a los usuarios que más participan, como es lógico. Y para ese
patrón, si queremos irnos a la página 1000, no hay forma de optimizarlo
(que yo sepa, y he probado bastantes cosas).

Probablemente, la no-optimización del motor se debe a que no entiende de
ingeniería social, y piensa que las probabilidades de que yo me guarde a
un usuario que ha escrito un mensaje son iguales que las de que yo me
guarde al tío que más escribe; pero en realidad, no es así… y cuanto
más probable sea que me guarde al tío que más escribe, mejor será el
enfoque por fecha (rápidamente encontraré los 25 primeros de mis
favoritos) y peor el enfoque por usuario (la cantidad de contenidos de
mis usuarios favoritos será muy grande).

s2