Есть кастомная СУБД, типа на 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}, перезаписывая полностью на какое-то изменение, но это какая-то боль и профнепригодность. Посоветуйте каких-нибудь дата-структурных идей, имея то допущение, что в нашей кастомной СУБД мы можем реализовать что взбредёт в голову, подобно вон тому «условному инкременту любых байт по любому оффсету» и даже страшнее.