Хранение сессий в Memory Storage Engine и переполнение кучи (heap)
Сегодня речь порйдёт об интересных граблях с авторизацией на сайте. Хранение сессий у нас реализовано в БД, с помощью Zend_Session_SaveHandler_DbTable. Это очень удобно, т.к. быстрее чем хранение в виде файлов, и нагляднее. Так вот, сегодня при авторизации вдруг стало выдаваться сообщение: Error: Zend_Session is currently marked as read-only. Также очень часто вылетала вот эта ошибка: Exception thrown without a stack frame in Unknown on line 0
“Хм, что бы это могло быть” подумал я. Сначало я подумал, что что-то не так с самим Zend Framework, посмотрел где возникает ошибка, и увидел следующий код:
При работе на продакшене, следующий код вызывал фатальную ошибку. Оказывается, что флаг записи стоит в FALSE. Начал выяснять почему он такой, и в каком месте он устанавливается в TRUE.
if (self::$_throwStartupExceptions) {
require_once ‘Zend/Session/Exception.php’;
set_error_handler(array(‘Zend_Session_Exception’, ‘handleSessionStartError’), $errorLevel);
}$startedCleanly = session_start();
…
parent::$_writable = true;
См. Zend_Session, строка 472
После гугления выяснилось, что в самом ErrorHandler’е вызывается исключение, что вызывает такую реакцию системы. Дальше мне было интересно посмотреть, а что же находится в таблице с сессиями. Посмотрев в неё, я был немного ошарашен, т.к. она собержала более 3000 записей. После очистки таблицы баг исчез.
Тут надо упомянуть, что в качестве движка хранения для этой таблицы у нас используетс я MEMORY storage engine. А он хранит данные в куче (HEAP), объем которой ограничен параметром MySQL “max_heap_table_size“.
Посмотреть значение этой переменной можно с помошью запроса “show
1 2 3 |
max_error_count | 64 max_heap_table_size | 16777216 max_insert_delayed_threads | 20 |
А установить с помощью вот этого запроса:
1 2 |
mysql> SET max_heap_table_size = 1024*1024; Query OK, 0 rows affected (0.00 sec) |
Количество памяти, необходимой для хранения данных таблицы можно посчитать по следующей формуле:
- The memory needed for one row in a
MEMORY
table is calculated using the following expression:
123SUM_OVER_ALL_BTREE_KEYS(<em><code>max_length_of_key</code></em> + sizeof(char*) × 4)+ SUM_OVER_ALL_HASH_KEYS(sizeof(char*) × 2)+ ALIGN(<em><code>length_of_row</code></em>+1, sizeof(char*))
ALIGN()
represents a round-up factor to cause the row length to be an exact multiple of thechar
pointer size.sizeof(char*)
is 4 on 32-bit machines and 8 on 64-bit machines.
Вообще, рекомендую вам почитать про Memory storage Engine на сайте MySQL.
Не пробовал http://rediska.geometria-lab.ru/documentation/integration-with-frameworks/zend-framework/zend_session-save-handler/ ?
Слышал много хороших отзывов, но не пробовал.
Хочу попробовать HandlerSocketSession.php из поставки http://code.google.com/p/php-handlersocket. Тоже самое NoSQL хранение сессий и быстрый доступ 🙂