Система хранения "лайков" в tarantool

496 views
Skip to first unread message

Евгений Панков

unread,
Jul 24, 2013, 9:37:32 AM7/24/13
to tarant...@googlegroups.com
День добрый.

Встал вопрос о хранении лайков к контенту. так и не нашел более оптимального варианта для хранения лайков.
Требования
   - возможно высокая нагрузка
   - Ограниченные объемы оперативки, хотелось бы использовать только "горячие лайки" в памяти, остальное на жестком

Реляционный подход отпадает, индексы растут дуром(бить на парцирование пока не выход)
Более менее подходит Redis с множествами
Графовая база тут не знай насколько актуально(хотя к ней склоняюсь), так как нужен функионал только на получение и проставление лайков.

1) Хотел узнать по поводу хранения хвоста  в тарантуле на жестком диске.
2) Если не секрет, или совет. что используется в mail или vk Для хранения лайков, примерно архитектура , и строит ли строить индексы для лайков в тарантуле?

Спасибо.

Konstantin Cherkasoff

unread,
Jul 24, 2013, 9:46:16 AM7/24/13
to tarant...@googlegroups.com
Офтопик, конечно, но
"Odnoklassniki uses Cassandra as their primary storage for of all
Like! buttons..."
http://planetcassandra.org/Company/ViewCompany
--
Konstantin Cherkasoff


24 июля 2013 г., 17:37 пользователь Евгений Панков
<panko...@gmail.com> написал:
> --
> Вы получили это сообщение, поскольку подписаны на группу tarantool-ru.
>
> Чтобы отказаться от подписки на эту группу и перестать получать из нее
> сообщения, отправьте электронное письмо на адрес
> tarantool-ru...@googlegroups.com.
> Настройки подписки и доставки писем:
> https://groups.google.com/groups/opt_out.
>
>

Konstantin Osipov

unread,
Jul 24, 2013, 10:01:07 AM7/24/13
to tarant...@googlegroups.com
* Евгений Панков <panko...@gmail.com> [13/07/24 17:44]:
Мейл большой, Мой Мир, по крайней мере пару лет назад, хранил в
Тарантуле.

Обычно лайк устроен так:

event_id, last_updated, like_count, user_id, user_id, user_id, ...

Старые лайки надо вытеснять, да, только обычно вытеснять надо весь
лайк, а не "хвост" - устаревает событие, которое люди лайкают,
соответственно и всё событие надо вытеснять.

У нас diskstore всё ещё не доступен в мастере, поэтому вытеснять в
наш движок не получится. Но у нас есть внутри Lua доступ к
MySQL/PostgreSQL, потому можно устаревшие лайки мигрировать в него
(запускается expiration fiber на Lua который обходит лайки и
прибивает старые, опционально оставив просто object_id, count, и
убрав всех юзеров чтобы не тратить память).

--
http://tarantool.org - an efficient, extensible in-memory data store

Konstantin Osipov

unread,
Jul 24, 2013, 10:22:53 AM7/24/13
to tarant...@googlegroups.com
* Konstantin Cherkasoff <k.cher...@gmail.com> [13/07/24 18:11]:
> Офтопик, конечно, но
> "Odnoklassniki uses Cassandra as their primary storage for of all
> Like! buttons..."
> http://planetcassandra.org/Company/ViewCompany

Возможно для масштаба Одноклассников это оправдано, у них есть
необходимость в cross-data-center репликации.

В целом сравнивать in-memory и не in-memory это яблоки с
апельсинами, у Tarantool/Memcached/Redis принципиально ниже
max response time, если это не нужно - то и связываться с
in-memory имхо смысла нет.

Евгений Панков

