LINUX.ORG.RU

Избранные сообщения trisobakov

Как реализовать лайки в кастомной СУБД?

Форум — Development

Есть кастомная СУБД, типа на redis, которая умеет хранить key=SET. SET - это хранилка уникального набора строк. По ключу key я могу в этот SET добавлять/удалять строки с результатом «успех/фейл», получать текущее число строк в этом SET и т.п. То есть, эта СУБД условно представляет собой C++-структуру map<string, set<string>>.

Юзер uid нажал лайк на посте/комменте/фотке obj_id - тогда я втыкаю в SET с именем likes_{obj_id} новую строку obj_id:

DB.insert("likes_" + obj_id, uid);

Мы можем достать из ключа likes_{obj_id} список лайкеров данного поста, просто увидеть число лайков, взяв size() этого SET и просто не дать юзеру лайкнуть что-то дважды, SET ведь не жрёт одну строку дважды.

Например, у меня есть чатик (группа в телеграме), а при заходе в чатик надо показать сразу 50 последних сообщений. Если сам чатик хранитсяв структуре данных типа key=LIST, то достать любой подинтервал сообщений чатика очень быстро по диску/памяти, но вот нарисовать у каждого сообщения сердечко с числом лайков этого сообщения - это уже нужно перебирать ключи likes_{obj_id}, где obj_id - конкретная сообщенечка. Когда 10К юзеров поставят утюг на F5 в этом чатике, серверу станет больно.

Чтобы сервер так не болел, в каждой сообщенечке есть бинарный header, где в каком-то месте есть 4-байтный счётчик лайков, а в СУБД реализован такой транзакционный прикол:

// Найти в key2 SET, вставить в него str и если это удалось
// то найти в key1 строку, и трактуя 4 байта по offset как uint32 заинкрементить этот uint32 на 1.
increment_uint32(key1, offset, key2, str);

Аналогично есть декремент и прочие подобные штуки. То есть, лайк сообщения сразу инкрементит что-то в header этого сообщения, никак не меняя длину объекта-сообщения. Таким образом, доставая сообщение, я сразу получаю и его число лайков.

Ну, в логике «классических» СУБД, типа mysql, можно сказать, что сообщение - это строка в таблице, у строки есть колонка «likes_cnt» и эту колонку в этой строке инкрементят какой-то хранимой процедурой, которая не инкрементит это дважды для данного юзера и данного сообщения. В общем, tuple из типизированных полей он и в африке такой: ему можно менять поля, не меняя его размер и сохраняя offset до каждой колонки константным.

Теперь возникает боль: а лайков-то разных бывает много. Лайк - это только один из видов реакции на сообщение. Во всяких там Slack разных реакций существует 100500 и юзеры ещё и свои умеют добавлять. Тут уже прикол с header внутри сообщения проканает чуть менее, чем никак. Я конечно могу в колонке «likes_cnt» хранить адский бинарный JSON вида {«likes»:10, «dislikes»:80, «cry»:4, «fuck»:1}, перезаписывая полностью на какое-то изменение, но это какая-то боль и профнепригодность. Посоветуйте каких-нибудь дата-структурных идей, имея то допущение, что в нашей кастомной СУБД мы можем реализовать что взбредёт в голову, подобно вон тому «условному инкременту любых байт по любому оффсету» и даже страшнее.

 

hellonik
()

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

Форум — Development

Сообщенька в базе - это бинарный «документ» (лежащий в чём-то типа 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
()