Perl 如何确保准备和执行之间的DB(Postgres)连接仍然有效?
我有一个守护程序脚本,它永远在while循环中运行。我有一个准备好的语句,这个语句在每个循环上执行 例如:Perl 如何确保准备和执行之间的DB(Postgres)连接仍然有效?,perl,postgresql,connection,reconnect,dbd-pg,Perl,Postgresql,Connection,Reconnect,Dbd Pg,我有一个守护程序脚本,它永远在while循环中运行。我有一个准备好的语句,这个语句在每个循环上执行 例如: my $dbh; sub get_dbh { return DBI->connect(...); } my $dbh = get_dbh(); my $sth = $dbh->prepare("SELECT ....") or die $DBI::errstr; while (1) { // is connection sti
my $dbh;
sub get_dbh {
return DBI->connect(...);
}
my $dbh = get_dbh();
my $sth = $dbh->prepare("SELECT ....") or die $DBI::errstr;
while (1) {
// is connection still there??
//if (!$dbh->ping) {
// $dbh = get_dbh();
//}
$sth->execute('param1');
// do some other things ... sleep between 0 and 3600
}
若准备好的语句是在几个小时前准备好的,那个么问题就会发生(或可能发生)。连接可能会中断,我的程序也会执行。在每次执行之前检查$dbh->ping看起来有点过分了
MySQL支持MySQL\u自动\u重新连接,非常有效。Pg没有类似的东西。我读过关于DBI::Apache的文章,但在我看来它依赖于mod_perl等。它显然是针对web应用程序的
是否有“最佳实践”方法来检查连接状态并在需要时重新连接#
我可以为每个循环准备语句,但这不是解决方案,只是解决问题的一种方法。我不明白你为什么说在每次执行
之前执行ping
是过分的,但另一种方法是显式处理execute
失败的情况,因为数据库句柄无效,需要重新连接、准备语句并再次发出execute
。这会稍微快一点,但我认为没有理由避免使用ping
策略
是否有“最佳实践”方法来检查连接状态并在需要时重新连接#
是的,至少在我看来是这样,因为只有一种方法没有竞争条件,那就是在重试循环中执行查询,如果出现错误,它会处理错误
否则,您仍然有:
准备
选择1代码>或任何您的测试语句
网络断开、后端崩溃、管理员重启服务器等等
执行
喷溅
正确的行为需要伪代码:
while not succeeded:
try:
execute_statement()
succeeded = True
except some_database_exception:
if transaction_is_valid():
// a `SELECT 1` or `select 1 from pg_prepared_statements where name = 'blah'
// succeeded in transaction_is_valid(), so the issue was probably
// transient. Retry, possibly with a retry counter that resets the
// connection if more than a certain number of retries.
// It can also be useful to examine the exception or error state to
// see if the error is recoverable so you don't do things like retry
// repeatedly for a transaction that's in the error state.
else if test_connection_usable_after_rollback():
// Connection is OK but transaction is invalid. You might determine
// this from the exception state or by seeing if sending a `ROLLBACK`
// succeeds. In this case you don't have to re-prepare, just open
// a new transaction. This case is not needed if you're using autocommit.
else:
// If you tried a SELECT 1; and a ROLLBACK and neither succeeded, or
// the exception state suggests the connection is dead. Re-establish
// it, re-prepare, and restart the last transaction from the beginning.
reset_connection_and_re_prepare()
冗长烦人?是的,但通常很容易包装在助手或库中。其他一切仍受种族限制
最重要的是,如果您的应用程序正在执行多个事务,那么它需要记住它所执行的所有操作,直到事务提交为止,并且在出现错误时能够重试整个事务。或者告诉用户“哦,我吃了你的数据,请重新输入并重试”
如果您不介意竞争,只想通过定期检查来处理任何明显死机的连接,只需将最后一次查询的时间存储在一个变量中。发出查询时,检查时间戳是否超过几分钟,如果超过几分钟,则发出SELECT 1
或查询pg_prepared_语句
,以检查您准备的语句。你要么准备好向用户吐出错误,要么就用正确的错误处理来包装整个过程。。。在这种情况下,时间检查和测试毫无意义。当您知道可能会让连接闲置一个小时时,ping/测试连接是非常合理的
更好的方法是使用DBI,并使其相对容易:
while (1) {
my $dbh = DBI->connect_cached(..., { RaiseError => 1 }); # This will ping() for you
my $sth = $dbh->prepare_cached('SELECT ...');
$sth->execute('param1');
# Do work, sleep up to 1 hour
}
这样,您将在连接的整个生命周期中重复使用相同的准备好的语句
(值得一提的是,现代DBD::Pg ping是通过一个高效的本机PostgreSQL调用实现的。)过度杀戮可能是一个很难的词,但这意味着我必须在每次执行之前执行它。我实际上在循环中有3-4次执行。我没有测试“重新连接”,但它可能会迫使我“重新准备”语句?此方法所做的只是将execute
用作ping
,除此之外,如果它能工作,就没有什么可做的了。对失败的执行的响应将与失败的ping的响应相同:在发出执行之前,您必须连接到数据库并再次准备查询。我决定将您的应答im与connect\u cached和是的,间歇性检查空闲连接没有什么错,只是你可以通过某种方式“验证”我不同意的连接。