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

Каким образом можно управлять ключами (в memcache) для предотвращения устаревших кешированных значений?

Недавно я реализовал memcache на моем сайте, который был под тяжелой загрузкой mysql (mysql был так же оптимизирован, как я мог это сделать). Он решил все мои проблемы с загрузкой, и сайт работает красиво.

Проблема, с которой сейчас сталкивается Im, - это устаревшие кешированные значения. У меня есть время истечения времени в 1 час на большинстве страниц, и я также удаляю ключ, когда значение в блоках DB, но Im с трудом отслеживает и эффективно очищает все ключи.

На некоторых страницах его тривиально. Я могу сделать ключ item_id (например, item_4653), а когда данные для него обновляются или элемент удален, ключ очищается.

Но на большинстве страниц я беру script имя_файла + querystring, md5 и использую его как ключ в memcache. Это особенно полезно для сложных URL-адресов (которые очень распространены).

Например, у меня загружена следующая страница.

?

index.php search_keywords = хорошо & search_section = 1 & = выпуск рода & страница = 2

Он будет содержать список элементов, которые будут извлечены из memcache. Затем другой пользователь отправляет элемент, который имеет "хороший" в своем названии, и он находится в диапазоне значений, где он появится на стр. 2, за исключением того, что он не появится там, пока не будет обновлен кеш. Что еще более усложняет, так это то, что вновь добавленный элемент также появится на index.php? Sort = newest, а также index.php? Category = some_category? Page = 1 и т.д. Каждый из них будет иметь уникальную key (md5 из script name + строка запроса).

Таким образом, недавно добавленный элемент может появиться на десятках страниц, если они были извлечены из Live DB, но он не будет виден ни на одном из них, пока не будет обновлен устаревший кеш. Единственный вариант - дождаться истечения срока действия элемента.

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

Любые идеи?

4b9b3361

Ответ 1

Поскольку вы кэшируете целые страницы в memcached, ваши страницы не могут передавать кэшированные данные из базы данных друг с другом. Скажем, у меня есть page1.php и page2.php, page1 и page2 как ключи в memcached. На обеих страницах отображаются элементы. Я добавляю новый элемент. Теперь я должен истечь page1 и page2.

Вместо этого я мог бы использовать элементы в memcached, чтобы page1.php и page2.php использовали для отображения элементов. Когда я добавляю новый элемент, я теряю ключ элементов (или лучше, обновляю его), а оба страницы1.php и page2.php являются актуальными.

Если вы все еще хотите кэшировать всю страницу, вы можете добавить информацию к своим клавишам, которые будут меняться при изменении данных в кешках (это не имеет смысла, если данные слишком часто меняются). Например:

"page1:[timestamp of newest item]"

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

Ответ 2

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

Вы также можете рассмотреть более короткое время кеша, например 20 минут?

Также - сколько элементов на странице вы извлекаете для каждой из этих страниц результатов поиска? Если у вас есть разбитый на страницы поиск - получение 50 элементов с сервера не должно быть слишком интенсивным.

Возможно, вы настроили сервер mysql, но вы настроили запросы (улучшая их, изучая вывод EXPLAIN) или структуры таблиц (добавив полезные индексы)?

Мне также интересно, насколько интенсивны запросы на этих страницах. Вы присоединяетесь к нескольким столам? Вы можете воспользоваться упрощенным запросом - или несколькими запросами (см. Ниже).

В качестве альтернативы - для каждой строки результата вы запускаете другой запрос - или несколько? Вы можете воспользоваться немного более сложным поисковым запросом, который позволяет вам выполнять вложенные запросы. Или вы укушены библиотекой ORM, которая делает то же самое, запускает поиск, а затем запросы для подпунктов на каждой итерации?

"Несколько простых запросов" - скажем, например, - если у вас есть элемент и хотите узнать его в наборе результатов...

Вместо этого:

SELECT i.id, i.name,
c.category FROM items AS i
INNER JOIN categories AS c
ON i.category_id = c.id;

Это простой пример - но, скажем, были категории и несколько других JOINs.

Вы можете пойти по этому маршруту:

// run this query
SELECT id, category FROM categories - and put that into a keyed array.

// then in PHP create an array keyed by the id
$categories = array();

while ( false !== ( $row = mysql_fetch_assoc ( $result ) ) ) 
{
  $categories[ $row['id'] ] = $row['category'];
}

// and so on
$types = array(); // ...
// etc.

Затем выполните поиск, но без всех JOINS, только из таблицы элементов с вашими предложениями, а на выходе...

<?php foreach($items as $item): ?>
  <h4><?php echo $item['name']; ?></h4>
  <p>Category:  <?php echo $categories[ $item['category_id'] ]; ?></p>
  <p>Type:  <?php echo $types[ $item['type_id'] ]; ?></p>
  <!-- and so on -->
<?php endforeach; ?>

Это небольшое гетто, но, возможно, это - и другие предложения - помогут.

Ответ 3

Memcached:: set имеет параметр expire. Возможно, вы можете оставить это значение по умолчанию в течение часа, но для страниц, возвращающих результаты поиска, или на вашем форуме, вы можете установить это на более короткий период времени.

Ответ 4

Простые вещи, которые вы можете сделать:

Во-первых, если вы действительно хотите использовать строку запроса в качестве ключа кэш-памяти, сделайте ее более детерминированной и предсказуемой. Я бы сделал это, сортируя строку запроса, например: ?zed=7&alpha=1 преобразуется в ?alpha=1&zed=7. Также отключите переменные, которые не относятся к ключу кеширования.

Чтобы справиться с проблемой параметра? page, а элементы не отображаются, потому что кеш не обновился, у меня есть пара идей:

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

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

cache.put("keyword,page=3", array(num_pages=7, value=...))

...later...
update_entry()
num_pages, value = cache.get("keyword,page=3")
for i in num_pages:
  cache.flush("keyword,page="+i)

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

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

cache.put("keyword", array(0="bla", 1=foo", ...)
...later...
cache.get("keyword")[page_num]

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

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

Ответ 5

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

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

Идея состоит в том, чтобы в основном иметь номер версии, хранящийся в memcache, в вашем случае определенное ключевое слово (за ключевое слово, а не комбинацию). Как это использовать?

Когда кто-то отправляет новый элемент:

  • для каждого слова в заголовке, if(!Memcache:increment("version_" + keyword)) {Memcache:set("version_" + keyword);}

Когда кто-то выполняет запрос:

  • вещь md5, которую вы делаете, уже хорошо. Кроме того, вам нужно добавить версию каждого ключевого слова в строку поиска к ключу memcache.

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

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

Ответ 6

Недействительность кэша - большая проблема.

"В информатике есть только две проблемы: кеш недействительности и именования вещей".

Я дам вам несколько идей, которые приведут вас к полному решению, поскольку для всех вариантов использования нет генрального решения.

  • прочитайте про лаки esi и X-Article-id https://www.varnish-software.com/blog/advanced-cache-invalidation-strategies
  • использовать nginx ssi
  • отслеживать все ваши элементы в кеше, поэтому, если вы кэшируете 100 сообщений форума, сохраняйте каждый из идентификаторов сообщений в db, чтобы вы имели что-то вроде lastMessages, содержали сообщения: 1,2,550,123 и т.д. теперь, когда какой-либо из элементов обновляется найдите, где его магазин и очистите их один за другим (или перестройте их и сохраните снова).
  • это похоже на одно и то же решение, но вместо каждого элемента кэша известно, что его магазин, каждая модель должна знать, где находится его хранилище.