Недавно я реализовал memcache на моем сайте, который был под тяжелой загрузкой mysql (mysql был так же оптимизирован, как я мог это сделать). Он решил все мои проблемы с загрузкой, и сайт работает красиво.
Проблема, с которой сейчас сталкивается Im, – это устаревшие кешированные значения. У меня есть время истечения времени в 1 час на большинстве страниц, и я также удаляю ключ, когда значение в блоках DB, но Im с трудом отслеживает и эффективно очищает все ключи.
На некоторых страницах это тривиально. Я могу сделать ключ item_id (например, item_4653), и когда данные для него обновляются или элемент удаляется, ключ очищается.
Но на большинстве страниц я беру скрипт filename + querystring, md5 и использую его как ключ в memcache. Это особенно полезно для сложных URL-адресов (которые очень распространены).
Например, у меня загружена следующая страница.
index.php? search_keywords = good & search_section = 1 & sort = release & page = 2
Он будет содержать список элементов, которые будут извлекаться из memcache. Затем другой пользователь отправляет элемент, который имеет « хороший » в своем названии, и он находится в диапазоне значений, где он появится на стр. 2, за исключением того, что он не появится там, пока не будет обновлен кеш. Что еще более усложняет, так это то, что вновь добавленный элемент также появится на index.php? Sort = newest, а также index.php? Category = some_category? Page = 1 и т. Д. Каждый из них будет иметь уникальную key (md5 имени скрипта + строка запроса).
Таким образом, новый добавленный элемент может отображаться на десятках страниц, если они были извлечены из живой БД, но он не будет виден ни на одном из них, пока не будет обновлен устаревший кеш. Единственный вариант – дождаться истечения срока действия элемента.
Эта проблема становится еще более выраженной на моем форуме (пользовательская кодировка), где значения HAVE должны быть обновлены по требованию для всех возможных кеш-комбинаций страниц. Допустим, у меня 4 страницы, и я замечаю 3 спам-сообщения на странице 2. После их удаления страница 2 перестраивается, но затем она также должна перестраивать страницы 3 и 4, в противном случае будут дублироваться сообщения на новой странице восстановления 2 , и старая страница 3. Это просто пример для ….. Есть десятки этих сценариев.
Есть идеи?
Поскольку вы кэшируете целые страницы в memcached, ваши страницы не могут передавать кэшированные данные из базы данных друг с другом. Скажем, у меня есть page1.php и page2.php, а страницы1 и page2 – как ключи в memcached. Обе страницы отображают элементы . Я добавляю новый элемент. Теперь я должен истечь страницы 1 и стр . 2.
Вместо этого у меня мог бы быть ключ элементов в memcached, который page1.php и page2.php используют для отображения элементов. Когда я добавляю новый элемент, я теряю ключ элементов (или, лучше, обновляю его значение), и оба page1.php и page2.php обновлены.
Если вы все еще хотите кэшировать всю страницу, вы можете добавить информацию к своим ключам, которые будут меняться при изменении кэшированных данных (это не имеет смысла, если данные слишком часто меняются). Например:
"page1:[timestamp of newest item]"
Таким образом, вы можете найти временную метку новейшего элемента, недорогой запрос и создать с ним кеш-ключ. Когда новый элемент будет добавлен, ключ кеша изменится, автоматически истечет. Этот метод означает, что вам все равно нужно ударить по базе данных, чтобы узнать, что такое временная метка новейшего элемента, каждый раз.
Вы можете использовать более простую схему именования для своих ключей 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; ?>
Это небольшое гетто, но, возможно, это – и другие предложения – помогут.
Memcached :: set имеет параметр expire. Возможно, вы можете оставить это значение по умолчанию в течение часа, но для страниц, возвращающих результаты поиска, или на вашем форуме, вы можете установить это на более короткий период времени.
Пара простых вещей, которые вы можете сделать:
Во-первых, если вы действительно хотите использовать строку запроса в качестве ключа кэша, сделайте ее более детерминированной и предсказуемой. Я бы сделал это, отсортировав строку запроса, например ?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]
Четвертая идея состоит в том, чтобы изменить ваш сервер кэширования и использовать что-то построенное для обработки этой ситуации. Я не знаю, какие еще кэш-серверы там, поэтому вам придется осмотреться.
Наконец, чтобы дополнить все это, вы можете попробовать и умнее об истечении времени на вхождениях в кеш. например, использовать среднее время между обновлениями или количество запросов в секунду для ключевого слова и т. д.
Что вы можете сделать, чтобы убедиться, что ваш кеш всегда обновляется, не делая много изменений в вашем коде, это работа с «кешем версии». Это увеличивает количество запросов memcache, которые вы сделаете, но это может быть решением для вас.
Еще одна хорошая вещь об этом решении заключается в том, что вы можете установить время истечения срока действия, чтобы никогда не истекать.
Идея состоит в том, чтобы в основном иметь номер версии, хранящийся в memcache, в вашем случае определенное ключевое слово (за ключ, а не комбинацию). Как это использовать?
Когда кто-то отправляет новый элемент:
if(!Memcache:increment("version_" + keyword)) {Memcache:set("version_" + keyword);}
Когда кто-то выполняет запрос:
Это гарантирует, что как только ключевое слово будет иметь новые результаты (или меньше при удалении), версия столкнется и, как таковая, все связанные запросы memcache.
Кэш всегда обновляется, и запросы могут оставаться дольше 1 часа в кеше.
недействительность кэша – большая проблема
«В информатике есть только две серьезные проблемы: недействительность кеша и именование вещей».
Я дам вам несколько идей, которые приведут вас к полному решению, поскольку для всех вариантов использования нет генрального решения ..