Когда приложение отправляет запрос на MySQL сервер, происходит две основные операции:
- Разбор, анализ и подготовка плана выполнения для SQL запроса.
- Выполнение нужных операций в движке таблицы и возврат результата.

Движок InnoDB позволяет включить специальный интерфейс для работы без использования SQL прослойки. Этот интерфейс называется HandlerSocket. Он предоставляет протокол работы с данными по принципу NoSQL.
Использование этого протокола может значительно ускорить простые запросы типа:
SELECT * FROM users WHERE id = 7
Протокол поддерживает базовые операции чтения/записи/обновления/удаления, а также ряд продвинутых (например, инкремент/декремент).
Настройка
Для установки необходимо поставить сборку Percona MySQL:
apt-get install percona-server-server-5.5
После этого, указать настройки портов протокола для чтения и записи данных в файле
loose_handlersocket_port = 9998
loose_handlersocket_port_wr = 9999
loose_handlersocket_threads = 16
loose_handlersocket_threads_wr = 1
open_files_limit = 65535
После перезагрузки сервера, нужно установить плагин:
mysql> install plugin handlersocket soname 'handlersocket.so';
Готово. Проверить установку можно с помощью команды:
mysql> show processlist; +----+-------------+-----------------+---------------+---------+------+-------------------------------------------+------------------+-----------+---------------+-----------+ | Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined | Rows_read | +----+-------------+-----------------+---------------+---------+------+-------------------------------------------+------------------+-----------+---------------+-----------+ | 1 | system user | connecting host | NULL | Connect | NULL | handlersocket: mode=rd, 0 conns, 0 active | NULL | 0 | 0 | 0 | | 2 | system user | connecting host | NULL | Connect | NULL | handlersocket: mode=rd, 0 conns, 0 active | NULL | 0 | 0 | 0 | | 3 | system user | connecting host | NULL | Connect | NULL | handlersocket: mode=rd, 0 conns, 0 active | NULL | 0 | 0 | 0 | | 4 | system user | connecting host | handlersocket | Connect | NULL | handlersocket: mode=wr, 0 conns, 0 active | NULL | 0 | 0 | 0 | | 5 | system user | connecting host | NULL | Connect | NULL | handlersocket: mode=rd, 0 conns, 0 active | NULL | 0 | 0 | 0 | | 6 | system user | connecting host | NULL | Connect | NULL | handlersocket: mode=rd, 0 conns, 0 active | NULL | 0 | 0 | 0 | | 7 | system user | connecting host | NULL | Connect | NULL | handlersocket: mode=rd, 0 conns, 0 active | NULL | 0 | 0 | 0 | | 8 | system user | connecting host | NULL | Connect | NULL | handlersocket: mode=rd, 0 conns, 0 active | NULL | 0 | 0 | 0 | ...
# Если плагин включен, он будет виден в процессах
PHP и HandlerSocket
Для работы из PHP существует библиотека php-handlersocket. Она устанавливается, как расширение:
wget https://php-handlersocket.googlecode.com/files/php-handlersocket-0.3.1.tar.gz tar -xvf php-handlersocket-0.3.1.tar.gz cd php-handlersocket-0.3.1 phpize ./configure make make install
После этого необходимо подключить расширение в
extension=handlersocket.so
Проверить установку можно так:
root@sh:~# php -i | grep handlersocket handlersocket
# Если расширение установлено, оно будет найдено в информации о php
Работа из приложения
HandlerSocket предоставляет работу с данными в движке InnoDB с помощью инициализации индекса и колонок, с которыми будут происходить манипуляции. Перед тем, как начать работу, необходимо подготовить таблицу:
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`text` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
# Создаем таблицу test в базе данных test
Запись данных
Запишем в эту таблицу данные:
<?
# указываем хост и порт для записи
$hs = new HandlerSocket('localhost', 9999);
# Открываем индекс и инициализируем работу с двумя колонками: id и text
$hs->openIndex(1, 'test', 'test', '', 'id,text');
# Вставляем данные
$hs->executeInsert(1, array('1', 'absde'));
Обратите внимание, что для записи используется порт, установленный в параметре loose_handlersocket_port_wr. Установленное значение можно проверить обычным SQL-запросом:
mysql> select * from test; +----+-------+ | id | text | +----+-------+ | 1 | absde | +----+-------+
Чтение данных
Прочитать данные из таблицы можно таким образом:
<?
# подключаемся к порту для чтения данных
$hs = new HandlerSocket('localhost', 9998);
# открываем первичный ключ таблицы
$hs->openIndex(1, 'test', 'test', HandlerSocket::PRIMARY, 'id,text');
# выполняем операцию получения данных первичного ключа со значением 1
$data = $hs->executeSingle(1, '=', array('1'), 1, 0);
print_r($data);
Производительность
Сравним два запроса на получение данных из таблицы — один через классический SQL, второй — через HandlerSocket:
<?
# Отправляем 200 тыс. запросов на чтение данных через HandlerSocket
$hs = new HandlerSocket('localhost', 9998);
$hs->openIndex(2, 'test', 'test', HandlerSocket::PRIMARY, 'id,text');
$t = microtime(1);
for ( $i = 0; $i < 200000; $i++ )
{
$retval = $hs->executeSingle(1, '=', array( mt_rand(1, 1000) ), 1, 0);
}
echo 'Spent ' . (microtime(1) - $t) . 's' . "\n";
# Отправляем 200 тыс. таких же запросов, только через SQL-интерфейс
mysql_connect('localhost', 'root', '');
$t = microtime(1);
for ( $i = 0; $i < 200000; $i++ )
{
$q = mysql_query('SELECT text FROM test.test WHERE id = ' . mt_rand(1, 1000));
$r = mysql_fetch_assoc($q);
}
echo 'Spent ' . (microtime(1) - $t) . 's' . "\n";
mysql_close();
Результаты на небольшом облачном сервере:
Spent 6.9966340065002s Spent 19.924588918686s
# Handlersoket быстрее
SQL оказался в 2.5 раза медленнее. Важно понимать, что такое преимущество будет получено, только если большинство данных помещается в память (читайте об эффективной настройке MySQL). Если существует большое количество операций с диском, выигрыш в скорости будет незаметным.
Применение на практике
Замена кэширования
Самое большое преимущество от использования HandlerSocket можно получить при замене кэширования данных. Большинство данных, которые находятся в кэше — это часто данные записей, полученных по первичному ключу:
$q = mysql_query('SELET name, age FROM users WHERE id = 7');
$data = mysql_fetch_assoc($q);
memcache_set('user7', $data);
# Классическое кэширования записей по первичному ключу

