nettsundere@tsundere:~/Desktop/test$ ruby compare.rb
Введите пароль, пожалуйста:
Благодарю вас!
Для 10 элементов результаты показаны следующие:
Было: 0.00035 сек. в среднем
Стало: 0.000275 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.0001375 сек. в среднем
Для 100 элементов результаты показаны следующие:
Было: 0.0004375 сек. в среднем
Стало: 0.0002875 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.00015 сек. в среднем
Для 1000 элементов результаты показаны следующие:
Было: 0.0011375 сек. в среднем
Стало: 0.0003125 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.0001625 сек. в среднем
Для 10000 элементов результаты показаны следующие:
Было: 0.0097125 сек. в среднем
Стало: 0.00035 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.000175 сек. в среднем
Было — это с ORDER BY RAND()
Стало — это два запроса
Стало (если бы… сохранили) — это если мы max не будем вычислять каждый раз, а достанем из кэша.
Первый запрос можно и нужно кэшировать (что видно из результатов (стало и стало с сохранением max), где я кэширование лишь изобразил, дабы не писать сложный тест, но видно, что запрос на max(id) достаточно тяжелый, чтобы его всякий раз выполнять). Записать max(id) можно даже в специальную таблицу для кэша, если не имеется возможности использовать redis или memcached. Обновлять придётся лишь при добавлении новых записей и при удалении.
Ну или просто оставить как есть, если устраивает.
Пусть мы имеем множество ключей с id:
1,2,3,4,5,6,7,8
и таким методом выбираем случайную запись.
Количество «выпадений» каждого кортежа с id будет практически равно при большом количестве выборок — идеальный случай. При очень большом количестве выборок. Тут мы говорим, что всякая выборка будет равновероятна (P=1/8 для каждого id) (Закон Больших Чисел из теории вероятности). Это как бросать монетку бесконечное количество раз — примерно будет пополам орла и решки.
А теперь представим, что мы удалили записи с id 1,2,3
И теперь имеем 4,5,6,7,8
А число, что генерируется всё ещё лежит в пределе [1, 8] и по закону больших чисел будет равновероятно выдавать значения от 1 до 8, но поскольку мы обращаемся к БД с «дырками», то вероятность выпадения 4 повышается, повышается в четыре раза (всякое значение функции получения случайного кортежа, в которой сгенерировалось случайное от 1 до 4 в результате даст нам запись с id = 4). Тут мы говорим, что равновероятность не соблюдается и вероятность появления записи с id=4 равна P=4/8=1/2, а у записей c id=5,6,7,8 вероятность P=1/4
Далее представим, что записей, допустим, тысяча. Вероятность выпадения одного элемента в идеальном случае — 1/1000. Ну, допустим, пару элементов перед ним удалили и она стала 3/1000, когда у других элементов будет меньше. Для многих такая разница будет бедой, а для многих — не будет, мы же не корабли в космос запускаем. На деле разница в таких вероятностях проявится только в статистике, если размер дыр не планируется соразмерный с количеством записей вообще.
Устранять дыры можно уже потом, когда перестанет радовать неравномерность, по крону. Да только придется ли вообще это делать? Вот где вопрос. Если удаляются записи не огромными пачками последовательными, то всё будет радовать пользователя.
А это и не нужно скорее всего. Тут какая-нибудь «случайная статья» же, или товар, например. Ну можно часа в четыре утра устранить погрешность за прошлый день по крону — никто не пострадает скорее всего. Считаю архитектурные изменения излишними. Редко на ZF пишут вроде энтерпрайз, где тервер строгий необходим.
nettsundere@tsundere:~/Desktop/test$ ruby compare.rb
Введите пароль, пожалуйста:
Благодарю вас!
Для 10 элементов результаты показаны следующие:
Было: 0.00035 сек. в среднем
Стало: 0.000275 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.0001375 сек. в среднем
Для 100 элементов результаты показаны следующие:
Было: 0.0004375 сек. в среднем
Стало: 0.0002875 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.00015 сек. в среднем
Для 1000 элементов результаты показаны следующие:
Было: 0.0011375 сек. в среднем
Стало: 0.0003125 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.0001625 сек. в среднем
Для 10000 элементов результаты показаны следующие:
Было: 0.0097125 сек. в среднем
Стало: 0.00035 сек. в среднем
Стало (если бы max сохранили в redis или memcached): 0.000175 сек. в среднем
Было — это с ORDER BY RAND()
Стало — это два запроса
Стало (если бы… сохранили) — это если мы max не будем вычислять каждый раз, а достанем из кэша.
Код теста: gist.github.com/1371692
Первый запрос можно и нужно кэшировать (что видно из результатов (стало и стало с сохранением max), где я кэширование лишь изобразил, дабы не писать сложный тест, но видно, что запрос на max(id) достаточно тяжелый, чтобы его всякий раз выполнять). Записать max(id) можно даже в специальную таблицу для кэша, если не имеется возможности использовать redis или memcached. Обновлять придётся лишь при добавлении новых записей и при удалении.
Ну или просто оставить как есть, если устраивает.