2012-03-28 2 views
5

Я ищу эффективный способ случайного выбора 100 строк, удовлетворяющих определенным условиям из таблицы MySQL, с потенциально миллионами строк.Как случайным образом выбрать несколько строк, удовлетворяющих определенным условиям, из таблицы MySQL?

Практически все, что я нашел, позволяет избежать использования ORDER BY RAND() из-за низкой производительности и масштабируемости.

Однако this article предлагает ORDER BY RAND() все еще может использоваться как «хороший и быстрый способ» для получения данных randow.

На основании этой статьи ниже приведен пример кода, показывающего, что я пытаюсь выполнить. Мои вопросы:

  1. Является ли это эффективным способом случайного выбора 100 (или до нескольких сотен) строк из таблицы с потенциально миллионы строк?

  2. Когда производительность будет проблемой?

 
    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

Имеет смысл выбирать случайные значения в поле с индексом. – Kayser

+0

@Kayser, я обеспокоен тем, что нам еще нужно сканировать ВСЕ строки для условий WHERE. Это повлияет на производительность с огромной таблицей (возможно, миллионы строк)? – user1298692

+0

Метод с помощью pk-subselect скорее всего уменьшит лишь незначительное время выполнения. Это потому, что с этой техникой или без нее, rand() вызывается для всех совпадающих строк, а количество строк для сортировки одинаковое. Предположительно, это интересно, если «пользователь» имеет множество столбцов или больших столбцов по размеру, а mysql недостаточно умен, чтобы ждать после того, как LIMIT имеет место для материализации пользователя. * (Который должен быть протестирован). –

ответ

0

Боюсь, никто не будет в состоянии ответить на ваш вопрос с любой точностью. Если вы действительно хотите знать, что вам нужно будет запустить некоторые тесты против вашей системы (а не в реальном времени, но точную копию). Сравнивайте это решение с другим решением (например, получая случайные строки с использованием PHP) и сравнивая числа с тем, что вы/ваш клиент считаете «хорошей производительностью». Затем наращивайте свои данные, пытаясь сохранить распределение значений столбцов как можно ближе к реальным как вы можете видеть, где производительность начинает снижаться. Если быть честным, если он работает для вас сейчас с небольшим запасом, то я бы пошел на это. Когда (если!) это станет узким местом, вы можете посмотреть на него еще раз - или просто добавьте утюг в вашу базу данных ...

1

Настоятельно настоятельно рекомендуем вам прочитать это article. Последний сегмент будет охватывать выбор нескольких случайных строк. И вы должны иметь возможность заметить заявление SELECT в PROCEDURE, который будет описан там. Это место, где вы добавите свой конкретный WHERE con виях.

Проблема с ORDER BY RAND() заключается в том, что эта операция имеет сложность n*log2(n), а метод, описанный в статье, который я связал, имеет почти постоянную сложность.

Давайте предположим, что при выборе случайной строки из таблицы, которая содержит 10 записей, используя ORDER BY RAND() принимает 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 */ 

И вы писали, что вы имеете дело с таблицей на масштабах миллионов.

0

Preprocess как можно больше попробовать что-то вроде (VB-как пример)

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)) 

Я не проверял синтаксис, но вы получите мой дрейф, я надеюсь. Это сделает записи в формате MySql, которые соответствуют «IN», и остановятся, когда достигнут 100, без предварительной обработки всех записей.

Пожалуйста, дайте мне знать разницу прошедшего времени, если вы попробуете его. (Я доброжелателен)

Смежные вопросы