Кеширование постраничных результатов, чистка при обновлении - как решить? - PullRequest
5 голосов
/ 21 сентября 2008

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

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

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

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

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

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

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

Спасибо.

Ответы [ 8 ]

7 голосов
/ 22 сентября 2008

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

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

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

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

Спасибо!

5 голосов
/ 21 сентября 2008

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

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

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

5 голосов
/ 21 сентября 2008

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

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

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

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

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

2 голосов
/ 21 сентября 2008

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

1 голос
/ 13 января 2009

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

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

, например

get seqno_mygroup
23

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

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

incr seqno_mygroup

Вуаля:

get seqno_mygroup
24

get mygroup24_mykey
...empty

и т.д ..

1 голос
/ 21 сентября 2008

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

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

1 голос
/ 21 сентября 2008

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

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

1 голос
/ 21 сентября 2008

Одно из возможных решений - не разбивать кэш потоков на форуме, а поместить информацию о потоках в 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]);
}

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

...