LINUX.ORG.RU

Как хранить лайки/реакции в базе?

 


3

2

Сообщенька в базе - это бинарный «документ» (лежащий в чём-то типа mongoDB или типа redis, в общем неком key=value), про который известны оффсеты до всех полей. Совершенно аналогично «туплу», который «строка» в «традиционных» табличных СУБД. В общем, сообщенька - это, можно сказать, строка в БД, в которой можно так же апдейтить/инкрементить отдельные произвольные «поля»/«колонки». В общем, скажем для простоты, что это «обычная строка в обычной БД».

У сообщеньки есть 8-битная поле/колонка - likes. Там лежит либо 0, либо 1.

Есть отдельные «микросервис» лайков - совершенно отдельная «субд», заточенная под хранение лайков - она хранит key=set, где key идентифирует пролайканный объект, тип лайка. Таким образом, можно понять сколько у этого объекта лайков, лайкал ли ты (данный uid) уже эту сущность, и автоматически не дать тебе что-то лайкнуть 2 раза.

Лайки - это частный случай реакции, реакция типа ноль. Например, если uid=123 лайкнул сообщеньку номер 10000 в чатике «zuzu», то uid=123 поставил реакцию типа ноль и в микросервисе лайков мы увидим ключ: zuzu:10000:0 в котором лежит set и в этом set мы обнаружим 123 среди прочих.

Теперь прикол в том, что показывая каждую сообщеньку, я не хочу ходить в микросервис лайков, выясняя есть ли для данной сообщеньки там ключ. Поэтому я умный и ставлю единичку в поле likes в сообщеньке, если был замечен хоть один факт лайков. Поле likes означает стоит ли ходить в микросервис лайков, выясняя сколько лайков у этой сообщеньки и лайкал ли ты её. Это сильно облегчает жизнь.

Теперь непонятно, как в сообщеньке таким же образом (как поле likes) закешировать инфу о том, что этой мессаге поставили не только лайк, но ещё и «огонь» и ещё «палец вверх».

Превратить likes в битовую маску - жопа, т.к. заранее неизвестно, сколько разных реакций возможно.

Есть идеи, как грамотно кешировать в самой строке сообщенки инфу о том, какие разные реакции у этой сообщеньки в принципе есть? Пока что приходит в голову только массивчик бинарных 16-битных чиселок, где каждое число означает, что реакция соответствующего типа вообще у этой сообщеньки хоть раз встречалась. Соответственно, когда какую-то реакцию кто-то ставит впервые, мы достаём этот массивчик, дописываем к концу новое число и сохраняем в мессагу обратно - но это некоторая жепь-ебрилло, поскольку подразумевает полный апдейт всей мессаги, ибо строка имеет переменный размер. Хранить в конце документа-бла-бла - не круто, т.к. где у него конец мы не знаем, ибо в сообщеньке есть ещё и текст переменной длины.

Ну или можно такой «массивчик», хотя на самом деле отдельный key=set хранить отдельно. Т.е. колонка likes говорит, что реакции в принципе были, далее уже достаём этот key=set и смотрим какие именно были.



Последнее исправление: trisobakov (всего исправлений: 2)

Ответ на: комментарий от etwrq

ну лайкая сообщеньку юзер подписывается в определённую ЦА. с этим ты что будешь делать?

Чё такое ЦА не знаю, сорян. Повезло мне: делать ничё не придётся.

trisobakov
() автор топика
Ответ на: комментарий от alysnix

а 64 бита не спасут?… сложно представить более 64х видов реакций… я и 32 представить не могу.

Во всяких Slack этих реакций как говна за баней, да ещё каждое сообщество там придумывает свою новую. Например чего стоят флаги всех государств, 100 видов сердечек и все известные мемы вида «not bad».

trisobakov
() автор топика
Ответ на: комментарий от trisobakov

я б имел 2 базы.

база сообщений

  1. сообщеньки (id, text, ключ первого элемента списка в базе реакций )

база реакций

  1. списки реакций на сообщение с неким id.

если в сообщении ключ списка на него реакций есть - значит есть какие-то реакции и нужно лезть в базу реакций по ключу первого элемента.

список реакций можно устроить по разному, в простейшем случае это id юзера, id типа реакции и ключ след. элемента списка

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от kardapoltsev

А выбирать их потом рекурсивно?

последовательно.

а мессаге id первого элемента в базе реакций. в первом этом элементе - id следующего элемента…и так далее. при добавлении новой реакции можно добавлять в голову списка, то есть вводится новая запись в реакции, ее поле _next ставится в текущую голову списка, текущая голова(записана в мессага) становится ключом новой. то есть тут апдейтится и база записей и база реакций.

чтобы апдейтить только базу реакций, можно или в хвост вставлять, или вставлять после первой записи(тогда модифицируется поле _next первой записи)…

для быстрой вставки в хвост, первый элемент может иметь поле указывающее на последний элемент. или можно отдельно делать запись «список», которая имеет ключ первого и последнего элемента списка.

alysnix ★★★
()

Мне порой кажется, что NoSQL для комментариев это такое проявление садизма - только чтобы посмотреть как потомки будут прикручивать к ним дополнительный функционал.

