Страницы

Поиск по вопросам

воскресенье, 1 декабря 2019 г.

Реляционная БД — где хранить счётчики (кол-во комментариев, кол-во лайков)?

#sql #база_данных #sql_server #архитектура #структуры_данных


В базе данных есть Записи, у которых могут быть Лайки и могут быть Комментарии. Нужно
отобразить список Записей и для каждой Записи отобразить кол-во Лайков и Комментариев.



Собственно, как это лучше сделать? Я вижу три варианта (не считая вариантов использования
кэша, вроде Redis):


Запрашиваем список Записей и для каждой записи запрашиваем COUNT Лайков и Комментариев.
Но это как-то слишком затрано, по-моему. Будет много запросов к базе.
Сохраняем кол-во Лайков и Комментариев в таблице Записей. Придётся добавить дополнительную
логику, но мне такой вариант нравится больше всего.
Создаём ещё одну таблицу PostInfo, в которой будем хранить кол-во Лайков и Комментариев
для конкретной Записи. При запросе Записей делаем JOIN к этой таблице. Так, наверное,
правильнее всего будет, так как Записям не нужно знать о кол-во Лайков и Комментариев.

    


Ответы

Ответ 1



В SqlServer вариант 1 можно реализовать с помощью materialized view, в этом случае по затратности он будет приближаться к третьему варианту. Создаём представление: create view dbo.LikeCount with schemabinding as select post_id, Cnt = count_big(*) from dbo.Like group by post_id GO Добавляем индекс, материализующий представление: create unique clustered index IX_LikeCount on dbo.LikeCount (post_id) GO Аналогичное представление можно создать для таблицы Comment. После чего можно запрашивать данные следующим образом: select p.id, p.title, isnull(lc.Cnt, 0) as LikeCnt, isnull(cc.Cnt, 0) as CommentCnt from Post p left join LikeCount lc with (noexpand) on lc.post_id = p.id left join CommentCount cc with (noexpand) on cc.post_id = p.id Удобство такого варианта в том, что при изменении данных в таблицах Comment и Like количества будут автоматически пересчитываться (в вариантах 2 и 3 эту логику придётся реализовывать специально - в триггерах, или процедурах/запросах, которые работают с добавлением/удалением лайков и комментариев). Минус в том, что для лайков и комментариев представления раздельные, в следствие чего дополнительных соединений в запросе два (в варианте 3 оно будет одно, а в варианте 2 его вообще не будет). Также, если есть вероятность, что в будущем может потребоваться удалять пользователей, оставляя лайки, то этот вариант не подойдёт, и лучше смотреть в сторону вариантов 2 и 3. Вариант 2 выгоднее для select (в запросе не потребуется дополнительное соединение для вытаскивания количеств лайков и комментариев). Вариант 3 лучше с точки зрения независимости постов от сопутствующих им количеств (если происходит изменение записи, соответствующей какому-то посту, и в это же время кому-то вздумалось лайкнуть эту запись, то эти два действия не будут блокировать друг-друга).

Ответ 2



По-моему, надо исходить из использования: хранить лучше в нормализованной схеме, т.е. 1-й вариант; читать быстрее из единого места: чтобы пост + счетчики (2-й вариант). Минус второго варианта в том, что вычисляемые данные хранятся для всех записей, в то время, как реально требуется только вершина айсберга – свежие записи, которые читают/запрашивают. Поэтому решение – кэш, тот же Redis, в котором держать записи+счетчики, а в БД – только данные по 1-му варианту. В кэше ограничить время жизни записи. При чтении искать в кэше, если нет, то дергать БД и писать в кэш. При новом лайке/комментарии обновлять БД и кэш, если запись в кэше ещё присутствует.

Ответ 3



Во втором и третьем случае вы рискуете получить веселенькие грабли с синхронизацией всех этих счетчиков. Пока вы - не фейсбук, наиболее простым будет вариант 1.

Комментариев нет:

Отправить комментарий