unread,
Jul 24, 2013, 4:27:26 PM7/24/13
to tarant...@googlegroups.com
Да вопрос in-memory, потому что в минимальной конфигурации это elementId,userId по ним индексы, если хранить в базе то получиться значения в таблице и индексе,
единственный + что индекс без лишних полей покрывающий, и работать с результатом можно не дергая таблицу(если поддерживает система такие индексы, но место лишнее жрет), 
поэтому реализация заведомо in-memory, выгружать куда-то конечно хороший подход, выше приведенный пример картежа не совсем представляю пока как оптимальней обойти в lua.
Использовали ли вы voltdb вроде шардиться, и построить 1 индекс можно было бы по elementId.
- Через какое время примерно скидывали в базу обратно лайки(выгружали из кеша)? 
- Через промежуток времени обновляли базу с лайками? или формировали очередь с запросами на изменение данных в бд чтоб не потерять данные?
- Какой примерно самый большой список из лайков(кол-во)?
- Подгрузка обратно в кеш дело может быть не простое ведь, если список большой( 
а так пока идея , redis(множества)+tarantool(хранение последнего времени изменения)+база

Konstantin Osipov

unread,
Jul 25, 2013, 2:00:44 AM7/25/13
to tarant...@googlegroups.com
* Евгений Панков <panko...@gmail.com> [13/07/25 00:38]:

> Да вопрос in-memory, потому что в минимальной конфигурации это
> elementId,userId по ним индексы, если хранить в базе то
> получиться значения в таблице и индексе, единственный + что
> индекс без лишних полей покрывающий, и работать с результатом
> можно не дергая таблицу(если поддерживает система такие индексы,
> но место лишнее жрет), поэтому реализация заведомо in-memory,
> выгружать куда-то конечно хороший подход, выше приведенный
> пример картежа не совсем представляю пока как оптимальней обойти
> в lua.

То есть список тех, кто поставил лайк не нужен?

> Использовали ли вы voltdb вроде шардиться, и построить 1 индекс можно было
> бы по elementId.

Нет.

> - Через какое время примерно скидывали в базу обратно лайки(выгружали из
> кеша)?

Подробностей реализации в Мире я не знаю.
Но Тарантул всё-таки достаточно надёжен, то есть те данные что живут
в нём нет необходимости хранить где-то ещё, достаточно лишь
обеспечить выселение холодных данных.

> - Через промежуток времени обновляли базу с лайками? или формировали
> очередь с запросами на изменение данных в бд чтоб не потерять данные?

> - Какой примерно самый большой список из лайков(кол-во)?

Имеется ввиду список пользователей?
Может быть достаточно большой, например десятки и сотни тысяч.
Ситуация как раз такая что большая часть имеет небольшое
количество, но есть 1% супер-популярных.

> - Подгрузка обратно в кеш дело может быть не простое ведь, если список
> большой(
> а так пока идея , redis(множества)+tarantool(хранение последнего времени
> изменения)+база

Что-то сложно очень :), по-моему надо выбрать либо редис либо
тарантул.

Max Lapshin

unread,
Jul 25, 2013, 2:08:01 AM7/25/13
to tarant...@googlegroups.com
А как можно хранить лайки без списка поставивших?

Dmitry E. Oboukhov

unread,
Jul 25, 2013, 3:31:25 AM7/25/13
to tarant...@googlegroups.com
> А как можно хранить лайки без списка поставивших?

например позволять лайкать повторно :)
либо не позволять используя 'барьер на клиенте'
для большинства случаев вполне пойдет
--
_______________
< tarantool.org > Mail.RU
---------------
\ ^__^
\ (oo)\_______
(__)\ )\/\ Dmitry E. Oboukhov <un...@debian.org>
U ||----w | GPGKey: 1024D / F8E26537 2006-11-21
|| || 1B23 D4F8 8EC0 D902 0555 E438 AB8C 00CF F8E2 6537

signature.asc

Konstantin Osipov

unread,
Jul 25, 2013, 4:05:05 AM7/25/13
to tarant...@googlegroups.com
* Dmitry E. Oboukhov <un...@tarantool.org> [13/07/25 12:03]:
> > А как можно хранить лайки без списка поставивших?
>
> например позволять лайкать повторно :)
> либо не позволять используя 'барьер на клиенте'
> для большинства случаев вполне пойдет

Тут ещё дело в том, что надо, например, нотификации периодически
полайкавшим рассылать.

И список полайкавших уметь показывать.

Евгений Панков

unread,
Jul 25, 2013, 6:03:39 AM7/25/13
to tarant...@googlegroups.com
Списки лайков обязательно хранить, tarantool хотел использовать для хранения element_id, last_event_synh(последнее время синхронизации с овновным хранилищем), last_updated(по нему отслеживать когда выгружать из кеша),like_count
использовать его как управляющее хранилище, базу как архивирование холодных лайков, редис без журналирование, только снятие снапшотов, использовать для работы со списками, так как уже поддерживает нативно  + получать последние лайки удобно, ну и драйвер пока быстрей response получает чем с tarantool. Реализовать как одно целое не особо впринципе сложно. Если где-то отвалятся лайки, для меня пока что это не критично

Dmitry E. Oboukhov

unread,
Jul 25, 2013, 7:06:00 AM7/25/13
to tarant...@googlegroups.com
ну и создаете спейс

el_id, last_sync, last_updated, count [, uid1, uid2, ...]

далее пишете луа функцию которая лайкает, примерно так будет
выглядеть:

-- возвращает 0 - если не лайкнуло, 1 если лайкнуло
funcion like(el_id, uid)
local l = box.select(space, 0, el_id)
if l == nil then
box.insert(space, el_id, 0, box.time(), uid)
return 1
end

