Как стать автором
Обновить

Комментарии 27

Не хватает сравнения с Couchbase

1. масштабирование там проще, при этом добавить сервак + ребаланс на порядки проще
2. есть обычный интерфейс memcache (не надо переписывать приложение) + есть удаление старые записей
3. можно использовать view\index для таггирования «кеша»
4. нету «приятной» особенонсти mongodb, когда система синхронизирует память с диском и сервис становится недоступным на какое-то время.

плюс у монго это конечно свобода запросов из «кеша», в кауче надо добавлять на каждый подход свой индекс, но если это все работает именно как кеш — то набор возможных запросов к кешу весьма ограничен.
www.couchbase.com/forums/thread/couchbase-bucket-and-lru-least-recently-used
Couchebase has no LRU implementation. You can approximate it with long expiry times, but that can be problematic if you have a lot of cache which becomes invalid frequently. If you try to store items with 0 expiry they will live forever in a couchbase bucket.

Правда, эта запись 2-летней давности: что-то поменялось с тех пор? Если ничего не поменялось, то Couchbase можно подключить к CacheLRUd для реализации стратегии очистки LRU в ней.

Я бы хотел еще отметить, что LRU — это НЕ удаление «старых записей». Это удаление НАИБОЛЕЕ РЕДКО ЧИТАЕМЫХ записей. Т.е. запись может быть очень-очень старой (добавлена неделю назад), но ее регулярно читают, и поэтому она не должна вымываться (LRU именно для этого).
Да, именно LRU нету насколько я в курсе.
У меня видимо таких задач не было поэтому я плохо представляю когда такой подход может быть необходим. Не проще ли поставить expire на неделю условно. Если эти данные актуальны — скрипт не найдет их в кеше и загрузит снова из базы. Если не актуальны, то через неделю они сами пропадут.

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

И как вариант «костылей» для couchbase:

1. в класс кеширования на get дописываем обновление записи (отмечу, что сохраняется json, те при get можно добавлять\обновлять поле условно last_access_time), само время кеширования можно поставить 0
2. добавляем view на поле last_access_time
3. удаляем записи старше Х в любой удобный момент времени, хоть по крону, хоть по какой-то рэндому в момент выполнения скрипта. Операция быстрая, можно делать и «на лету».

Expire на неделю? Так за неделю накопится столько, что никакого места не хватит! Весь смысл в том, чтобы держать в кэше наиболее часто востребованные данные, вытесняя те, что востребованы редко. И вот вариантов этих данных может быть очень много.

Наглядная ситуация (хотя, возможно, не очень хорошая) — представьте, что у вас есть очень большой и «разлапистый» сайт, и вы каждую сгенерированную на нем страницу кладете в кэш, ключом которого является URL страницы. И тут пришли черви и начали перебирать URL-ы, добавляя ?a=1, ?a=2 и т.д. к адресам страниц (условно).

Более реалистичный пример — кэш кусочков страниц, в ключ которого замешаны timestamp-ы некоторых ресурсов. Обновился ресурс, ключ изменился, начали генерироваться и читаться другие кэш-данные, а старые потихоньку устарели и «вымылись». Это довольно удобно, когда в качестве ключа можно взять md5(serialize(что-то)) и не думать о размере кэша.

«В класс кеширования на get дописываем обновление записи» — ну так нельзя же делать, в посте про это есть. Нельзя на каждую операцию чтения генерировать операцию записи. Вернее, если все это живет на одной машине, то можно, но когда машин много, да еще и в разных датацентрах они стоят… получите неустойчивую архитектуру. Обновлять last_access_time в документах коллекции можно только асинхронно, что, собственно, CacheLRUd и делает.
Да, в вариенте кеширования всей страницы и бота подставляющего параметры — представил ситуацию переполнения кеша.
Хотя наверно это стоит решать какой-то фильтрацией параметров либо формируя ключ кеша страницы на основе ответов блоков из которых состоит страница.

Вариант 2 честно пытался представить но не смог. Если что-то вымывается, то оно вымоется и просто по expire time.

Но наверно когда-то удобнее определять размер кеша, а не его время, просто я с этим не сталкивался.
Оно вымоется по expire time, весь вопрос в том, что это может произойти слишком поздно, и место на диске закончится раньше. Самое плохое в этом то, что оно может закончиться непредсказуемо раньше: вчера было все хорошо, а завтра место закончилось за 10 минут. LRU спасает именно от этого.
В couchabse можно использовать memcached bucket, где есть LRU. А масштабироваться она при этом будет как couchbase. Само собой никакой надежности, т.к. данные не имеют реплик и хранятся исключительно в памяти.
docs.couchbase.com/couchbase-manual-2.2/
memcached Buckets
Replication: No

Т.е. под вопросом надежность операции очистки кэша по тэгу как минимум, плюс работает только шардинг, не репликация (в некоторых случаях это и неплохо).
Не вижу ничего страшного в потере закешированных данных.
Ну при чем здесь потеря закэшированных данных. :) Речь о том, что данные могут оказаться неверными. Ресурс изменился, вы очистили связанный с ним тэг, а он не очистился, и все видят старые данные.
Неверными они могут оказаться в случае рассинхронизации кластера. Подобные сообщения пишутся в лог. При наличии такого события бакет можно очистить и избежать неконсистентности базы. Если рассинхронизация случается слишком часто, то надо исследовать что не так.
В качестве «замены» memcached имеет смысл рассматривать все-таки Redis, Tarantool или Memcachedb.
Удивлён, что Redis здесь (топик и комментарии) упоминается всего 1 раз. Логичное решение. Для LRU в MongoDB имеет смысл использовать небольшую capped-коллекцию, но и там есть свои подводные камни. Троллейбус в заголовке идеально описывает данный подход.
Через capped-коллекции LRU сделать вроде как нельзя (если можно, напишите, пожалуйста, как).
Да, мой пробел, по всей видимости. Продумал ещё раз — никак что-то не получается. Даже если делать апдейт на каждое использование с гарантированным move — всё равно не получится, так как для самой монги данные окажутся там же, а не перемешёнными.
Я использую кэширование в Redis. Через expire управляю временем жизни отдельного ключа, исходя из здравого смысла.
Expire и LRU — разные вещи. Одно через другое не реализуется.
Очистку по тэгам Redis поддерживает?
не совсем понятно, что значит очистка по тегам. заводи лист с id ключей для определенного тега и чисти их по запросу.
Можно пример для случая, когда одним тэгом помечен, например, 100000 ключей, и нужно очистить эти ключи?
redis-cli KEYS «prefix:*» | xargs redis-cli DEL
Там же у каждого ключа может быть несколько тэгов навешано. Это не префикс ключа, это именно тэги. На одном тэге — много ключей, на одном ключе — много тэгов.
ну в сете по определенному тегу храни id ключей, что то не особо понятно в чем проблема.

sadd tag:search 1 2 .. sadd tag:news 2 10 .. sinter 'tag:news' 'tag:search'
Ну да, так можно проэмулировать. Редис вообще хорош для кэширования, есть только несколько особенностей:
а) не настолько автоматизированная failover-функциональность, как в монге (хотя они допилят, наверное),
б) при предложенном способе работы с тэгами нужно как-то отслеживать момент, когда все ключи, помеченные тэгом, «протухли» (чтобы и сам тэг удалить, дабы он не занимал память),
в) хорошо бы очистку делать в фоновом режиме, асинхронно — иначе удаление большого числа ключей, помеченных одним тэгом, затянется.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории