Gapless вторичный идентификатор для выборки случайных строк в MySQL - PullRequest
0 голосов
/ 08 мая 2011

В нескольких проектах, над которыми я работал, я столкнулся с необходимостью выбирать случайные строки из больших (> 1M строк) таблиц.С такими большими таблицами ORDER BY rand() LIMIT 1 не вариант, поскольку он быстро поставит базу данных на колени.

Обычным решением было генерировать случайное число в диапазоне от MIN(id) до MAX(id) и выбирать эту строкунепосредственно.Однако, если в последовательности идентификаторов есть большие пробелы, это потребует либо большого количества повторных бросков, либо использования WHERE id >= :myrandomnumber, что приведет к тому, что строки с большими пробелами получат значительно больше хитов, чем в среднем.думал решить эту проблему, создав новый индексированный столбец исключительно для целей рандомизации, скажем id2.Этот столбец всегда будет последовательностью без пробелов между 1 и числом строк в таблице.

Вопрос: Как лучше всего сохранить эту последовательность без промежутков?

Первое решение, которое приходит на ум, - это создание вспомогательной таблицы recycled_ids, которая будет содержать столбцы tablename и id2.Всякий раз, когда строка удаляется из tablename, id2 этой строки вставляется в recycled_ids.Когда вставляются новые строки, id2 выбирается из recycled_ids или, если ни одна из них не доступна, создается новая.Есть ли более простой способ?

Бонусные вопросы: Существуют ли ORM или структуры, которые уже делают это или имеют эффективный случайный выбор строк?Это существующий шаблон, у него есть имя?


Обновление: Я написал быстрый тест для этого и провел его по таблице с 125 000 строк и 30 000 пробелов между ними,Результаты довольно многообещающие:

Fetch a random row 100 times using id2: 0.0234689712524 seconds
Fetch a random row 100 times using ORDER BY rand() LIMIT 1: 54.992347002 seconds

При вставке тестовых данных я удалил одну случайную строку на каждые пять вставленных строк.Последовательность остается без промежутка все время.

for($i=1; $i<=$amount; $i++) {
    insert_row();
    if($i % 5 == 0)
        delete_random_row();
}

Повторное выполнение этого цикла с $amount = 10000 занимает 9 секунд на моем младшем сервере.Это 0,009 секунды на строку и включает удаление случайной строки каждые пять итераций.С ростом таблицы она замедляется, но выборка случайной строки - нет.

Мои оригинальные вопросы по-прежнему применяются.

Ответы [ 2 ]

0 голосов
/ 08 мая 2011

Рейтинг на помощь, я бы сказал.

SET @rank:= 1;  

SELECT * FROM
  (
  SELECT @rank:= @rank + 1 as rank, * FROM table1  
  ) s
WHERE s.rank = $random;
0 голосов
/ 08 мая 2011

Вот как я это сделаю -

  1. Выберите МАКС (id) из вашей таблицы
  2. В PHP (или на любом другом языке, который вы используете), сгенерируйте случайныйцелое число от 1 до MAX (id)
  3. SELECT * FROM table WHERE id >= $random ORDER BY id ASC LIMIT 1
  4. Если 3 ничего не возвращает, SELECT * FROM table WHERE id < $random ORDER BY id DESC LIMIT 1

Предотвращает выполнение любых запросов, которые будут крайне медленными.Это также позволяет избежать лишних столбцов, которые, если оставить их без промежутков, действительно будут ужасной работой!

...