Начало серии постов [здесь](http://www.smira.ru/2008/06/22/web-cache-memcached-1/). Продолжаем разбираться с этим вопросом. Проблема №3. Одного мемкеша мало ------------------------------------- Если сайт большой, данных много, кешей тоже много, в один memcached физически не умещаемся. Пожалуйста: создаем кластер мемкешовых серверов, хешируем ключи для определения номера сервера, на котором ключ должен храниться. Всё вроде бы хорошо до тех пор, пока эти сервера не начинают падать или с ними не начинает теряться сетевая connectivity (что легко происходит, т.к. архитекутра кластерная, место на площадках ограничено, сервера физически разнесены, каналы забиты, ненадежны и т.п.) Самый простой алгоритм хеширования вида: $memcache_server_id = crc32($key) % count($memcache_servers); начинает "веселиться", когда обнаруживает, что сервер упал. Когда обнаруживается проблема сетевого доступа к серверу, [драйвер memcachа](http://pecl.php.net/package/memcache) убирает его из списка $memcache_servers. При этом count($memcache_servers) изменяется, какая-то часть ключей "сдвигается" на другие сервера, при этом "пропадает" куча кешей (хорошо, если кешей, которые не так жалко потерять, а если речь идёт о сессиях пользователей?). Необходимо отметить, что та же самая проблема будет, когда мы вводим в строй или выводим из пула сервера мемкеша. Интересная математическая задача: если раньше было N серверов мемкеша и схема распределения ключей по серверам взятием остатка от деления на N (как написано выше), а после каких-то событий стало K серверов, то какой процент ключей останется на том же сервере, на котором они были до изменения количества серверов? Когда куча ключей теряется, резко начинают перестраиваться кеши, вырастает нагрузка на БД, и т.д., и т.п., то есть падение одного сервера мемкеша заваливает весь "отказоустойчивый" кластер. Что делать? Есть [другие алгоритмы хеширования]( http://www.lastfm.ru/user/RJ/journal/2007/04/10/rz_libketama_-_a_consistent_hashing_algo_for_memcache_clients) ключей, устойчивые к удалению/добавлению серверов в пул. Для [PHP-шного модуля Memcache](http://pecl.php.net/package/memcache) это: ini_set('memcache.hash_strategy', 'consistent'); Проблема №4. Одновременное перестроение кеша несколькими мордами ------------------------------------------------------------------------- Если мы выставляем ключу (кешу) некоторое время жизни, рано или поздно, memcache скажет, что такого ключа на сервере нет. А если это "популярный" кеш, например, использующийся на главной странице, то такую ситуацию могут "обнаружить" одновременно несколько морд. И они все попытаются построить этот кеш, то есть они все одновременно отправят запрос в БД, то есть они отправят *много* запросов в БД, причём за короткий промежуток времени. Конечно, как только первый запрос завершится, морда запишет новое значение ключа и больше новых запросов за этой выборкой не будет. Но мы уже подвергли БД высокой нагрузке, хотя хотели этого избежать при помощи кеширования. Напрашивается решение: пока одна морда строит кеш, все остальные должны её "подождать". Как это реализовать? Например, с помощью блокировок в том же мемкеше. Перед тем, как начать строить кеш для ключа mykey, проверяем, не выставлен ли уже ключ mykey_lock, если он выставлен, засыпаем и ждём некоторое время, что ключ исчезнет, если он исчез - пробуем снова прочитать mykey, если он появился (другая морда его построила), то берем готовое значение. Не появился за какой-то разумный промежуток времени - начинаем строить сами (значит, никто другой не смог построить значение этого кеша). Перед тем, как сами начинаем строить кеш (например, если блокировки не было обнаружено), обязательно создаем блокировку: выставляем ключ блокировки mykey_lock, например, на 10 секунд, тем самым не подпуская другие морды к построению такой же выборки. Как выборку построили - записываем её в мемкеш, ключ блокировки удаляем. Такая схема позволяет избежать части коллизий, который возникают при одновременном построении кешей.