2009-09-17 3 views
5

Я новичок в memcached. Является ли этот код уязвимым для состояния истечения срока действия кеша? Как бы вы его поправили?memcacheD Это нормально?

$memcache = new Memcache; 
$memcache->connect('127.0.0.1'); 
$arts = ($memcache===FALSE) ? FALSE : $memcache->get($qparams); 
if($arts===FALSE) { 
    $arts=fetchdb($q, $qparams); 
    $memcache->add($qparams, $arts, MEMCACHE_COMPRESSED, 60*60*24*3); 
} 
if($arts<>FALSE) { 
    // do stuff 
} else { 
    // empty dataset 
} 
  • $ qparams содержит параметры для запроса, поэтому я использую его в качестве ключа.
  • $ arts Получите массив со всеми полями, которые мне нужны для каждого элемента.

Предположим, что запрос X содержит 100 строк. Немного после того, как строка №50 изменена другим процессом (скажем, что розничная цена увеличивается).

  • Что делать с кешем?
  • Как я могу узнать в строке # 50 кэшируется?
  • Должен ли я аннулировать ВСЕ записи в кеше? (звучит, как перебор, для меня).

ответ

3

Является ли этот код уязвимым для состояния истечения срока действия кеша? Как бы вы его улучшили?

Да. Если два (или более) одновременных клиента пытаются извлечь один и тот же ключ из кеша и в конечном итоге вытащить его из базы данных. У вас будут всплески в базе данных, и в течение периодов времени база данных будет находиться под большой нагрузкой. Это называется кеш-штампом. Есть несколько способов справиться с этим:

  • Для новых предметов предварительно разогревайте кеш (в основном это означает, что вы предварительно загружаете объекты, которые вам нужны до того, как сайт начнет жить).
  • Для элементов, срок действия которых истекает, периодически создает время истечения, которое немного в будущем, чем фактическое время истечения срока действия (скажем, 5-10 минут). Затем, когда вы вытаскиваете объект из кеша, проверьте, закрыто ли время истечения срока действия, кеширование в будущем, чтобы никто другой клиент не обновлял кеш и не обновлялся из базы данных. Для того, чтобы работать без кеш-штампов, вам нужно либо реализовать блокировку ключа, либо использовать токены (требуется последняя клиентская библиотека для работы).

Для получения дополнительной информации проверьте memcached faq.

Предположим, что запрос X содержит 100 строк. Немного после того, как строка №50 изменена другим процессом (скажем, что розничная цена увеличивается).

У вас есть три типа данных в кэше:

  1. объектов
  2. Списки объектов
  3. Сформированные данные

То, что я обычно делаю, чтобы сохранить объекты, как отдельный и затем используйте «указатели» кэша в списках. В вашем случае у вас есть N объектов где-то в кеше (скажем, ключи 1,2..N), а затем у вас есть список объектов в массиве array(1,2,3,10,42...). Когда вы решите загрузить список с объектами, вы загружаете ключ списка из кеша, а затем загружаете фактические объекты из кеша (с помощью getMulti для уменьшения запросов). В этом случае, если какой-либо объект обновляется, вы обновляете его только в одном месте, и он автоматически обновляется повсюду (не говоря уже о том, что вы сохраняете огромное количество пространства с помощью этой техники).

Редактировать: Решил добавить немного больше информации о времени истечения опережения.

Вы установили свой объект с данными об истечении срока годности x и сохраните его в базе данных с датой истечения срока действия x+5minutes. Это те шаги, которые вы берете, когда вы загрузите объект из кэша:

  1. Проверьте, пришло время обновить (time() - x < 0)
  2. Если да, блокировка ключ, так что никто не может изменить его в то время как вы освежать пункт. Если вы не можете заблокировать ключ, то кто-то еще уже обновляет ключ, и он становится SEP (проблема кого-то другого). Поскольку memcached не имеет решения для замков, вам необходимо разработать собственный механизм. Обычно я делаю это, добавляя отдельный ключ с исходным значением ключа + ":lock" в конце. Вы должны установить этот ключ для истечения срока действия в кратчайшей возможной сумме (для memcached, которая равна 1 секунде).
  3. Если вы получили блокировку ключа, сначала сохраните объект с новым сроком действия (таким образом, вы уверены, что другие клиенты не попытаются заблокировать ключ), а затем займитесь своим бизнесом и обновите ключ от базы данных и снова сохранить новое значение с соответствующими выдержками времени ожидания (см. пункт 1).

Надеется, что это очищает все вверх :)

+0

За время истекает в тэ будущего. Я не понимаю. Предположим, что мое время «закрытия истекает» - 5 минут, и я ожидаю, что позиция № 5432 истечет в 17:00. В 16: 55hs четыре разных пользователя запрашивают элемент # 5432. Все они попадут в db ... Я изучу блокирующие и кассовые жетоны, но я не понимаю, как это сделать x mins раньше. –

+0

Если вы сделаете это за 5 минут до истечения срока действия объекта, остальные клиенты могут использовать устаревший объект, пока вы загружаете новый из базы данных. Предоставьте его, вам все равно придется использовать блокирующие и кассовые жетоны, чтобы не допустить, чтобы все сразу обновлялись. Вы можете сделать вероятностную догадку и обновление с большей вероятностью, чем ближе к истечению времени, которое вы получите. В любом случае вы хотите, чтобы один клиент обращался к базе данных. Вы могли бы даже создать cron, который был бы для вас в фоновом режиме, но это потребовало бы очень специфического вида объекта :) –

+0

Я отредактировал ответ с более подробной информацией. Надеюсь, теперь это яснее :) –

1

Вы должны аннулировать любой кешированный объект, содержащий измененный элемент. Либо вам нужно изменить механизм кэширования для хранения элементов на более узком уровне, либо аннулировать всю запись.

Это в основном то же самое, что сказать, что вы кэшируете всю БД в одиночном кэше. Вы либо истекаете, либо нет.

Смежные вопросы