За последние 24 часа нас посетили 38504 программиста и 1284 робота. Сейчас ищут 1055 программистов ...

оптимизированный ORDER BY RAND()

Тема в разделе "MySQL", создана пользователем semnt, 26 дек 2013.

  1. semnt

    semnt Новичок

    С нами с:
    25 дек 2013
    Сообщения:
    93
    Симпатии:
    0
    Поделитесь алгоритмом выборки из БД случайного набора записей (например 10 записей).
    Я всегда использовал:
    Код (Text):
    1. ORDER BY RAND()
    ... но я был очень удивлён, когда обнаружил, что этот метод работает медленно. Это незаметно на небольших таблицах, но попробуйте поэксперементировать с таблицей в миллион записей и вы заметите, что такая выборка занимает порядка 0.5 сек. На порталах с нормальной нагрузкой это очень много для одного запроса.

    Запрос выглядит так:
    Код (Text):
    1.  
    2. SELECT `id`
    3. FROM `big_tab`
    4. ORDER BY RAND()
    5. LIMIT 10;
    Для создания таблицы в миллион записей можно использовать вот такой запрос:
    Код (Text):
    1.  
    2. CREATE TABLE `big_tab` (
    3.     `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    4.     PRIMARY KEY (`id`)
    5. )
    6. COLLATE='utf8_general_ci'
    7. ENGINE=MyISAM;
    8.  
    9. INSERT INTO `big_tab` ( `id` ) VALUES ( NULL );
    10.  
    11. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    12. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    13. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    14. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    15. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    16. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    17. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    18. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    19. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    20. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    21.  
    22. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    23. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    24. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    25. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    26. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    27. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    28. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    29. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    30. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
    31. INSERT INTO `big_tab` ( `id` ) SELECT NULL FROM `big_tab`;
     
  2. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    Откажись от myisam
     
  3. semnt

    semnt Новичок

    С нами с:
    25 дек 2013
    Сообщения:
    93
    Симпатии:
    0
    igordata, если таблица на движке InnoDB то такой запрос занимает тоже порядка 0.5 сек (даже чуть больше чем для MyISAM).
    А Вы какой движок посоветуете?
     
  4. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    мдя, тоже не слишком лучше.
    а эта, айдишники подряд идут, или всё же есть условия и пропуски?
     
  5. semnt

    semnt Новичок

    С нами с:
    25 дек 2013
    Сообщения:
    93
    Симпатии:
    0
    Сейчас в тестовой таблице пропусков нет, но в рабочем варианте пропуски конечно будут (записи удаляются).
     
  6. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    Можно попробовать генерить случайные двадцать айдишников, выбирать и смотреть хватило ли. Повторить если нет.
     
  7. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    ищите ту на формуме. эту тему уже обсасывали вдоль и поперек. там было много решений, со своими плюсами и минусами.
     
  8. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    тормоза ORDER BY RAND() объясняются тем, что при каждом запросе создается временная таблица с чем-то вроде "новых случайных id" для нашей сортируемой таблицы.

    хорошее решение для одной случайной записи из большой таблицы:
    http://jan.kneschke.de/projects/mysql/order-by-rand/
    суть — выбор первого значения с id >= RAND() * MAX(id). скорость почти постоянна для любого размера таблицы.

    для N случайных записей можно добавить какие-то трюки с циклом, кешем и блекджеком.
     
  9. semnt

    semnt Новичок

    С нами с:
    25 дек 2013
    Сообщения:
    93
    Симпатии:
    0
    artoodetoo, спасибо, это интересно. Значит придётся использовать процедуру в SQL, чтобы достать несколько ID'шников.
     
  10. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    Мне оч нравится движок blackhole.
    А вообще надо ждать, пока Falcon не войдет в релизы, интересная штука намечается. Еще из экзотики движок Memory. Работает а-ля мемкеш, только реляционный. То есть таблички существуют не в виде файлов, а в виде куска оперативки. Работают соответственно. Для часто дергаемой мелкотуры, которую не жалко потерять - самое оно.
     
  11. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    мемори жрёт новую память каждый раз когда ему не хватает, и жрёт ее столько, сколько уже имеет - растёт на двое.
    а освободившуюся не отдаёт. это надо понимать, прежде, чем использовать.
     
  12. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    Сдается мне мемори отдаст все что угодно, если хорошо попросить :)
    ALTER TABLE name ENGINE=MEMORY;
     
  13. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    Если убить.
     
  14. retvizan

    retvizan Активный пользователь

    С нами с:
    27 дек 2013
    Сообщения:
    68
    Симпатии:
    22
    А разве на него не забили?

    Зависит от характера разреженности, например, если записи с id: 1,99,100, то случайность крайне условная. В такой ситуации нужен limit X,1 Пример реализации здесь:
    http://sqlinfo.ru/forum/viewtopic.php?pid=11053#p11053
     
  15. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    Ситуация неоднозначная. Мб и родят-таки, а мб сдох :)