Кэширование результатов с разбивкой на страницы, очистка при обновлении – как решить?

StackOverflow https://stackoverflow.com/questions/109480

Вопрос

Я создал форум, и мы внедряем решение для кэширования APC и Memcache, чтобы сэкономить часть работы базы данных.

Я начал реализовывать уровень кэша с помощью таких ключей, как «Categories::getAll», и если бы у меня были данные, специфичные для пользователя, я бы добавлял к ключам такие вещи, как идентификатор пользователя, чтобы вы получили "User::getFavoriteThreads|1471".Когда пользователь добавлял новую избранную тему, я удалял ключ кэша, и запись создавалась заново.

Однако и здесь возникает проблема:

Я хотел кэшировать темы на форуме.Достаточно просто: «Forum::getThreads|$iForumId».Но...При нумерации страниц мне пришлось бы разделить это на несколько записей кэша, например

"Forum::getThreads|$iForumId|$iLimit|$iOffset".

И это нормально, пока кто-нибудь не создаст новую тему на форуме.Теперь мне придется удалить все ключи под "Forum::getThreads|$iForumId", независимо от предела и смещения.

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

Спасибо.

Это было полезно?

Решение

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

Если вы обнаружите, что 80% просмотров вашего форума просматривают первую страницу темы, вы можете кэшировать только эту страницу.Это означало бы, что и чтение, и запись в кэше реализовать гораздо проще.

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

Другие советы

Просто обновление:Я решил, что точка зрения Джоша на использование данных была очень хорошей.Люди вряд ли будут продолжать просматривать 50-ю страницу форума.

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

Таким образом, я могу использовать один ключ кэша для каждого форума, и для очистки/обновления кэша требуется совсем немного усилий :-)

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

Спасибо!

Мне удалось решить эту проблему, расширив memcache class с пользовательским классом (скажем, ExtendedMemcache), который имеет защищенное свойство, которое будет содержать хеш-таблицу группирования значений ключей.

А ExtendedMemcache->set метод принимает 3 аргумента ($strGroup,$strKey, $strValue) Когда вы звоните, он сохранит отношения между $strGroup, и $strKey, в защищенной собственности, а затем продолжайте сохранять $strKey к $strValue отношения в memcache.

Затем вы можете добавить новый метод в ExtendedMemcache класс под названием «deleteGroup», который при передаче строки находит ключи, связанные с этой группой, и очищает каждый ключ по очереди.

Это было бы примерно так:http://pastebin.com/f566e913bЯ надеюсь, что все это имеет смысл и сработает для вас.

ПС.Я полагаю, что если бы вы хотели использовать статические вызовы, защищенное свойство можно было бы сохранить в memcache сам под своим ключом.Просто мысль.

По сути, вы пытаетесь кэшировать представление, что всегда будет сложно.Вместо этого вам следует попытаться кэшировать только данные, поскольку данные редко меняются.Не кэшируйте форум, кэшируйте строки тем.Тогда ваш вызов БД должен просто вернуть список идентификаторов, которые уже есть в вашем кеше.Вызов базы данных будет быстро выполняться в любой таблице MyISAM, и тогда вам не придется выполнять большое соединение, которое съедает память базы данных.

Одно из возможных решений — не разбивать кэш тем на форуме, а поместить информацию о темах в Forum::getThreads|$iForumId.Затем в вашем PHP-коде извлеките только те, которые вам нужны для этой страницы, например.

$page = 2;
$threads_per_page = 25;
$start_thread = $page * $threads_per_page;

// Pull threads from cache (assuming $cache class for memcache interface..)
$threads = $cache->get("Forum::getThreads|$iForumId");

// Only take the ones we need
for($i=$start_thread; $i<=$start_thread+$threads_per_page; $i++)
{
    // Thread display logic here...
    showThread($threads[$i]);
}

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

флунгабунга:Ваше решение очень близко к тому, что я ищу.Единственное, что удерживает меня от этого, — необходимость сохранять отношения в кэше памяти после каждого запроса и загружать их обратно.

Я не уверен, насколько это будет означать снижение производительности, но это кажется немного неэффективным.Я проведу несколько тестов и посмотрю, что из этого получится.Спасибо за структурированное предложение (и немного кода, который можно показать, спасибо!).

Будьте очень осторожны при проведении такого рода оптимизации, не имея жестких фактов для сравнения.

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

В ответ флунгабунге:

Другой способ реализовать группировку — поместить имя группы и порядковый номер в сами клавиши и увеличить порядковый номер, чтобы «очистить» группу.Текущий действительный порядковый номер для каждой группы сохраняется в ее собственном ключе.

например

get seqno_mygroup
23

get mygroup23_mykey
<mykeydata...>
get mygroup23_mykey2
<mykey2data...>

Затем, чтобы «удалить» группу просто:

incr seqno_mygroup

Вуаля:

get seqno_mygroup
24

get mygroup24_mykey
...empty

и т. д..

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top