【问题标题】:Check database connection before open a new one在打开一个新的之前检查数据库连接
【发布时间】:2018-10-22 14:34:01
【问题描述】:

我的应用程序结构如下:

 /root_dir/lib/Mojo_App/Controller/Main.pm  
 /root_dir/lib/Mojo_App/Database/Db.pm  
 /root_dir/ ...

在 Database::Db 中,我有一个创建与 SQL SERVER 的连接的子例程。该子例程被导出并用于 Main.pm
示例:
数据库::数据库

my $config = {
db1 => {
    host => 'dbi:Driver:server=ip01;database=db01',
    user => 'user01',
    pass => 'pass01'
},
db2 => {
    host => 'dbi:Driver:server=ip02;database=db02',
    user => 'user02',
    pass => 'pass02'
},
db3 => {
    host => 'dbi:Driver:server=ip03;database=db03',
    user => 'user03',
    pass => 'pass03'
}
};


sub connect_db {
use DBI;
my $self = shift;

return 0 unless $self;

my $dbh;

if (exists($config->{$self})) { 
    return $dbh = DBI->connect($config->{$self}->{host}, $config->{$self}->{user}, $config->{$self}->{pass}) || die( $DBI::errstr . "\n" ) ;
} else {
    return "Connection not in config. \n" ;
}
return $dbh; 

}

在 Main.pm 我这样连接:

my $dbh = connect_db('db1');

现在假设我需要在索引页面上连接到数据库,所以我将在 Main.pm 中有一个像这样的子例程索引:

sub index {
    my $self = shift;
    my $dbh = connect_db('db1');
    ...
    $self->render();
}

现在我不希望每次用户访问索引页面时都建立新连接,我想检查连接是否处于活动状态,如果没有创建它。
我已经阅读了 ping 方法,但我不知道如何在这里实现它。 我还阅读了有关 DBIx 并尝试了以下实现:

sub connect_db {
use DBIx::Connector;

my $self = shift;

return 0 unless $self;

my $dbh;

if (exists($config->{$self})) { 
    my $conn = DBIx::Connector->new($config->{$self}->{host}, $config->{$self}->{user}, $config->{$self}->{pass}, {
        RaiseError => 1,
        AutoCommit => 1,
    });
    return $dbh = $conn->dbh; ;
} else {
    return "Connection not in config. \n" ;
}
return $dbh; 
}   

这个实现能达到我想要的吗?当数据库连接处于活动状态以供使用且未重新创建时。如果是,像 Dbi 一样使用 DBIx 是否安全?我这里指的是:

my $dbh  = $conn->dbh;
$dbh->do('INSERT INTO foo (name) VALUES (?)', undef, 'Fred' );
and not 
$conn->run(fixup => sub {
$_->do('INSERT INTO foo (name) VALUES (?)', undef, 'Fred' );
});

谢谢

【问题讨论】:

    标签: perl dbi mojolicious


    【解决方案1】:

    在 DBIx::Connector 对象中使用 dbh 方法是安全的,然后将其用作普通 DBI 句柄,但如果不保留 DBIx::Connector 的大部分好处,您将错过DBIx::Connector 对象。创建一个新的 DBIx::Connector 对象意味着需要一个新的连接,因为该对象是缓存连接的地方。此外,每当您调用->dbh->run 时,DBIx::Connector 都会检查连接是否处于活动状态并且您尚未分叉,并在需要时建立新连接。因此最好存储和传递 DBIx::Connector 对象,然后让任何单独的代码段检索dbh 或调用run。在 Mojolicious 应用程序中,通常会将这样的内容存储在应用程序 helper 中(当然,您可以将其放在任何方便的地方)。

    # in application startup
    my %dbs;
    $app->helper(db => sub {
      my ($c, $name) = @_;
      return $dbs{$name} //= DBIx::Connector->new(...);
    });
    
    # elsewhere
    my $dbh = $c->db('db1')->dbh;
    # or
    $c->db('db1')->run(...);
    

    【讨论】:

    • 感谢您的回复,以确保我理解。如果我有这个 sub connect_db { .. 与上面相同,直到 ... my $conn = DBIx::Connector->new(...);返回 $conn ; ... } 在我的控制器中,我使用我的 $conn = connect_db('db1');我的 $dbh = $conn->dbh();我会实现我想要做的事情吗?谢谢
    • 不,这仍然会创建一个新的 DBIx::Connector 对象,因此每次调用 connect_db 时都需要重新连接。
    • 所以正确的方法是创建一个如您所展示的助手并在控制器中使用 $dbh = $c->db->dbh?
    • 是的,或者以其他方式存储您的 DBIx::Connector 对象。重要的部分是您希望始终为每个要使用的请求保留相同的 DBIx::Connector 对象。
    • 我在设置时遇到问题,我已经在此粘贴箱链接pastebin.com/2CqugMrz 上添加了我的尝试,因为我无法在 cmets 中格式化...你能看看并发布答案吗如果它是正确的?
    【解决方案2】:

    这是DBI::connect_cached应该解决的问题:

    $dbh = DBI->connect_cached($data_source, $username, $password)
          or die $DBI::errstr;
    $dbh = DBI->connect_cached($data_source, $username, $password, \%attr)
          or die $DBI::errstr;
    

    connect_cached 类似于“connect”,只是返回的数据库句柄也存储在与给定参数关联的散列中。如果使用相同的参数值再次调用connect_cached,则如果仍然有效,则将返回相应的缓存$dbh。如果缓存的数据库句柄已断开或ping 方法失败,则将其替换为新连接。

    有关重要的免责声明,请参阅文档。

    【讨论】:

    • 感谢回复,我在发布之前已阅读此内容,但认为它不是我的应用程序的最佳解决方案,因为我连接到 3 个不同的 dbs/ip,并且在 connect_cached 的文档中说:缓存连接可能很有用在某些应用程序中,但它也可能导致问题,例如连接过多,因此应谨慎使用。特别是,避免更改通过 connect_cached() 创建的数据库句柄的属性,因为它会影响可能使用相同句柄的其他代码。当 connect_cached() 返回一个句柄时,属性将被重置为其初始值。
    • 我看不到您引用的评论,@rooger,说明您无法使用 connect_cached 的任何原因。三个不同的数据库将使用三个不同的缓存。与使用 DBIx::Connector 相比,使用 connect_cached 不会使“连接过多”问题变得更糟。对我来说似乎是安全的,除非你在连接后真的改变了句柄。
    • 感谢您的快速回复,因此,如果索引上的用户刷新页面 50 次,则通过使用 connect_cache (在我的 connect_db 子中返回 $dbh = DBI->connect_cached(...))只会建立 1 个连接吗?有没有办法检查与 SQL Server 建立了多少连接?另外,有没有办法实现ping?我在想类似 return $dbh = DBI->connect(...) 除非 $dbh->ping();但这无论如何都会在进行 ping 之前建立连接...
    • LE: 在网上看了一些资料后,越来越多的人说 DBIx::Connector 比 connect_cached 好 justatheory.com/tags/dbixconnector metacpan.org/pod/DBIx::Connector - 引用“你可能熟悉...和DBI 的 connect_cached() 构造函数。DBIx::Connector 满足类似的需求,但做得更好。 reddit.com/r/perl/comments/54nl6c/… - “除此之外,建议使用像 DBIx::Connector 这样的连接管理器,而不是依赖于 connect_cached ”。
    • 你有什么意见?对我来说,dbix 连接器的唯一缺点是它最后一次更新是在 2016 年
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    • 2013-04-25
    • 2014-07-05
    • 2012-08-06
    相关资源
    最近更新 更多