2012-03-28 26 views
5

Estoy buscando una manera eficiente de seleccionar aleatoriamente 100 filas que satisfagan ciertas condiciones de una tabla MySQL con potencialmente millones de filas.¿Cómo seleccionar al azar varias filas que satisfagan ciertas condiciones de una tabla MySQL?

Casi todo lo que he encontrado sugiere evitar el uso de ORDER BY RAND(), debido a su bajo rendimiento y escalabilidad.

Sin embargo, this article sugiere ORDER BY RAND() todavía se puede utilizar como una "manera buena y rápida" para buscar datos randow.

Basado en este artículo, a continuación se muestra un código de ejemplo que muestra lo que estoy tratando de lograr. Mis preguntas son:

  1. ¿Es esta una forma eficiente de selección aleatoria de 100 (o hasta varios cientos) filas de una tabla con los potencialmente millones de filas?

  2. ¿Cuándo se convertirá en un problema el rendimiento?

 
    SELECT user.* 
    FROM (
      SELECT id 
      FROM user 
      WHERE is_active = 1 
      AND  deleted = 0 
      AND  expiretime > '.time().' 
      AND  id NOT IN (10, 13, 15) 
      AND  id NOT IN (20, 30, 50) 
      AND  id NOT IN (103, 140, 250) 
     ORDER BY RAND() 
      LIMIT 100 
      ) 
      AS  random_users 
    STRAIGHT JOIN user 
    ON  user.id = random_users.id 
+0

Tiene sentido seleccionar valores aleatorios en un campo con índice. – Kayser

+0

@Kayser, me preocupa que aún tengamos que escanear TODAS las filas para conocer las condiciones DONDE. ¿Va a afectar el rendimiento con una gran tabla (potencialmente millones de filas)? – user1298692

+0

Es probable que el método con la subselección pk reduzca solo marginalmente el tiempo de ejecución. Esto se debe a que con o sin esta técnica, se llama a rand() para todas las filas coincidentes, y el número de filas para ordenar es el mismo. Presumiblemente, esto es interesante si "usuario" tiene muchas columnas, o columnas grandes en tamaño, y mysql no es lo suficientemente inteligente como para esperar después de que el LÍMITE tenga lugar para materializar al usuario. * (Eso debe probarse). –

Respuesta

0

me temo que nadie va a ser capaz de responder a su pregunta con precisión. Si realmente quieres saber, necesitarás ejecutar algunos puntos de referencia en tu sistema (no el vivo, idealmente, sino una copia exacta). Compare esta solución con otra diferente (obteniendo las filas aleatorias usando PHP, por ejemplo) y compare los números con lo que usted/su cliente considera "buen desempeño". Luego incremente sus datos tratando de mantener la distribución de los valores de las columnas lo más cerca posible de la realidad. como sea posible y vea dónde comienza a caer el rendimiento. Para ser honesto, si funciona para usted ahora con un poco de margen, entonces yo iría por eso. Cuando (¡si!) se convierte en un cuello de botella, entonces puede verlo de nuevo - o simplemente tirar de hierro extra en su base de datos ...

1

se recomendamos encarecidamente que lea este article el último segmento será que cubre la selección de fila al azar múltiple y usted debería ser capaz de notar la expresión en SELECT.. el PROCEDURE que se describirá allí. Ese sería el lugar donde agrega su estafa específica WHERE ditions.

El problema con ORDER BY RAND() es que esta operación tiene una complejidad de n*log2(n), mientras que el método descrito en el artículo que he vinculado tiene una complejidad casi constante.

supongamos, que la selección aleatoria fila de la tabla, que contiene 10 entradas, utilizando ORDER BY RAND() toma 1 time unit:

entries | time units 
------------------------- 
     10 |   1  /* if this takes 0.001s */ 
     100 |  20 
    1'000 |  300 
    10'000 |  4'000 
    100'000 | 50'000 
1'000'000 | 600'000  /* then this will need 10 minutes */ 

Y usted escribió que se trata de tabla de la escala de millones de personas.

0

preproceso tanto como sea posible intentar algo así como (ejemplo VB-como)

Dim sRND = New StringBuilder : Dim iRandom As New Random() 
Dim iMaxID As Integer = **put you maxId here** 
Dim Cnt as Integer=0 
While Cnt < 100 
     Dim RndVal As Integer = iRandom.Next(1, iMaxID) 
     If Not ("10,13,15,20,30,50,103,140,250").Contains(RndVal) Then 
      Cnt += 1 
      sRND.Append("," & RndVal) 
     end if 
End While 
String.Format("SELECT * FROM (Select ID FROM(User) WHERE(is_active = 1) AND deleted = 0 AND expiretime > {0} AND id IN ({1}) .blahblablah.... LIMIT 100",time(), Mid(sRND.ToString, 2)) 

Yo no comprobar la sintaxis pero vas a mi deriva espero. Esto hará que MySql lea los registros que se ajustan a 'IN' y se detendrá cuando llegue a 100 sin la necesidad de preprocesar todos los registros primero.

Háganme saber la diferencia de tiempo transcurrido si lo intenta. (Estoy seguro)

Cuestiones relacionadas