Dklab_Cache: тэги в memcached, namespaces, статистика

    Memcached community предприняло немало попыток написать «родные» патчи для кода memcached, добавляющие в него поддержку тэгов. Наиболее известный из таких патчей — проект memcached-tag. К сожалению, memcached-tag все еще очень далек от стабильной версии: нетрудно написать скрипт, приводящий к зависанию пропатченного memcached-сервера. Похоже, на момент написания данной статьи не существует ни одного надежного решения проблемы тэгирования на уровне самого memcached-сервера.

    Библиотека Dklab_Cache


    Dklab_Cache — это (в основном) библиотека поддержки тэгирования ключей для memcached, использующая интерфейсы Zend Framework. Сама библиотека написана на чистом PHP. Вот полный список возможностей библиотеки:
    • Backend_TagEmuWrapper: тэги для memcached и любых других backend-систем кэширования Zend Framework;
    • Backend_NamespaceWrapper: поддержка пространств имен для memcached и др.;
    • Backend_Profiler: подсчет статистики по использованию memcached и др. backend-ов;
    • Frontend_Slot, Frontent_Tag: каркас для высокоуровневого построения систем кэшиирования в сложных проектах.
    Собственно, для поддержки тэгов имеется класс TagEmuWrapper. Он представляет собой декоратор («обертку») для backend-классов кэширования Zend Framework. Другими словами, вы можете с его помощью «прозрачно» добавить поддержку тэгов в любую подсистему кэширования Zend Framework. Мы будем рассматривать backend для работы с memcached: Zend_Cache_Backend_Memcached, но, если в вашем проекте используется какой-то другой backend-класс, вы можете подключить тэгирование и к нему без каких-либо особенностей.

    TagEmuWrapper реализует стандартный backend-интерфейс Zend_Cache_Backend_Interface, поэтому с точки зрения вызывающей системы он сам является кэш-backend'ом. Вообще, Zend Framework хорош тем, что на уровне интерфейса он поддерживает тэги с самого начала! Например, в методе save() уже имеется параметр, позволяющий снабдить ключ тэгами. Однако ни один из backend-ов в составе Zend Framework тэги не поддерживает: попытка добавить тэг к некоторому ключу вызывает исключение (в частности, для Zend_Cache_Backend_Memcached).

    Технические подробности, документацию, а также примеры использования можно посмотреть тут: dklab.ru/lib/Dklab_Cache

    Что такое тэги?


    Работа с типичной кэширующей системой (в том числе с memcached) заключается в выполнении трех основных операций:
    • save($data, $id, $lifetime): сохранить данные $data в ячейке кэша с ключом $id. Можно указать «время жизни» ключа $lifetime; спустя это время данные в кэше «протухнут» и удалятся.
    • load($id): загрузить данные из ячейки с ключом $id. Если данные недоступны, возвращается false.
    • remove($id): очистить ячейку кэша с ключом $id.
    Предположим, мы хотим эакэшировать долгий SQL-запрос для быстрого отображения части страницы. В этом случае мы проверяем: имеется ли запись в ячейке кэша, соответствующей этому запросу. Если ячейка пуста, данные загружаются из СУБД и сохраняются в кэш для возможных будущих извлечений.

    if (false === ($data = $cache->load("key"))) {
        $data = executeHeavyQuery();
        $cache->save($data, "key");
    }
    display($data);
    

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

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

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

    Давайте модифицируем предыдущий пример с использованием тэгов. Предположим, что SQL-запрос существенно зависит от ID текущего пользователя $loggerUserId, поэтому каждому такому пользователю выделяется отдельная ячейка с именем «key_{$loggedUserId}». Однако данные зависят и от ID другого человека $ownerUserId, чей профиль просматривает текущий пользователь. В этом случае мы можем пометить ячейку тэгом, связанным с пользователем $ownerUserId:

    if (false === ($data = $cache->load("key_{$loggedUserId}"))) {
        $data = loadProfileFor($loggedUserId, $ownerUserId);
        $cache->save($data, "key_{$loggedUserId}", array("profile_{$ownerUserId}");
    }
    display($data);
    

    Теперь, если меняются данные в профиле пользователя $ownerUserId (например, человек поменял свое имя), нам достаточно дать команду на очистку тэга, связанного с этим профилем:

    $cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("profile_{$ownerUserId}");
    

    Обратите внимание, что кэш-ячейки всех остальных пользователей при этом не пострадают: очистятся только те, которые зависели от $ownerUserId.

    Собственно, фраза «пометить ячейку C тэгом T» означает то же, что утверждение «ячейка C зависит от данных, описанных как T». Тэги — это зависимости, ничего более.

    Небольшое отступление: о зависимостях в коде


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

    if (false === ($data = $cache->load("profile_{$userId}"))) {
        $data = loadProfileOf($userId);
        $cache->save($data, "profile_{$userId}", array(), 3600 * 24); // кэширование на 24 часа
    }
    display($data);
    

    и потом еще в совершенно другой части программы:

    $cache->remove("profile_{$userId}");
    

    Как видите, фразу «profile_{$userId}» приходится повторять аж три раза. И если в первом случае мы можем убрать повтор ценой введения новой переменной:

    $cacheKey = "profile_{$userId}";
    $cacheTime = Config::getInstance()->cacheTime->profile;
    if (false === ($data = $cache->load($cacheKey))) {
        $data = loadProfileFor($userId);
        $cache->save($data, $cacheKey, array(), $cacheTime);
    }
    display($data);
    

    … то во второй части программы нам «в лоб» не избавиться от знания, как именно строится ключ кэширования, и от каких параметров он зависит.

    Важное замечание
    Строчка «profile_{$userId}» — это именно знание, и не следует недооценивать вред о распространении этого знания по излишне большому числу независимых мест. В нашем примере знание очень просто, но на практике ключ кэша может зависеть от десятков различных параметров, часть из которых нужно даже загружать из БД по первому требованию.

    Ситуация в действительности даже хуже, чем может показаться.
    • Кто может дать гарантию, что в переменной $userId хранится именно ID текущего пользователя, а не какой-нибудь мусор? А что, если кто-то попробует подставить туда неверные данные? Очевидно, что ключ кэша в действительности зависит не от ID пользователя, а от самого этого пользователя. Попытка использовать для генерации ключа что-либо, кроме объекта-пользователя, заведомо ошибочна, но в программе это ограничение явно не выражено.
    • Время кэширования мы должны хранить не прямо в коде, а где-то в конфигурации системы (см. предыдущий пример), чтобы его можно было менять, не трогая код. Это — еще одна зависимость от роли кэш-ячейки и строчки «profile».

    Как это работает в Dklab_Cache


    Вместо долгих разъяснений я сразу приведу пример использования Slot-класса, построенного в соответствии с идеологией Dklab_Cache_Frontend.

    $slot = new Cache_Slot_UserProfile($user);
    if (false === ($data = $slot->load())) {
        $data = $user->loadProfile();
        $slot->save($data);
    }
    display($data);
    

    Для очистки кэша:

    $slot = new Cache_Slot_UserProfile($user);
    $slot->remove();
    

    Чем же это лучше?
    • Знание об алгоритме построения ключа кэша заключено в едином месте — в классе Cache_Slot_UserProfile.
    • Там же заключено знание о времени жизни кэша. В нашем случае мы задали его явно, однако никто не мешает брать время жизни из параметра конфигурации, имя которого совпадает с именем слот-класса.
    • Параметр $user конструктора класса Cache_Slot_UserProfile — типизированный. Это означает, что мы не сможем «подсунуть» слот-классу что-либо, кроме корректного объекта-польователя. Естественно, зависимость может быть от нескольких объектов; все это определяется параметрами конструктора.
    Вы должны написать столько собственных слот-классов, сколько видов кэш-хранилищ существует у вас в программе. Это дисциплинирует: заглянув в директорию Cache/Slot, вы сразу сможете увидеть, сколько именно различных кэшей используется в программе, а также — от чего они зависят.

    Ну а теперь, собственно, о тэгах


    Слоты, помимо прочего, поддерживают тэгирование. Вот пример использования тэгов для сквозного кэширования (естественно, можно применять и «несквозное»).

    $slot = new Cache_Slot_UserProfile($user);
    $slot->addTag(new Cache_Tag_User($loggedUser);
    $slot->addTag(new Cache_Tag_Language($currentLanguage);
    $data = $slot->thru($user)->loadProfile();
    display($data);
    

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

    $tag = new Cache_Tag_Language($currentLanguage);
    $tag->clean();
    

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

    Заключение


    В этой статье говорится сразу обо всем: и о тэгировании кэша, и о кэш-зависимостях в коде, и о методе абстракции от кэш-хранилища Slot и Tag, реализованном в библиотеке.

    Скачать исходники библиотеки и примеры можно здесь: dklab.ru/lib/Dklab_Cache
    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 57
    • 0
      Оправданны ли все эти навороты.

      Может проще и однозначно с большей производительностью задействовать механизм _by_key из libmemcached?
      • –2
        Сама библиотека написана на чистом PHP.

        А как же немножко уличной магии?
        • –2
          DmitryKoterov, извиняюсь за оффтоп. эта библиотека у вас уже с незапамятных времён, почему вы только сейчас разактивничались на хабре? кризис коснулся вас или предреклама нового проекта?
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              я глюк, не стоило обращать внимания, вопрос не к тебе.
            • 0
              Причины две:
              1) получить фидбэк от профессионального сообщества, представителей которого здесь больше, чем где-либо;
              2) узнать альтернативные (возможно, более эффективные) методы реализации того, что делают предложенные библиотеки.
            • +3
              мы у себя в проектах используем модифицированную версия memcache:
              code.google.com/p/memcached-tag/
              code.google.com/p/memcached-tags/
              • 0
                На тот момент, когда я ее проверял (несколько месяцев назад) она сегфолтилась, а иногда висла и начинала бесконечно отжирать память. Возможно, сейчас ее уже починили. Кстати, в дистрибутиве Dklab_Cache есть Dklab_Cache_Backend_MemcachedTag, который именно для memcached-tags предназначен (адаптер для Zend_Cache_Backend).
                • 0
                  memcached-tag и memcached-tags — разные вещи (см мой коммент ниже), я так понимаю вы тестировали только memcached-tag и поддержка в Dklab_Cache_Backend_MemcachedTag только для ...tag (надеюсь только пока?).
                  • 0
                    Memcached-tags — это, насколько я понимаю, форк от memcached-tag, совместимый по интерфейсу. Поэтому для нее Dklab_Cache_Backend_MemcachedTag подхоит (правда, Dklab_Cache_Backend_MemcachedTag не поддерживает tags_delete, но такую поддержку нетрудно реализовать, позволив методу clean() принимать список, каждый элемент которого может быть именем тэга, а может — массивом имен).
                    • 0
                      Посути это fork, только с заново переписанным core, но протокол — тот же.

                      > но такую поддержку нетрудно реализовать
                      Было бы здорово если бы библиотека поддерживала и tags_delete.
              • +1
                Никто статью не читал что-ли?
                Повтор 2х абзацей
                >>Обратите внимание, что кэш-ячейки всех остальных пользователей при этом не пострадают: очистятся только >>те, которые зависели от $ownerUserId.

                >>Собственно, фраза «пометить ячейку C тэгом T» означает то же, что утверждение «ячейка C зависит от >>данных, описанных как T». Тэги — это зависимости, ничего более.
                • +2
                  Читали.
                  Именно поэтому вопрос и возник.

                  В libmemcached понятие _by_key по сути является является аналогом тэга в понимании DmitryKoterov.
                  И служит именно для управления зависимостями. При этом производительность будет похоже даже не в разы, а на порядки выше и кода меньше.

                  Советую почитать
                  tangent.org/
                  pecl.php.net/package/memcached
                  • 0
                    Это очень интересная тема.
                    А можете дать прямую ссылку на документацию по by_key-функциям?

                    А то я за 10 минут в Гугле не нашел внятных подробностей, как оно работает. Ссылки в основном ведут на доки по UDF-функциям MySQL. Также есть ссылки на листы рассылки, из которых следует, что by_key-функции применяются для привязки некоторого набора ключей гарантировано к одному серверу. Про тэги ни слова.
                    • 0
                      Абсолютно разные вещи.

                      С помощью ByKey можно несколько записей привязать к одному ключу, но только к одному. То есть, грохнув этот ключ, мы грохнем все записи, с ним ассоциированные. И сделано это ни разу не для теггирования — это дает возможность разносить записи по «виртуальным серверам» в терминологии libmemcached, которые в свою очередь могут быть размещены на одном или нескольких физических серверах.

                      Исходники я не смотрел, но судя по тому, что они говорят «The larger the the set of nodes, the fewer misses that will occur. » каждый виртуальный сервер — на низком уровне ни что иное, как обычная запись в мемкэше. То есть выборка getByKey($serverKey, $recordKey) двухэтапная — сначала делаем get($serverKey) по всем серверам из нашего кластера, затем к полученной хэш-таблице get($recordKey). Перегон хэш-таблицы (которая может быть достаточно больше) из памяти в память — сомнительное мероприятие в плане производительности.

                      Возможно, эта либа более интеллектуальная — в первом проходе она запоминает сервер, на котором был найден виртуальный сервер и слабы, в которых он содержится, а на втором только к этим слабам применяется поиск по аггрегатному ключу ($serverKey, $recordKey). Тогда все более производительно (не ищем записи там, где их гарантировано нет), но более сложно.

                      Очевидно, что ничего общего с тегированием не имеет.
                      • 0
                        По производительности для php в свое время бенчмарил.
                        Аналогичные методы при использовании libmemcached от не хуже до 30% быстрее чем у данги.
                        mget при выборке 16 ключей приблизительно в 7 — 10 раз делал стандартный get. Если тянуть за раз два ключа — выигрыш процентов 20 получился.

                        Естественно влияют настройки дистрибуции. Если загнать на предел, MEMCACHED_BEHAVIOR_KETAMA и т.п., set процентов на 15-20 сливал данге, get на 0-10, mget тоже проседал. Правда получаем с другой стороны полноценную кетаму.
                      • 0
                        Прогнал. Просто в свое время когда запнулся за них _by_key натолкнуло на мысль как крайне просто контроль зависимостей реализовать. Обертку уже и не помню за час или полтора накидал. Больше про нее и не вспоминал — работает ведь. А в голове почему то отложилось что _by_key использовал.
                        На коленке упрощенный пример накидал как это реализовано.
                        Это для pecl.php.net/package/memcached. Для данговского клиента по моему достаточно будет Memcached на Memcache поменять :-(

                        • 0
                          Код обрезался.

                          class MyMemcached extends Memcached
                          {
                          /**
                          * Сохраняет в кэше сущность от которой зависят остальные
                          * @param str $byKey тэг
                          * @param str $key ключ
                          * @param any $value значение
                          */
                          public function set_and_key($byKey, $key, $value){
                          // В реале конечно по другому, тут кому как удобнее главное уникальный
                          if ($this->set($byKey, rand())) // Сохраняем новое уникальное значение для тэга $byKey
                          return $this->set($key, $value);
                          return false;
                          }

                          /**
                          * Сохраняет в кэше зависимую сущность, привязав ее к текущему значению тэга
                          * @param str $byKey тэг
                          * @param str $key ключ
                          * @param any $value значение
                          */
                          public function set_by_key($byKey, $key, $value){
                          return $this->set($this->get($byKey). $key, $value); // Ложим в кэш с ключем привязанным
                          // к текущему значению тэга
                          }

                          /**
                          * Получает из кэша зависимую сущность
                          * @param str $byKey
                          * @param str $key ключ
                          */
                          public function get_by_key($byKey, $key){
                          return $this->get($this->get($byKey). $key);
                          }
                          }

                          $mc = new MyMemcached;
                          $mc->addServer('localhost', 11211);

                          $masterValue = 1;
                          $mc->set_and_key('byKey', 'masterValue', $masterValue);

                          $slaveValue = 'value'; // Значение, которое мы привяжем к $byKey

                          $mc->set_by_key('byKey', 'slaveValue', $slaveValue); // Сохраняем в кэше
                          //$slaveValue, привязав его к текущему значению byKey
                          var_dump($mc->get_by_key('byKey', 'slaveValue')); // Проверяем, что лежит в кэше;
                          //string(5) «value» — то что положили

                          $masterValue = 2; // Меняем значение тэга, от которого зависит $slaveValue
                          $mc->set_and_key('byKey', 'masterValue', $masterValue); // обновляем его значение в кэше

                          var_dump($mc->get_by_key('byKey', 'slaveValue')); // Пытаемся получить из кэша $slaveValue;
                          //bool(false) — то что и нужно, тэг изменился
                  • 0
                    Давно мучал вопрос зависимостей кеша.
                    Очень, интересная статья, спасибо.

                    Реализван ли у вас фронтэнд кеша в файловой системе? и каким образом хранятся там сами тэги?
                    • 0
                      Каждый слот имеет список тэгов, которыми он помечен, вместе с версиями этих тэгов. При инвалидации тэга его версия изменяется, поэтому библиотека при извлечении очередного слота понимает, что он «протух». Ну а для ускорения операции используется «групповой фетч» в memcached, который поддерживается в новых версиях PHP (т.е. за один запрос к memcached получаются сразу все тэги некоторого слота).
                      • 0
                        Хмм, с memcached все более менее ясно.
                        Но я имел ввиду насколько оптимально ваша библиотека работает с Zend_Cache_Backend_File?
                        Так как цена извлечения ключа, т.е. чтения файла, в разы больше чем в memcached.
                        А наличие тегов только уменьшает скрость чтения/записи/очистки/сборки мусора…

                    • +1
                      о какой высоконагруженности вы говорите с зенд фреймворком? да он положит сервер раньше чем вы успеете придумать что-то об использовании мемкеша (не поймите неправильно, я очень люблю ЗФ и сам его юзаю, но на 7000-10000 запросов в секунду он ляжет как минимум временем на подключение своих классов)

                      хотел бы вам порекомендовать посмотреть видео с конференции HighLoad о мемкеше (http://smotri.com/video/view/?id=v649374d232) — там как раз описывается как можно обновлять кеши на высокой нагрузке чтобы при этом ничего не падало — а вы говорите практически невозможно ;)
                      • +1
                        мы все молимся на smira
                        после цикла его статей нагрузка на серверы упала почти в 6 раз

                        и (ИМХО) он тэги описал понятнее
                        • 0
                          Насколько я понимаю, высоконагруженность порядка проекта МойКруг ;).
                          • +3
                            Точно 7000-10000? у меня он почему-то клал сервер на 6999-9999 запросах в секунду… Наверное, я не с теми параметрами PHP компилировал… :-)
                            • 0
                              Joka, покажите пожалуйста нам всем решение на PHP, которое на одном сервере будет вам генерировать 10000 страниц в секунду, ну или хотя бы ссылку дайте, ну или хотя бы расскажите о каких запросах вы говорите.
                            • +1
                              $slot->addTag(new Cache_Tag_User($loggedUser);
                              $slot->addTag(new Cache_Tag_Language($currentLanguage);
                              не хватает скобок
                              • –3
                                Плюсанул из уважения, но вообще «обертка к обертке» это не очень круто.
                                • 0
                                  постмодернизм?
                                  • +1
                                    Дано:
                                    а) СУБД
                                    б) Memcashe

                                    Задача:
                                    Снизить нагрузку на БД.

                                    Хочется: некий слой с) МЕМСУБД — который решает эту задачу

                                    Не знаю есть же некие интерфейсы для работы с базами данных. Ну например в кодеигнитере я пишу на активрекорд:
                                    $this->db->select('id');
                                    $this->db->from('table')
                                    , а эта фигня сама решает — взять данные из бд -> положить в кеш и вынуть из него или взять из кеша. Протух кеш или нет, сама генерит ключи для запросов и т.п. Как то так хочется. И желательно без апи над апи зенда над апи мемкеша.
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                      • 0
                                        На больших таблицах к сожалению это уже не так.
                                        Даже если весь индекс лежит в раме.

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

                                        Другой вопрос — обертка может быть сама настолько тяжелой, что съест выигрыш от кэширования.
                                        Ну это уже вопрос используемого API. Стандартное данговское не выдерживает никакой критики по ущербности функционала.

                                        В принципе, libmemcached подкупает тем, что большинство функционала из статьи, в ней реализовано. И если и делать обертку, то предельно простую и соответственно быструю.

                                        А mget там — это вообще ракета.
                                        • +1
                                          И мало того, MySQL и Oracle сохранять результаты этих запросов в памяти, т.е. сами их кэшируют. Другое дело, что они их могут выкинуть, по своему усмотрению, а в memcache мы сами определяем время хранения.
                                          • 0
                                            Кстати да, решение со стороны СУБД кажется наиболее логичным. Т.е. если бы например мускул можно было так «оттюнить» чтобы он максимум информации держал в горячем кеше и сбрасывала инфу на диск только периодически — было бы супер, и мемкеш бы не понадобился.
                                            Кстати пока писал, вспомнил чито в СУБД firebird есть такая опция (правда не тестил), но фаербёрд с других концов для веба не очень удачная СУБД. С другой стороны уже более года как Майэскюэль купил отца основателя interbase|firebird — Джима Старки, и где то в недрах мускула вызревает субд falcon… Так что надеюсь в ближайшее время мы увидим решение позволяющее избежать костылей с мемкешем.
                                            • +1
                                              Хаха, я как в воду глядел!
                                              The main goals of Falcon are to exploit large memory for more than just a bigger cache
                                              • 0
                                                Ещё 2 раза ха.Falcon уже вышел:
                                                dev.mysql.com/doc/refman/6.0/en/se-falcon.html
                                                С активным кэшем записей, версиями записей прямо в оперативке, оптимизацией под 64 бита и многими другими штуками. Традиционный вопрос — взлетит?)
                                              • 0
                                                В MySQL, в некоторых случаях помогает использование временных таблиц в памяти: dev.mysql.com/doc/refman/5.1/en/memory-storage-engine.html
                                                Для индексов можно использовать хеш, как в memcache или b-tree.

                                                Можно извратиться, и, например хранить базу на RAM-диске и бекапить ее по крону, при старте mysqld, восстанавливать :) Только все равно при падении сервака, часть инфы потеряем, Ну разве что журналы на обычный хард писать :)
                                                • 0
                                                  мускл можно так оттюнить ;)

                                                  если использовать иннодб, гиг 16 памяти и адекватные настройки сервера мускл — то он выборки из 4-5 миллионых таблиц делает просто в момент потому что все висит в памяти

                                                  само собой не нужно забывать о правильной архитектуре базы, использовании индексов на каждом запросе и тд и тп

                                                  почитайте блог www.mysqlperformanceblog.com/ — там оочень доходчиво народ пишет как нужно работать с базой данных
                                                  • 0
                                                    оттуда:
                                                    Preload table / index into buffer_pool. You can use custom queries by primary / secondary key to «warm up» part of table, but this solution is ugly and may be slow due to random logical I/O.
                                                    рекомендуют целиком пихать, но это не для моего сервака с 526 метрами)
                                                    • 0
                                                      у меня сервера разогреваются в процессе работы, там по 16 гиг памяти — поэтому все влазит :)

                                                      после часа разогрева основная серверная нагрузка идет с fast-cgi процессов… базу даже не особо то и заметно…
                                                • 0
                                                  Конечно же в memcached реализовано магическое сжатие, позволяющее сжать всю вселенскую информацию, скажем, в один гиг и извлекать из нее крохотные кусочки со скоростью света +))))
                                                  В memcached зашит обычный RCU: данные, которые реже запрашиваются — первые кандидаты на убийство, когда заканчивается память, выделенная memcached'у, а это на нагруженных проектах случается, хоть и нечасто с учетом стоимости памяти и объема хранимых в нем данных (у нас сейчас, например, забито всего 1.3 гига и 173 метра свободно).

                                                  В базах тот же самый RCU + инвалидация при изменении таблиц, входящих в запрос. С одной стороны это конечно хорошо — база гарантировано очистит кэш, если данные поменяются, но с другой стороны — это просто отвратительно, поскольку база грохнет кэш, независимо от того, затронули ли изменения возвращаемые строки и/или их количество или нет.
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                          • 0
                                            Можно посмотреть на это решение?
                                          • НЛО прилетело и опубликовало эту надпись здесь
                                            • 0
                                              Ух, заинтересовала прежде всего не статья, а её автор. Очень приятно видеть Вас здесь. Будем читать…
                                              • 0
                                                не понятно про теги! если вот без единого кода на уровне чисто работы с данными — какую проблему решаем? где что хранится?

                                                у меня сомнения по поводу верности предпосылок — я про вот этот кусок:

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

                                                тут вероятно идут игры вокруг отношений юзер-юзер. так может пример рассмотрим? на примере и будет понятно что и зачем.
                                                а то вот я с мемкешом уже лет пять, да всё как-то без тегов прекрасно обходился ;)
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                  • 0
                                                    Радостно юзаю наработки автора. Экономия кучи времени. Все работает без напильников и с толковой документацией.
                                                  • 0
                                                    Дмитрий,

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


                                                    да действительно в memcached-tag есть несколько багов, приводящих в итоге к утечке памяти.
                                                    но прошу заметить что memcached-tags это совершенно другой проект, в котором кроме всего прочего эти баги были исправлены. Более того проект обновлен до memcached 1.2.6 а на днях выйдет обновление до 1.2.8.

                                                    >> На тот момент, когда я ее проверял (несколько месяцев назад) она сегфолтилась, а иногда висла и начинала бесконечно отжирать память. Возможно, сейчас ее уже починили.

                                                    Опять же, это memcached_tag, попробуйте проверить memcached-tagS.
                                                    Мы используем tags на 16ти серверах при нагрузке в среднем в 4Mбайта/сек обмена данными между вэб-приложением и мэмкэшд вот уже полгода, перезагружать не приходилось ни разу.

                                                    Пара вопросов по вашей библиотеке:
                                                    1. где именно хранится связка tag+key(/slot)?
                                                    2. можно ли удалить ячейки по нескольким тагам (например если я хочу удалить всё для $loggedUser но только для $currentLanguage)?
                                                    • 0
                                                      Спасибо, попробую. Собственно, Dklab_Cache_Backend_MemcachedTag, входящий в дистрибутив, поддерживает memcached-tagS тоже.

                                                      1. Где хранится: habrahabr.ru/blogs/php/57142/#comment_1534035
                                                      2. Нет, пока этой поддержки нет, но см. habrahabr.ru/blogs/php/57142/#comment_1538275 — кстати, как называется соответствующая PHP-функция для tagS_delete? и есть ли ссылка на PECL-расширение?
                                                    • 0
                                                      сорри, но всё равно не совсем понятно где физически хранятся тэги (на диске/в памяти/в memcached)?
                                                      т.е. если у меня 2(или больше) параллельных Apache+PHP серверa на один сайт (за load-balancer'ом) + 1 memcached за всем этим. И вот один из них PHP серверов решает удалить всё по тэгу $currentLanguage, удалит ли он всё из самого memcached, или все другие web-сервера продолжат видеть якобы «удаленные» ячейки? Sorry, я не в курсе как работает Zend_Cache_Backend_File.

                                                      насчет PHP-функции и PECL-расширения — врятли они существуют, т.к. memcached-tags это патч к самому memcached серверу и мы используем клиента только на С и Perl.
                                                      Какое расширение использовалось для memcached-tag? Я помотрел здесь ничего такого нет. Вероятно использовался пропаченый 2.2.x pecl memcache модуль отсюда?

                                                      • 0
                                                        Тэги физически хранятся там же, где и ключи. Подробности алгоритма достаточно сложны, но вы можете: а) посмотреть в коде, как оно работает; б) посмотреть в phpt-тестах все варианты использования. Также алгоритм разработан так, чтобы случайная потеря ключа с тэгом (например, в результате вытеснения из памяти) автоматом вела к инвалидации всех зависимых от него ключей. Т.е. алгоритм консистентный.

                                                        Кстати, если memcached-серверов несколько, то можно использовать входящий в библиотеку модуль Dklab_Cache_Backend_ReplicationWrapper. Он обеспечивает репликацию операций удаления (а при желании — и записи) ключей. Вообще, в последних версиях php-модуля memcache репликация поддерживается на встроенном уровне, но с ними все та же проблема: они нестабильны, сегфолтятся и подвисают (видимо, потому до сих пор и бета). Поэтому пришлось сделать простейшую репликацию самостоятельно (благо оно несложно).

                                                        Про PECL для memcached-tag — да, совершенно верно. Там есть поддержка tag_add и tag_delete. А вот в Вашей версии memcached-tagS, вернее, в PHP-модуле для нее, — есть ли поддержка функции tagS_delete?
                                                        • 0
                                                          До сегодняшнего дня у нас небыло своего PHP-модуля, т.к. я уже говорил — мы используем клиента только на С и Perl. (я честно говоря вообще не думал что в ПХП для этого нужен целое расширение)
                                                          но пропатчить pecl php memcache на основе tag_delete оказалось не так сложно:
                                                          надеюсь кому-нибудь пригодиться
                                                          • 0
                                                            Там в дистрибутиве куча файлов, начинающихся с точки. Видимо, бэкапы?

                                                            Кстати говоря, для PECL-расширения ИМХО лучше распространять не полный дистрибутив, а патч. Там могут новые фичи добавиться (всякие там поддержки консистентности и т.д.), и Ваша версия быстро устареет, если она будет не в виде патча идти.
                                                      • –1
                                                        PHP закрывает скобки за вас?)
                                                        • 0
                                                          С использованием слотов для хранения в кеше одного объекта все понятно.
                                                          А что если я хочу хранить коллекцию объектов?
                                                          • 0
                                                            Получается вот так?

                                                            class Communities
                                                            {
                                                                public function findCommunities()
                                                                {
                                                                    $community = new stdClass();
                                                                    $community->id = 1;
                                                            
                                                                    return array($community);
                                                                }
                                                            }
                                                            class Cache_Tag_Communtity extends Geometria_Cache_Frontend_Tag 
                                                            {
                                                                public function __construct($community)
                                                                {
                                                                    parent::__construct("community_{$community->id}");
                                                                }
                                                            }
                                                            class Cache_Slot_Communities extends Geometria_Cache_Frontend_Slot 
                                                            {
                                                                public function __construct()
                                                                {
                                                                    parent::__construct('communities', 60 * 60);
                                                                }
                                                                
                                                                public function save($communities)
                                                                {
                                                                    foreach ($communities as $community) {
                                                                    	$this->addTag(new Cache_Tag_Communtity($community));
                                                                    }
                                                            
                                                                    return parent::save($communities);
                                                                }
                                                            }
                                                            
                                                            $slot = new Cache_Slot_Communities();
                                                            $communities = $slot->thru(new Communities)->findCommunities();
                                                            

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