if l:find(3, uid) ~= nil then
-- апдейтим время последнего обновления если нужно
box.update(space, el_id, '=p', 2, box.time())
return 0
end

box.update(space, el_id, '=p!p', 2, box.time(), #l + 1, uid)
return 1
end


далее пишете файбер, который "вытесняет" данные в БД, для него можно
как использовать дополнительные индексы, так и не использовать.

если использовать, то его функция будет более эффективна, но будет
больше брать памяти индекс. если нет, то он будет более тупым итп.

если не использовать то примерно так:

funcion expire()

for l in box.space[space].index[0]:iterator(box.index.ALL) do
if box.unpack('i', l[2]) < box.time() - 3600 then
move_like(l)
else
-- даем поработать остальным
box.fiber.sleep(0)
end
end

end

запустить можно из init.lua:

box.fiber.wrap(
function()
while true do
expire()
-- раз в 10 минут запускаем процесс
box.fiber.sleep(600)
end
end
)

функция move_like может класть лайк в БД постргрис/мускл. сейчас в
тарантуле вы можете прямо из луа ходить в постргрю/мускл.

кстати функция like тоже может доставать их оттуда (из mysql/pg),
но только надо подумать на счет если вдруг толпа пришла людей а лайк в
постгри лежит. то есть флаг какой-то держать что такой-то лайк
вытаскивается сейчас, чтобы только первый запрос шел в БД, а остальные
его ждали.
signature.asc

Евгений Панков

unread,
Jul 25, 2013, 9:13:21 AM7/25/13
to tarant...@googlegroups.com
Спасибо за код, попробую на тестах, насколько ресурсоемкая операция на распаковку кортежа(если будет несколько тыщ, сот, милионов, значений)
- Можно ли получать из tarantool конкретные поля а не весь кортеж? без lua
- l.find(3,uid)   первый параметр 3 это позиция откуда ищеться?
- Почему мир ушел от подобного подхода , и куда если не секрет)
а так пока для меня наверное самый рабочий вариант, правда уже lua на редис пописал, но лучше в 1 запрос все проблемы решать.

Dmitry E. Oboukhov

unread,
Jul 25, 2013, 11:23:26 AM7/25/13
to tarant...@googlegroups.com
> Спасибо за код, попробую на тестах, насколько ресурсоемкая операция на
> распаковку кортежа(если будет несколько тыщ, сот, милионов, значений)
> - Можно ли получать из tarantool конкретные поля а не весь кортеж? без lua
> - l.find(3,uid) первый параметр 3 это позиция откуда ищеться?

размер кортежа ограничен ЕМНИП одним мегабайтом

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

лучше тогда нормализовать.

у меня была нужда реализовать коллекции большой длины (в среднем
коллекция длиной 20, но блин *редкие* долетают до миллиона) и я
выделил спейс на хранение коллекций

name(text), internal_id(num64), value

праймари ключ у меня name:internal_id

далее можно посмотреть на хвост коллекции нечто вроде:

function last(name)

return box.space[space].index[0]:max(name)

end

и на голову:

function first(name)

return box.space[space].index[0]:min(name)

end

и далее сделать push:

function push(name, value)

local l = last(name)
local no = tonumber64(0)
if l ~= nil then
no = box.unpack('l', l[1]) + tonumber64(1)
end
return box.insert(space, name, box.pack('l', no), value)

end

и это будет коллекция неуникальных значений. к ней можно дописать
методы pop, shift, unshift (что у меня и сделано)



если мы хотим коллекцию уникальных значений то делаем праймари ключ:

name, value

и второй ключ

name, internal_id

тапл: name, internal_id, value

по второму ключу можно восстановить порядок с которым помещены записи
(взять последнюю/первую). Праймари ключ адресует запись

function first(name)
return box.space[space].index[1]:min(name)
end

function last(name)
return box.space[space].index[1]:max(name)
end

function push(name, value)
local t = box.select(space, 0, name, value)
local no = tonumber64(0)
if t == nil then
local l = last(name)
if l ~= nil then
no = box.unpack('l', l[1]) + tonumber64(1)
end
end
return box.insert(space, name, box.pack('l', no), value)
end



как-то так.

у меня в неуникальной коллекции было еще необходимость удалять
элементы по значению - было два индекса:
name:id (праймари), name:value (неуникальный)

ну а в коллекции уникальных
name:value (праймари), name:id (уникальный)

получается два спейса на все случаи жизни.

но в целом для лайков вероятно это подойдет если вы вытеснять лайки
собираетесь очень редко и поэтому будут накапливаться большие
значения.

кстати функция like() из предыдущего примера могла контроллировать
размер и вытеснять по достижении скажем 100-200 лайков в тапле. и не
городить огород с коллекциями в спейсах.



