Голосование за сущности (когда голосов и сущностей много)
Задался вопрос об оптимальном способе хранения голосования за некоторые сущности. Классический вариант это счетчик у сущности(что-то вроде votes_count) и отдельная таблица votes(user_id, vote).
Однако при таком подходе если оставлять возможность голосовать за все сущности вечно, при количестве, например 500 000 штук, за которые можно голосовать и среднем количестве голосов 3 получаем 1 500 000 тысяч записей в таблице vote.
Вопрос — есть ли лучший способ хранения этих данных? И как вы поступаете в ситуациях с высокопосещаемыми ресурсами и голосовалками за те же комменты?
PS: На хабре вроде нельзя посмотреть в старой теме, что ты голосовал за коммент, соответственно таблица чистится по истечении тайм-аута. Видимо для экономии места и ускорении выборок.
Вариантов работы с большим количеством данных множество. Зависит от того, какие цифры нужно получить. К примеру, если не нужно помнить кто конкретно за что конкретно голосовал, то можно данные агрегировать. Т.е. взять сущность, посчитать сколько голосов отдано за период Х и запись в базу одной запись, а из таблицы с голосами для данной сущности за этот период голоса удалить.
Если нужно все и всегда, то бъем таблицу на куски и старые записи просто сохраняем в архивные таблицы. Данные метод совершенно не исключает агрегирования, он просто помнить все голоса в системе.
P.S. Можно. Я без проблем нашел свой голос 2-ей давности.
Поясните, пожалуйста, мысль про архивные таблицы. Если у нас есть связка (comment_id, post_id, user_id,vote) по каким признакам их бить? Допустим первые десять миллионов comment_id в одной таблице, вторые десть во втрой и так далее?
При наличии трех отдельных индексов по comment_id, post_id, user_id эта таблица может содержать десятки, и сотни миллионов записей и ничего тормозить не будет. Партиционирование (разбиение данной таблицы на несколько таблиц по какому-либо критерию — например, по 10 миллионов id) применяется как правило в тех случаях, когда старые (архивные) данные изменяются очень редко, а новые (последние) очень часто. Партиционирование можно выполнить конечно и вручную (создать отдельные таблицы и работать с ними), но проще реализовать это средствами современных СУБД, поддерживающих партиционирование. Однако повторюсь, ИМХО, в вашей задаче разбивать таблицу на партиции не требуется — просто создайте необходимые индексы.
>Если у нас есть связка (comment_id, post_id, user_id,vote) по каким признакам их бить?
Лично я бы использовал, да и использую, другую структуру: (id, type, user_id, vote), где id — инкрементально возрастающий первичный ключ, type — ENUM описывающий тип ресурса (а данном случае либо comment либо topic). Смысл в бОльшей расширяемости без необходимости изменения структуры таблицы. К примеру, завтра возникла необходимость добавить голоса и за пользователей. Все что нужно — добавить в ENUM новый тип user. Структура таблицы, код останутся прежними.
Касательно разбиения да, можно пилить и по такому простому принципу. В любом случае я бы рекомендовал вынести эту бизнес логику на уровень СУБД. К примеру, в Postgresql можно сделать наследование таблиц + повесить триггеры которые будут сами вычислять, в какую таблицу за данными нужно ходить. Приложение же работает с этими данными как с одной таблицей. Как результат такая таблица хорошо маштабируется на уровне СУБД абсолютно прозрачно для приложения. Если завтра у нас возникнет необходимость работы с огромным массивом данных, то такая таблица может быть легко разнесена на 2 (3, 4...n) серверов без изменения логики в самом приложении. Главное, что бы в рамках одной партиции все индексы помещались в ОЗУ.
alan008 >когда старые (архивные) данные изменяются очень редко
Архивные данные не изменяются по определению. Если они изменяются, то это не архивные данные автоматически.
До сих пор поступал именно описанным в вопросе «классическим» способом.
Но для nosql архитектуры, с которой только знакомлюсь на основе Amazon SDB, можно прямо в записи сущности собирать id проголосовавших пользователей. Работает для малого числа голосов за одну сущность — для примера с харбракомментариями. Для +\- варианта придётся в два поля видимо писать id: кто минусует, и кто плюсует. Либо изобретать синтакс типа id+5 или id-3 для +5 или -3 баллов соотв.
Хорошо бы представлять себе реальное использование этих баллов, и предполагаемые рамки использования.