Подтвердить что ты не робот

Шаблон проектирования для кэширования кэшированных данных

Легко обернуть дополнительное кэширование 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, а затем пакетный - получите соответствующий срез массива.

Я не уверен, как аккуратно спроектировать это, есть ли у кого-нибудь какие-либо предложения?

Еще лучше, если вы сами справитесь с этим. Как вы справляетесь с этим?

4b9b3361

Ответ 1

Если вы используете кеш, чтобы максимально использовать его, вы должны признать, что ваши данные будут всегда устаревать до некоторой степени и что некоторые части данных будут не синхронизированы друг с другом, Попытка сохранить все записи в актуальном состоянии, поддерживая одну копию, лучше всего оставить в реляционных базах данных, поэтому, если это то, что вам нужно, вам, вероятно, будет лучше с мощным 64-битным сервером БД с большим количеством ОЗУ поэтому он может выполнять собственное внутреннее кэширование.

Если вы можете принять устаревшие данные (которые вам понадобятся, если важна реальная масштабируемость), то один подход состоит в том, чтобы просто выбросить весь набор результатов в кеш; не беспокойтесь о дублировании. ОЗУ дешево. Если вы обнаружите, что ваш кеш заполняется, просто покупайте больше оперативной памяти и/или серверов кеша. Например, если у вас есть запрос, который представляет элементы 1-24 в наборе, отфильтрованном условиями X и Y, используйте ключ кеша, который содержит всю эту информацию, а затем, когда его попросят повторить этот же поиск, просто верните весь набор результатов из кэш. Вы либо получаете полный набор результатов из кеша одним ударом, либо переходите в базу данных.

Самое сложное - выяснить, сколько данных может быть устаревшим, и насколько он может быть устаревшим: (а) люди слишком много замечают или (б) нарушают бизнес-требования, такие как минимальные интервалы обновления.

Этот подход хорошо работает для приложений, ориентированных на чтение, особенно тех, которые имеют запрограммированные запросы и/или конечный набор критериев фильтрации для данных. Это также означает, что ваше приложение работает точно так же с кешем вкл. Или выкл., Только с 0% ударом, когда кеш выключен. Это подход, который мы принимаем во blinkBox почти во всех случаях.

Ответ 2

Читайте о Паттерн идентификационной карты. Это способ убедиться, что вы сохраняете только одну копию данной строки в своем прикладном пространстве. Сохраняете ли вы его в memcached или просто в простых объектах, это способ справиться с тем, что вы хотите. Я бы предположил, что Identity Map лучше всего использовать, когда вы обычно выбираете по одной строке за раз.

Когда вы извлекаете целые подмножества таблицы, вы должны обрабатывать каждую строку отдельно. У вас может часто возникать дилемма: лучше ли вы используете ваш кеш, потому что, если 99% ваших строк находятся в кеше, но требуется выборка из базы данных, вы все равно должны запускать SQL-запрос (по крайней мере один раз).

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

Ответ 3

Ну, я думаю, что с тобой тебе придется жить. Memcahced будет работать лучше всего, если вы на самом деле не делаете это в партиях. Например, это отлично подходит для таких вещей, как "где вещи для этого пользователя? Вот куча вещей для этого пользователя". Это не означает, что этот запрос не выполняет партии. Конечно, это будет - если некоторые вещи пользователя такие вещи, как его/ее сообщения.

Я предполагаю, что проблема, с которой вы столкнетесь, - это случаи, когда вы смешиваете запросы, которые должны сами получить элемент из БД, а некоторые, которые получают кучу того же типа предыдущих элементов.

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

По-моему, это всегда сводится к тому, "какие запросы я действительно действительно хочет кэшировать?"

EDIT:

То, как я буду это делать:

  • Запрос по одному элементу - если в memcached используется тот, в противном случае извлечение из базы данных и обновление memcached.
  • Пакетный запрос - не беспокойтесь о том, какие элементы находятся в memcached, просто получите все и обновите memcached.

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

Однако, в конечном счете, ваш кеш будет содержать много элементов, если вы будете использовать пакетные запросы много. Поэтому вам придется ударить по балансу, чтобы определить, в какой момент вы все еще хотите выполнять поиск базы данных. Хорошо, если пакетный запрос раньше в жизненном цикле ваших приложений, тогда все будет кэшировано ранее. После первого пакетного запроса вы можете сказать себе, что вам больше не нужно извлекать из БД, если данные в кеше не будут аннулированы обновлениями или удалениями.

Ответ 4

Здесь я понимаю, как это делает NHibernate (и, следовательно, Hibernate). Он имеет 4 кэша:

  • row cache: кэширует строки базы данных. Кэш-кеш - это имя_билета # id, другие записи - значения строк.
  • кеш запросов: это кэширует результаты, возвращаемые для конкретного запроса. Кэш-кеш - это запрос с параметрами, данные - это список ключей строки TableName # id, которые были возвращены в качестве результатов запроса.
  • collection cache: кэширует дочерние объекты любого родителя (который NHibernate допускает ленивую загрузку). Поэтому, если вы подключитесь к myCompany.Employees, коллекцию сотрудников будут кэшироваться в кеше коллекций. Кэш-кеш - CollectionName # entityId, данные - это список ключей строки таблицы имен # id для дочерних строк.
  • кеш обновления таблицы: список каждой таблицы и время последнего обновления. Если таблица была обновлена ​​после кэширования данных, данные считаются устаревшими.

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