Все мы привыкли работать с базой в стиле:
Но пока работает длинный запрос, мы в приложении можем выполнить что-то полезное. Не простаивать же процессорному времени.
Для PostgreSQL в DBD::Pg есть некоторое подобие асинхронности. И иногда оно таки нам помогает.
Вы можете продолжить выполнять приложение, не дожидаясь выполнения запроса. Включается это параметром pg_async к prepare-запросу:
Для pg_async есть три константы:
Так же есть три вспомогательных метода —
Из минусов — нельзя установить callback на момент исполнения запроса. Нужно постоянно проверять.
Так же в один момент времени в одном соединением можно выполнять только один запрос. Но что мешает открыть десяток соединений и по очереди посылать в них запросы?)
Попробую рассказать о применениях этой технологии:
- выполнить запрос
- дождаться ответа
- продолжить выполнение
Но пока работает длинный запрос, мы в приложении можем выполнить что-то полезное. Не простаивать же процессорному времени.
Для PostgreSQL в DBD::Pg есть некоторое подобие асинхронности. И иногда оно таки нам помогает.
Вы можете продолжить выполнять приложение, не дожидаясь выполнения запроса. Включается это параметром pg_async к prepare-запросу:
use strict;
use warnings;
use Time::HiRes 'sleep';
use DBD::Pg ':async';
my $dbh = DBI->connect('dbi:Pg:dbname=postgres', 'postgres', '', {AutoCommit=>0,RaiseError=>1});
## Запустить длинный запрос
my $sth = $dbh->prepare("SELECT pg_sleep(?)", {pg_async => PG_ASYNC});
$sth->execute(5);
## Пока работает, мы можем что-то сделать
print "Your query is processing. Thanks for waiting\n";
check_on_the_kids();
while (!$dbh->pg_ready) {
check_on_the_kids();
## и подождём чуть-чуть
sleep 0.1;
}
print "The query has finished. Gathering results\n";
my $result = $sth->pg_result;
print "Result: $result\n";
my $info = $sth->fetchall_arrayref();
Для pg_async есть три константы:
- PG_ASYNC — выполнение запроса в асинхронном режиме
- PG_OLDQUERY_CANCEL — если в этот момент работал предыдущий запроса, то он отменяется
- PG_OLDQUERY_WAIT — блокируемся для ожидания предыдущего запроса и только потом начинаем выполнять новый
Так же есть три вспомогательных метода —
- pg_cancel — отменить запрос. Реально открывается ещё одно соединение, в котором посылается SELECT pg_cancel_backend(?);
- pg_ready — возвращает true, если запрос выполнился.
- pg_result — блокируется до момента выполнения запроса, после чего возвращает то же, что и ->execute в стандартном режиме.
Из минусов — нельзя установить callback на момент исполнения запроса. Нужно постоянно проверять.
Так же в один момент времени в одном соединением можно выполнять только один запрос. Но что мешает открыть десяток соединений и по очереди посылать в них запросы?)
Попробую рассказать о применениях этой технологии:
- тяжёлые запросы + сложная логика. Равномерно загружаем свой сервер и сервер БД.
while (){ my $foo = compute_foo();#тяжёлая функция #блокируемся до тех пор, пока не выполнится предыдущий запрос $dbh->do('UPDATE stats SET foo=?', { pg_async => PG_ASYNC + PG_OLDQUERY_WAIT }, $foo); }
- работа с несколькими серверами БД
$first_dbh->do('DELETE FROM old_data', { pg_async => PG_ASYNC }); $second_dbh->do('UPDATE new_data SET status=0', { pg_async => PG_ASYNC })
- таймауты. вы можете убить запрос по таймауту, если он ещё не отработал
- конкурентные запросы. посылаем один и тот же запрос двум серверам и отдаём данные с того сервера, который ответил быстрее.
- запросы, результат выполнения которых вам абсолютно безразличен