> - Почему мир ушел от подобного подхода , и куда если не секрет)
> а так пока для меня наверное самый рабочий вариант, правда уже lua на редис
> пописал, но лучше в 1 запрос все проблемы решать.

мир никуда не ушел, все хайлоады только на этом подходе и вертятся :)
signature.asc

Евгений Панков

unread,
Jul 25, 2013, 12:05:31 PM7/25/13
to tarant...@googlegroups.com
С ограничением размера коллекции хорошая идея, думаю более достоверную уникальность можно делать на синхранизации с базой.
Огромное спасибо, пощупаю тарантул на такие схемы, посмотрю что лучше по нагрузке какую схему хранить

Alexandre Kalendarev

unread,
Jul 25, 2013, 4:29:58 PM7/25/13
to tarant...@googlegroups.com
у нас для лайков был разработан свой NoSQL сторадж на базе Tokyo
именно из-за того что тарантул мемори-онли, мы отказались его использовать и решили сделать своё решение
кстати на счет редиса -он тоже мемори онли, и его стоит использовать с остоложностью если лайков будет слишком много.

Наш сторадж имел несколько разных банков - банк - аналог key/value таблицы
Банк может быть одним из типов COUNTER INDEX DATA
Если ты лайкаешь, то увеличивается COUNTER того кого лайкнули, а в его  INDEX  добавляется твой ID, чтоб не было повторного лайка.

Аналогичным образом можно реализовать и на тарантуле.

PS. подал на эту тему доклад на Hi++...



25 июля 2013 г., 20:05 пользователь Евгений Панков <panko...@gmail.com> написал:
С ограничением размера коллекции хорошая идея, думаю более достоверную уникальность можно делать на синхранизации с базой.
Огромное спасибо, пощупаю тарантул на такие схемы, посмотрю что лучше по нагрузке какую схему хранить

--
Вы получили это сообщение, поскольку подписаны на группу tarantool-ru.
 
Чтобы отказаться от подписки на эту группу и перестать получать из нее сообщения, отправьте электронное письмо на адрес tarantool-ru...@googlegroups.com.
Настройки подписки и доставки писем: https://groups.google.com/groups/opt_out.
 
 



--
Alexandre

Alexandre Kalendarev

unread,
Jul 25, 2013, 4:38:56 PM7/25/13
to tarant...@googlegroups.com
В дополнение к ранее сказанному:
Для реализации INDEX можно использовать составной ключ  в спейсе типа TREE, где первая часть ключа есть id кого лайкнули, а вотрая -того кто лайкнул ,соответственно выбор оскуществляется операцией box.select_rang() от ключа ID,00000000 до ID.FFFFFFFF


26 июля 2013 г., 0:29 пользователь Alexandre Kalendarev <aka...@gmail.com> написал:



--
Alexandre

Dmitry E. Oboukhov

unread,
Jul 25, 2013, 4:54:46 PM7/25/13
to tarant...@googlegroups.com
> у нас для лайков был разработан свой NoSQL сторадж на базе Tokyo
> именно из-за того что тарантул мемори-онли, мы отказались его использовать и
> решили сделать своё решение
> кстати на счет редиса -он тоже мемори онли, и его стоит использовать с
> остоложностью если лайков будет слишком много.

> Наш сторадж имел несколько разных банков - банк - аналог key/value таблицы
> Банк может быть одним из типов COUNTER INDEX DATA
> Если ты лайкаешь, то увеличивается COUNTER того кого лайкнули, а в его  INDEX
> добавляется твой ID, чтоб не было повторного лайка.

> Аналогичным образом можно реализовать и на тарантуле.

> PS. подал на эту тему доклад на Hi++...

ты обещал попилить драйвера для PhP
signature.asc

Alexandre Kalendarev

unread,
Jul 25, 2013, 6:00:46 PM7/25/13
to tarant...@googlegroups.com


ты обещал попилить драйвера для PhP

 вроде как Костя сказал, что допилят сами,  
но принципиально есть время, так как сейчас в отпуске, а поездка сорвалась из-за болезни ребенка,
так что могу уже завтра взяться...


Alexandre

Alexandre Kalendarev

unread,
Jul 25, 2013, 6:17:44 PM7/25/13
to tarant...@googlegroups.com
> - Какой примерно самый большой список из лайков(кол-во)?

у меня служба знакомств, активных 25М пользователей
кол-во лайков на простые анкеты от 5 или 50 до 540 -730...
но есть и популярные, те кто уже попал в топ на первую страницу, их  лайкают все подряд:  более 3500 - 4500
конечно - это не контакт :), наши нужды среднего проекта вполне удовлетворяет.

--
Alexandre
Reply all
Reply to author
Forward
0 new messages