Имеет смысл использовать HandlerSocket вместо SQL для получения таких данных. Тогда кэшировать их не понадобится:
$hs = new HandlerSocket('localhost', 9998);
$hs->openIndex(1, 'db', 'users', HandlerSocket::PRIMARY, 'id,name,age');
$data = $hs->executeSingle(1, '=', array('7'), 1, 0);
# Получаем данные пользователя без SQL
Во втором случае мы:
- уменьшим расход памяти, т.к. не будет необходимости сохранять данные в кэш
- упростим код, т.к. данные будут храниться в едином месте, и не будет необходимости синхронизировать данные в кэше и базе
- уменьшим риск резкого роста нагрузки при сбое кэширующих серверов
Флаги

Другой вариант применения — это работа с key-value данными. Например, различные флаги для пометки состояния пользователя:
<?
$hs = new HandlerSocket('localhost', 9999);
$hs->openIndex(1, 'test', 'flags', '', 'id,name,value');
$hs->executeInsert(1, array('1', 'is_moderator', 1));
Операция вставки не заменяет предыдущего значения, поэтому для обновления данных необходимо будет использовать операцию update.
Счетчики

Есть поддержка атомарных операций, в том числе инкремент/декремент:
$hs = new HandlerSocket('localhost', 9999);
$hs->openIndex(1, 'test', 'counters', '', 'value');
$hs->executeSingle(1, '=', array('1'), 1, 0, '+', array('1'));
# Увеличиваем колонку value в таблице counters на 1
Эти операции удобно использовать для работы со счетчиками (например, подсчет количества просмотров у статьи).
Самое важное
В большинстве случаев чтение данных по ключу с помощью HandlerSocket даст прирост в производительности и экономию ресурсов. Большим преимуществом этого протокола является то, что не будет необходимости устанавливать и поддерживать дополнительное решение. Кроме этого, все данные остаются в удобном табличном виде, а значит будет возможность делать SQL выборки.
Смотрите подробное описание протокола и PHP библиотеки.