Легко обернуть дополнительное кэширование memcached вокруг ваших существующих запросов к базе данных. Например:
Старый (только DB):
function getX x = get from db return x end
Новый (DB с memcache):
function getX x = get from memcache if found return x endif x = get from db set x in memcache return x end
Дело в том, что это не всегда так, как вы хотите кэшировать. Например, выполните следующие два запроса:
-- get all items (recordset) SELECT * FROM items; -- get one item (record) SELECT * FROM items WHERE pkid = 42;
Если бы я использовал вышеупомянутый псевдокод для обработки кэширования, я бы дважды сохранил все поля элемента 42. Однажды в большой набор записей и один раз сам по себе. В то время как я предпочел бы сделать что-то вроде этого:
SELECT pkid FROM items;
и кешировать этот индекс PK. Затем кешируйте каждую запись отдельно.
Таким образом, в целом стратегия доступа к данным, которая лучше всего подходит для БД, не соответствует стратегии memcache. Поскольку я хочу, чтобы уровень memcache был дополнительным (т.е. если memcache не работает, сайт все еще работает), я как бы хочу иметь лучшее из обоих миров, но для этого я уверен, что мне нужно будет поддерживать много запросов в 2 разных формах (1. выборка индекса, затем записи и 2. выборка набора записей в одном запросе). Это сложнее с разбиением на страницы. С помощью БД вы будете делать запросы LIMIT / OFFSET SQL, но с memcache вы просто получите индекс PK, а затем пакетный – получите соответствующий срез массива.
Я не уверен, как аккуратно спроектировать это, есть ли у кого-нибудь предложения?
Еще лучше, если вы сами справитесь с этим. Как вы справляетесь с этим?
Если вы используете кеш, чтобы максимально использовать его, вы должны признать, что ваши данные будут всегда устаревать до некоторой степени и что некоторые части данных будут не синхронизированы друг с другом. Попытка сохранить все записи в актуальном состоянии, поддерживая одну копию, лучше всего оставить в реляционных базах данных, поэтому, если это то, что вам нужно, вам, вероятно, будет лучше с мощным 64-битным сервером БД с большим количеством ОЗУ поэтому он может выполнять собственное внутреннее кэширование.
Если вы можете принять устаревшие данные (которые вам понадобятся, если важна реальная масштабируемость), то один подход состоит в том, чтобы просто выбросить весь набор результатов в кеш; не беспокойтесь о дублировании. ОЗУ дешево. Если вы обнаружите, что ваш кеш заполняется, просто покупайте больше оперативной памяти и / или серверов кеша. Например, если у вас есть запрос, который представляет элементы 1-24 в наборе, отфильтрованном условиями X и Y, используйте ключ кеша, который содержит всю эту информацию, а затем, когда его попросят повторить этот же поиск, просто верните весь набор результатов из кэш. Вы либо получаете полный набор результатов из кеша за один удар, либо переходите в базу данных.
Самое сложное – выяснить, сколько данных может быть устаревшим, и насколько он может быть устаревшим: (а) люди слишком много замечают или (б) нарушают бизнес-требования, такие как минимальные интервалы обновления.
Этот подход хорошо работает для приложений, ориентированных на чтение, особенно тех, которые имеют запрограммированные запросы и / или конечный набор критериев фильтрации для данных. Это также означает, что ваше приложение работает точно так же с кешем вкл. Или выкл., Только с 0% ударом, когда кеш выключен. Это подход, который мы принимаем в blinkBox практически во всех случаях.
Читайте о шаблоне карты идентичности . Это способ убедиться, что вы сохраняете только одну копию данной строки в своем прикладном пространстве. Сохраняете ли вы его в memcached или просто в простых объектах, это способ справиться с тем, что вы хотите. Я бы предположил, что Identity Map лучше всего использовать, когда вы обычно выбираете по одной строке за раз.
Когда вы извлекаете целые подмножества таблицы, вы должны обрабатывать каждую строку отдельно. У вас может часто возникать дилемма: лучше ли вы используете ваш кеш, потому что, если 99% ваших строк находятся в кеше, но требуется выборка из базы данных, вы все равно должны запускать SQL-запрос (по крайней мере один раз).
Вы можете преобразовать SQL-запрос для извлечения только строк, которые не находятся в кеше, но нетривиально выполнять это преобразование автоматически, не делая запрос SQL более дорогостоящим.
Ну, я думаю, это то, с чем вам придется жить. Memcahced будет работать лучше всего, если вы на самом деле не делаете это в партиях. Например, это здорово для таких вещей, как «где вещи для этого пользователя? Вот куча вещей для этого пользователя». Это не означает, что этот запрос не выполняет партии. Конечно, это будет – если некоторые вещи пользователя будут такими же, как и его / ее сообщения.
Я предполагаю, что проблема, с которой вы столкнетесь, – это случаи, когда вы смешиваете запросы, которые должны получить элемент из БД самостоятельно, а некоторые, которые получают кучу того же типа предыдущих элементов.
Ситуация всегда меняется. Если вы действительно хотите получить волосатые с вашей реализацией, вы можете изменить свои пакетные запросы, чтобы не включать элементы, уже присутствующие в memcached.Very очень уродливые …
По-моему, это всегда сводится к тому, «какие запросы я действительно хочу кэшировать?»
РЕДАКТИРОВАТЬ:
То, как я это сделаю, это:
Это, конечно же, предполагает, что пакетные запросы уже уходят в ад намного больше времени для завершения, и поэтому я уже трачу столько времени, что могу жить с внешним поиском уже кэшированных элементов.
Однако, в конечном счете, ваш кеш будет содержать много элементов, если вы будете использовать пакетные запросы много. Поэтому вам придется ударить по балансу, чтобы определить, в какой момент вы все еще хотите выполнять поиск базы данных. Хорошо, если пакетный запрос раньше в жизненном цикле ваших приложений, тогда все будет кэшировано ранее. После первого пакетного запроса вы можете сказать себе, что вам больше не нужно извлекать из БД, если данные в кеше не будут аннулированы обновлениями или удалениями.
Вот мое понимание того, как это делает NHibernate (и, следовательно, Hibernate). Он имеет 4 кэша:
Это довольно гибкое решение, очень эффективное по размеру и гарантирует, что данные не будут устаревшими. Недостатком является то, что для одного запроса может потребоваться несколько обращений к кешу, что может быть проблемой, если кэш-сервер находится в сети.