Как хранить сессии PHP в APC Cache?

Хранение сессий на диске очень медленное и болезненное для меня. У меня очень высокий трафик. Я хочу сохранить сеанс в Advanced PHP Cache, как я могу это сделать?

9 ответов

Решение

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

Я бы собрал рекомендуемые решения, как это:

Использование APC в качестве хранилища сеансов

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

Итог: избегайте этого, это не сработает.

альтернативы

Может быть доступно несколько обработчиков сеансов. Проверьте вывод phpinfo() на Session раздел "Зарегистрированные обработчики сохранения".

Хранение файлов на RAM-диске

Работает "из коробки", но по понятным причинам требует, чтобы файловая система была смонтирована как RAM-диск.

Общая память (мм)

Доступно, когда PHP скомпилирован с mm включен. Это встроено в Windows.

Memcache (д)

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

<?php

// to enable paste this line right before session_start():
//   new Session_APC;
class Session_APC
{
    protected $_prefix;
    protected $_ttl;
    protected $_lockTimeout = 10; // if empty, no session locking, otherwise seconds to lock timeout

    public function __construct($params=array())
    {
        $def = session_get_cookie_params();
        $this->_ttl = $def['lifetime'];
        if (isset($params['ttl'])) {
            $this->_ttl = $params['ttl'];
        }
        if (isset($params['lock_timeout'])) {
            $this->_lockTimeout = $params['lock_timeout'];
        }

        session_set_save_handler(
            array($this, 'open'), array($this, 'close'),
            array($this, 'read'), array($this, 'write'),
            array($this, 'destroy'), array($this, 'gc')
        );
    }

    public function open($savePath, $sessionName)
    {
        $this->_prefix = 'BSession/'.$sessionName;
        if (!apc_exists($this->_prefix.'/TS')) {
            // creating non-empty array @see http://us.php.net/manual/en/function.apc-store.php#107359
            apc_store($this->_prefix.'/TS', array(''));
            apc_store($this->_prefix.'/LOCK', array(''));
        }
        return true;
    }

    public function close()
    {
        return true;
    }

    public function read($id)
    {
        $key = $this->_prefix.'/'.$id;
        if (!apc_exists($key)) {
            return ''; // no session
        }

        // redundant check for ttl before read
        if ($this->_ttl) {
            $ts = apc_fetch($this->_prefix.'/TS');
            if (empty($ts[$id])) {
                return ''; // no session
            } elseif (!empty($ts[$id]) && $ts[$id] + $this->_ttl < time()) {
                unset($ts[$id]);
                apc_delete($key);
                apc_store($this->_prefix.'/TS', $ts);
                return ''; // session expired
            }
        }

        if (!$this->_lockTimeout) {
            $locks = apc_fetch($this->_prefix.'/LOCK');
            if (!empty($locks[$id])) {
                while (!empty($locks[$id]) && $locks[$id] + $this->_lockTimeout >= time()) {
                    usleep(10000); // sleep 10ms
                    $locks = apc_fetch($this->_prefix.'/LOCK');
                }
            }
            /*
            // by default will overwrite session after lock expired to allow smooth site function
            // alternative handling is to abort current process
            if (!empty($locks[$id])) {
                return false; // abort read of waiting for lock timed out
            }
            */
            $locks[$id] = time(); // set session lock
            apc_store($this->_prefix.'/LOCK', $locks);
        }

        return apc_fetch($key); // if no data returns empty string per doc
    }

    public function write($id, $data)
    {
        $ts = apc_fetch($this->_prefix.'/TS');
        $ts[$id] = time();
        apc_store($this->_prefix.'/TS', $ts);

        $locks = apc_fetch($this->_prefix.'/LOCK');
        unset($locks[$id]);
        apc_store($this->_prefix.'/LOCK', $locks);

        return apc_store($this->_prefix.'/'.$id, $data, $this->_ttl);
    }

    public function destroy($id)
    {
        $ts = apc_fetch($this->_prefix.'/TS');
        unset($ts[$id]);
        apc_store($this->_prefix.'/TS', $ts);

        $locks = apc_fetch($this->_prefix.'/LOCK');
        unset($locks[$id]);
        apc_store($this->_prefix.'/LOCK', $locks);

        return apc_delete($this->_prefix.'/'.$id);
    }

    public function gc($lifetime)
    {
        if ($this->_ttl) {
            $lifetime = min($lifetime, $this->_ttl);
        }
        $ts = apc_fetch($this->_prefix.'/TS');
        foreach ($ts as $id=>$time) {
            if ($time + $lifetime < time()) {
                apc_delete($this->_prefix.'/'.$id);
                unset($ts[$id]);
            }
        }
        return apc_store($this->_prefix.'/TS', $ts);
    }
}

Теоретически, вы должны иметь возможность написать собственный обработчик сеанса, который использует APC, чтобы сделать это прозрачно для вас. Тем не менее, я не смог найти ничего действительно многообещающего в быстром пятиминутном поиске; кажется, что большинство людей используют APC для кэширования байт-кода и помещают свои сессии в memcached.

Просто поместите ваш диск /tmp (или, где хранятся файлы сессий PHP) на RAM-диск, такой как tmpfs или же ramfs также имел бы серьезный прирост производительности и был бы намного более прозрачным переключателем с нулевыми изменениями кода.

Прирост производительности может быть значительно меньше, но он все равно будет значительно быстрее, чем сеансы на диске.

Храните его в файлах cookie (в зашифрованном виде) или MongoDB. APC на самом деле не предназначен для этой цели.

Явное закрытие сессии сразу после запуска, открытия и записи сеанса должно решить проблему блокировки в ответе Unirgy (где доступ к сеансу всегда циклический (начало / открытие-запись-закрытие). Я также представляю себе второй класс - APC_journaling или что-то подобное, используемое совместно с сеансами в конечном итоге было бы лучше.... Сессия начинается и записывается с уникальным внешним идентификатором, назначенным для каждого сеанса, этот сеанс закрывается, а журнал (массив в кэше apc через _store & _add) открывается / создается для любые другие записи, предназначенные для перехода в сеанс, которые затем могут быть прочитаны, проверены и записаны в сеанс (идентифицированный этим уникальным идентификатором!) в apc при следующей удобной возможности.

Я нашел хороший пост в блоге, объясняющий, что блокирующий хаос, на который ссылается Свен, исходит из блокировки сеанса до его закрытия или завершения сценария. Сеанс, который немедленно закрывается, не мешает чтению только записи. http://konrness.com/php5/how-to-prevent-blocking-php-requests - ссылка на пост в блоге. Надеюсь это поможет.

Другое хорошее решение - хранить сессии PHP в memcached.

session.save_handler = memcache

Вы можете хранить данные сеанса в общей памяти PHP.

session.save_handler = mm

Но это должно быть доступно: http://php.net/manual/en/session.installation.php

Кеширование внешних данных в PHP

Ссылка на учебник - http://www.gayadesign.com/diy/caching-external-data-in-php/


Как использовать APC Caching с PHP

Ссылка на учебник - http://www.script-tutorials.com/how-to-use-apc-caching-with-php/

Другие вопросы по тегам