ritsufag ★★★★★
()

склалось впечатление, что просто ТС (или кто там что-то проектнул) недавно узнал про битовые поля и бинарные форматы, поэтому пытается воткнуть их повсюду где дотягивается.

А вот на@#й там биты и смещения ? вот чем принципиально вызвана сия необходимость…

MKuznetsov ★★★★★
()
Последнее исправление: MKuznetsov (всего исправлений: 1)
Ответ на: комментарий от kardapoltsev

Итого чтоб отобразить 10 сообщений у каждого по 10 реакций делаем 100 запросов. Не, редис конечно быстрый, но мне кажется это не очень то хороший путь.

разумеется, держать список надо блоками по, например, 8 реакций… идеально размер блока д.б. таким чтобы >90 процентов сообщений имели только один блок реакций, тогда понадобится только два чтения на сообщение с реакциями.

alysnix ★★★
()
Ответ на: комментарий от alysnix

На самом деле список не нужен. Держим все реакции как строку интов в колонке likes (вместо одного 8-битного инта, который там щас лежит). 03010007000800 - это вектор размера 3 (первый байт) - (1,7,8). Больше чем страница B+-Tree этот вектор не раздует в любом случае (так что 8-битная длина этого вектора - годнота), иначе это уже изврат (мессага, у которой больше 255 РАЗНЫХ реакций - это боль и «ненужно» даже визуально).

То есть, сохранить вектор интов вида (11, 501, 800, 804) (естественно в бинарном виде, где инт 16-битен) в самой сообщеньке в отдельной колонке и читать это в составе сообщеньки - вполне годнота.

То есть, реальна реализация, в которой чтение 10 сообщенек - это вообще 1 чтение. Сообщеньки-то лежат физически друг за другом в одном блоке B+-Tree, и в каждой из них свой вектор вида (300, 303, 308). То есть, выгребание поддиапазона сообщенек треда, учитывая, что тред - это key=list<message> - это вообще последовательное чтение куска памяти, т.к. участка одного блока, т.е. даже с диска это будет 1 чтение. Всё отлично.

Как апдейтить: если сервис лайков, куда я запихнул реакцию типа 501 (лицо Обамы «Not Bad») сказал, что этой сообщеньке эту реакцию я поставил первым, ТОГДА пойти и проапдейтить колонку likes сообщеньки - достать оттуда вектор интов, добавить 501, положить обратно. Можно CAS-ом в цикле, чтобы 501 не пролюбить. В целом, этих апдейтов будет мало - по числу уникальных реакций. Получние 1000 сердечек и 2000 Обам - это таких 2 апдейта.

Всё красиво, но я нагнал слегка.

Нам ещё нужно узнать чиселку у каждой реакции. Поэтому, каждая сообщенька породит N запросов, где N - длина вектора из колонки likes. Допустим, если чатик называется hello, id сообщеньки - 13444, а её likes - это (1,7,8), то в сервис лайков нам надо отправить 3 запроса len("hello:13444:1"), len("hello:13444:7"), len("hello:13444:8").

На прочие вопросы

Зачем NoSQL

Важно что там внутри физически и за что ты хочешь платить. Если у тебя в MySQL табличка из 2 колонок (key,value), первичный и единственный индекс по key и движок InnoDB то на диске ты имеешь то же самое и столько же чтений с диска при доступк к ключу, что будет в не-MySQL-опердени, которая использует B+-Tree для key-value. Дальше начинаются низкоуровневые детали, «исторически сложилось».

Зачем ковыряться в битах.

Причина - пошлое грязное бабло и user experience. Скажем, ранний VK работал в штатах быстрее FB, а современная телега не тормозит при 100500 нагрузки в неё во все дыры как раз из-за этого байтоёбства. Код, который физически хранит в телеге ваши мессаги читать без крови из глаз вы тоже не сможете, но всем насрать на страдания полутора несчастных рабов в серверных, как пассажирам титаника было похер на жару в машинном зале (и затонул он не поэтому), важно как быстро телега открывается, сколько пассажиров на борту и сколько дисков Дурову нужно купить в сервера, сколько канала сожрать на клиентов и на бекапы в соседний ДЦ. В результате байтоёбства расход этих бездуховных денег сокращается и тупо продлевает жизнь проекта, что уже прямо политика и бизнес. Как говорил мужик в красной рубашке на 1:18:00 на этом видосе https://www.youtube.com/watch?v=rX0ItVEVjHc - люди, которым пофиг как долго сортируется список - причина того, что у меня ворд в винде по 30 секунд запускается! Так что не будем наезжать на байтоёбство, оно нужно и важно, просто не для всех. Так же, как не все люди проходят отбор в Navy SEAL в США - чё теперь, запретить их.

trisobakov
() автор топика
Последнее исправление: trisobakov (всего исправлений: 5)

А давай ТС выпишет здесь какие запросы будут приходить в его систему. И какая должна быть алгоритмическая сложность получения ответов в процессоре и в числе чтений с диска.

А то звучит все конечно по умному, но обоснования никакого нет.

OxiD ★★★★
()