Publi

Pecados capitales: ORDER BY RAND() cuando sólo queremos una fila

Cometí el error cuando estrené las aplicaciones de frases en Facebook de utilizar la siguiente sentencia SQL para sacar una frase aleatoria:

1
SELECT * FROM `frases` ORDER BY RAND() LIMIT 1;

Al principio funcionaba bien, la verdad es que no confiaba mucho en el crecimiento de la aplicación, por lo que dejé el código así. Pero al cabo de unos días, noté que mi proveedor de hosting desactivó mi cuenta por excesivo uso de CPU: el rápido crecimiento de usuarios y frases dentro de la aplicación causaron un uso exponencial de CPU en mi servidor.
Al principio no parece lógico pero si nos fijamos en lo que estamos haciendo con esta línea:

  • Ordenamos de forma aleatoria la tabla frases (normalmente es más pesado que ordenar por fecha, o por un entero)
  • Escoger el primero de la lista obtenida (esto ya no cuesta nada)

Es decir, estamos ordenando la lista por completo (de unas 2000 frases) cada vez que un usuario quiere consultar sólo 1. Por lo que al final (imaginémonos unos 30000 usuarios a la hora), hacemos muchas ordenaciones innecesarias que aumentan el uso de CPU.

Una buena solución podría ser extraer el total de filas que tenemos en la tabla (es más, no tendremos que extraerlo siempre, sino cuando añadamos o borremos información), es una consulta bastante ligera; y luego extraer un sólo elemento empezando por la fila obtenida al azar.
En el siguiente ejemplo propongo un código en PHP que obtiene una fila aleatoria de una tabla (estas líneas las extraigo de un proyecto más grande, por lo que se podrían simplificar un poco más):

1
2
3
4
5
6
$sql="SELECT COUNT(id) FROM `frases`";
$res=mysql_fetch_row(mysql_query($sql));
// Tenemos en $res[0] el número de filas
$num=rand(0, $res-1); // Obtenemos un número aleatorio.
$sql="SELECT * FROM `frases` LIMIT ".$num.",1";
$frase_elegida=mysql_fetch_array(mysql_query($sql));

He dejado un poco de lado las comprobaciones y he dado por hecho que en la tabla frases hay algún contenido. Pero creo que queda bastante clara la idea.

Puede haber más soluciones, pero creo que con esta tenemos más control sobre los resultados (podemos introducir condiciones al número y más cosas).

También podría interesarte...

There are 2 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

  2. algm /
    Usando Mozilla Firefox Mozilla Firefox 3.5.5 en Windows Windows 7

    También vale: SELECT * FROM frases ORDER BY $num LIMIT 1
    Aunque tu solución parece más eficiente 🙂

    Por cierto, la eficiencia de las consultas Select en MySQL aumenta si las tablas son MyISAM y si en vez de poner SELECT *, pones los nombres de los campos.

    Saludos!

Leave a Reply