В нескольких проектах, над которыми я работал, я столкнулся с необходимостью выбирать случайные строки из больших (> 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 секунды на строку и включает удаление случайной строки каждые пять итераций.С ростом таблицы она замедляется, но выборка случайной строки - нет.
Мои оригинальные вопросы по-прежнему применяются.