Большим недостатком стандартной реализации MySQL клиента является отсутствие поддержки асинхронных запросов, выполняющихся в отдельном потоке не блокируя основной. Такие запросы отлично подходят для операций не нуждающихся в ответе сервера, как например обновление статистики, логирование и т.д. Особенно если операции работают с большими объемами данных и занимают много времени (например добавления строки в таблицу статистики вызывает триггер пересчета общей статистики для всех пользователей).
Для обхода этого ограничения MySQL клиента, приходиться придумывать разнообразные костыле-методы позволяющие выполнять запросы не блокируя выполнение приложения.
Мне кажется самый простой и очевидный метод запустить отдельно от приложения скрипт (или сервис) который в бесконечном цикле будет проверять некую очередь запросов и выполнять их в порядке поступления, тогда как приложение будет просто добавлять запросы в эту очередь.
Ниже речь пойдет о похожем, но более красивом методе, использующем встроенный в HipHop сервер сообщений XBox.
Xbox - простой сервер сообщений, для работы с которым достаточно 3 функций:
- xbox_post_message
- отправка сообщения без блокирования основного потока
- xbox_send_message
- отправка сообщение с блокированием основного потока пока сообщение не будет обработано (или до истечения таймаута)
- xbox_process_message
- обработка сообщения
Для нашей задачи интересна функция xbox_post_message.
Рассмотрим на примере реализацию выполнения MySQL запросов асинхронно с использованием XBox сервера для формирования и обработки очереди.
Начнем с определения функции обработчика сообщений. Для упрощения, в сообщении будем отправлять только sql запрос, поэтому все что нужно сделать в обработчике - выполнить полученное сообщение.
function xbox_process_message($message) {
sleep(5);
mysql_connect('127.0.0.1', 'root', '');
mysql_query($message);
echo "Async: ".$message."\n";
}
* я поставил sleep(5) в начале функции, чтобы сэмулировать сложный запрос, для лучшей демонстрации фонового выполнения.
Для того чтобы добавить запрос в очередь нужно выполнить:
xbox_post_message($sqlQuery)
первый параметр функции содержит текст сообщения, который в нашем случае является sql запросом.
После выполнения xbox_post_message запрос будет добавлен в очередь и как только один из XBox серверов освободится - выполнен.
Как видите просто и продуктивно.
Напишем небольшой тестовый скрипт, чтобы убедиться что асинхронные запросы работают:
mysql_connect('127.0.0.1', 'root', '');
echo "Started\n";
function xbox_process_message($message) {
sleep(5);
mysql_connect('127.0.0.1', 'root', '');
mysql_query($message);
echo 'Async: '.$message."\n";
}
function query($sql) {
mysql_query($sql);
echo 'Sync: '.$sql."\n";
}
function async_query($sql) {
xbox_post_message($sql);
}
async_query("SELECT 10");
query("SELECT 1");
while(true) {
sleep(1);
}
Выполним:
hphp -k 1 --log=3 test_async.php
Получим:
Sync: SELECT 1
Async: SELECT 10
Как видно с результата, сначала был выполнен синхронный запрос (хотя он был вызван вторым), а после него асинхронный. И сложная обработка данных (в нашем случае сэмулированная sleep(5)) выполняясь не блокируя основной поток, чего мы и хотели добиться.
Бесконечный цикл в конце скрипта не обязателен (добавил его только для того чтобы было куда выводить echo), так как асинхронные сообщения Xbox'а обрабатываются отдельно от главного потока, поэтому нет необходимости управлять их выполнением. Если в данном примере убрать цикл, запрос выполниться, но сообщение "Async: SLEEP(10)" выводить будет некуда.
Вообще у XBox сервера огромный потенциал использования - от фоновых задач до распаралелливания исчислений и эмуляции многопоточности. А выполнение некритичного для запроса кода в фоне может значительно уменьшить время обработки запроса.
В тему:
Если пост понравился - нажмите на +1 - мне будет приятно.