用Perl访问BerkeleyDB的正确方法是什么?

| 我在使用BerkeleyDB时遇到了一些问题。我有多个相同代码的实例指向一个DB文件存储库,并且在5-32小时内一切正常,然后突然出现了死锁。该命令提示在执行db_get或db_put或游标创建调用之前立即停止。因此,我只是在寻求处理这些呼叫的正确方法。这是我的总体布局: 这是创建环境和数据库的方式:
my $env = new BerkeleyDB::Env ( 
   -Home   => \"$dbFolder\\\\\" , 
   -Flags  => DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL) 
   or die \"cannot open environment: $BerkeleyDB::Error\\n\";

my $unsortedHash  = BerkeleyDB::Hash->new (
   -Filename => \"$dbFolder/Unsorted.db\", 
   -Flags => DB_CREATE,
   -Env  => $env
   ) or die \"couldn\'t create: $!, $BerkeleyDB::Error.\\n\";
该代码的单个实例运行,转到站点并保存要由另一个实例解析的URL(我设置了标志,以便在锁定一个数据库时每个DB都被锁定):
        $lk = $unsortedHash->cds_lock();
        while(@urlsToAdd){
            my $currUrl = shift @urlsToAdd;
            $unsortedHash->db_put($currUrl, \'0\');
        }
        $lk->cds_unlock();
它会定期检查“未排序”中是否有一定数量的项目:
$refer = $unsortedHash->db_stat();
$elements = $refer->{\'hash_ndata\'};
在将任何元素添加到任何数据库之前,它首先检查所有数据库以查看该元素是否已经存在:
if ($unsortedHash->db_get($search, $value) == 0){
    $value = \"1:$value\";
}elsif ($badHash->db_get($search, $value) == 0){
    $value =  \"2:$value\";
....
接下来的下一个代码,它的许多实例并行运行。首先,它获取未排序的下一项(不具有繁忙值\'1 \'),然后将该值设置为繁忙\'1 \',然后对其进行处理,然后将数据库条目完全移至另一个数据库(从未排序的数据库中删除并存储在另一个数据库中):
my $pageUrl = \'\';
my $busy = \'1\';
my $curs;
my $lk = $unsortedHash->cds_lock(); #lock, change status to 1, unlock
########## GET AN ELEMENT FROM THE UNSORTED HASH #######
while(1){
    $busy = \'1\';
    $curs = $unsortedHash->db_cursor();
    while ($busy){
        $curs->c_get($pageUrl, $busy, DB_NEXT);
        print \"$pageUrl:$busy:\\n\";
        if ($pageUrl eq \'\'){
            $busy = 0;
        }
    }
    $curs->c_close();
    $curs = undef;

    if ($pageUrl eq \'\'){
        print \"Database empty. Sleeping...\\n\";
        $lk->cds_unlock();
        sleep(30);
        $lk = $unsortedHash->cds_lock();
    }else{
        last;
    }
}

####### MAKE THE ELEMENT \'BUSY\' AND DOWNLOAD IT 


$unsortedHash->db_put($pageUrl, \'1\');
$lk->cds_unlock();
$lk = undef;
在其他任何地方,如果我在ANY DB上调用db_put或db_del,它都将像这样被锁包裹:
print \"\\n\\nBad.\\n\\n\";
        $lk = $badHash->cds_lock();
        $badHash->db_put($pageUrl, \'0\');
        $unsortedHash->db_del($pageUrl);
        $lk->cds_unlock();
        $lk = undef;
但是,我的db_get命令是自由浮动的,没有锁,因为我认为读取不需要锁。 我已经看了上百万遍此代码,并且算法是密不透风的。因此,我只是想知道是否要实现此错误的任何部分,使用错误的锁定等。或者是否存在更好的方法来防止BerkeleyDB和Strawberry Perl发生死锁(甚至诊断死锁)? 更新:更具体地说,问题是在Windows 2003服务器(1.5 GB RAM,不确定是否重要)上发生的。我可以在Windows 7计算机(4GB RAM)上很好地运行整个安装程序。我还开始使用以下命令打印出锁定统计信息: 将此标志添加到环境创建中:
-MsgFile => \"$dbFolder/lockData.txt\"
然后每60秒调用一次:
my $status = $env->lock_stat_print();
print \"Status:$status:\\n\";
状态始终返回为0,表示成功。这是最近的统计报告:
29  Last allocated locker ID
0x7fffffff  Current maximum unused locker ID
5   Number of lock modes
1000    Maximum number of locks possible
1000    Maximum number of lockers possible
1000    Maximum number of lock objects possible
40  Number of lock object partitions
24  Number of current locks
42  Maximum number of locks at any one time
5   Maximum number of locks in any one bucket
0   Maximum number of locks stolen by for an empty partition
0   Maximum number of locks stolen for any one partition
29  Number of current lockers
29  Maximum number of lockers at any one time
6   Number of current lock objects
13  Maximum number of lock objects at any one time
1   Maximum number of lock objects in any one bucket
0   Maximum number of objects stolen by for an empty partition
0   Maximum number of objects stolen for any one partition
3121958 Total number of locks requested
3121926 Total number of locks released
0   Total number of locks upgraded
24  Total number of locks downgraded
9310    Lock requests not available due to conflicts, for which we waited
0   Lock requests not available due to conflicts, for which we did not wait
8   Number of deadlocks
1000000 Lock timeout value
0   Number of locks that have timed out
1000000 Transaction timeout value
0   Number of transactions that have timed out
792KB   The size of the lock region
59  The number of partition locks that required waiting (0%)
46  The maximum number of times any partition lock was waited for (0%)
0   The number of object queue operations that required waiting (0%)
27  The number of locker allocations that required waiting (0%)
0   The number of region locks that required waiting (0%)
1   Maximum hash bucket length
我对此保持警惕:
8   Number of deadlocks
这些僵局是如何发生的,如何解决? (代码的所有部分仍在运行)。在这种情况下,僵局到底是什么?     
已邀请:
简而言之,您需要执行死锁检测。我可以看到两种可能性。首先,您可以使用
db_deadlock
实用程序。其次,也许更方便,您可以在打开环境时指定
-LockDetect
标志,该标志在Perl文档中对于
BerkeleyDB.pm
并没有完全解释。 在4.5.20版中,这两种方式对我来说似乎都可以正常工作。 (顺便问一下,您的版本是?) 现在为细节。 指定
-LockDetect
标志就是这样。有两个值可供选择。我选择了
DB_LOCK_DEFAULT
,看来效果很好。有了关于正在发生的事情的更多线索,您当然可以得到更多的幻想。 运行
db_deadlock
实用程序可以像这样完成:
db_deadlock -h your/env/dir -v -t 3   # run as daemon, check every 3 seconds
db_deadlock -h your/env/dir -v        # run once
这是ѭ10手册的引文:   只要有多个线程或进程正在访问数据库,并且其中至少有一个正在修改它,则该实用程序应作为后台守护程序运行,或者应以其他方式调用基础的Berkeley DB死锁检测接口。 我得出的结论是,通过反复与两位作者和一位读者进行测试,两种方法都可以正常工作,这会导致两次死锁,同时将新条目快速连续(每秒100个)放入数据库中,或者通过游标数据库中所有键的数量。 标志方法似乎可以非常快速地处理死锁,在我的测试中它们并没有引起注意。 另一方面,用脚本在Parlles中运行带有详细输出的
db_deadlock
实用程序是很有启发性的,因为您可以看到它们如何阻止,然后在中止了储物柜后继续执行,尤其是与
db_stat
实用程序结合使用时:
db_stat -Cl # Locks grouped by lockers
db_stat -Co # Locks grouped by object
db_stat -Cp # need_dd = 1 ?
db_stat -CA # all of the above plus more
我缺乏解释所有细节的专业知识,但是您可以看到,在受阻情况下,其中有某些条目,而在其他情况下,则没有。另请参阅《伯克利DB程序员参考指南》中标题为“伯克利DB并行数据存储锁定约定(ѭ21”?)的部分。 您正在问这些僵局是如何发生的。不能确切地说,但是我确实看到它们是在并发访问中发生的。您还问他们是如何解决的。我不知道。在我的测试方案中,被阻止的脚本只会挂起。也许在您的情况下有人在您不知情的情况下运行了死锁检测? 为了完整起见,您的应用程序可能只是挂起,因为线程在退出之前尚未关闭资源。如果仅按Ctrl-C进程并且没有适当的清理处理程序来关闭资源,则可能会发生。但这似乎不是您的问题。 如果确实成为问题,则应查看《参考指南》中有关“数据存储和并行数据存储”应用程序中的处理失败的部分。 CDS和DS没有恢复的概念。由于CDS和DS不支持事务,也不维护恢复日志,因此它们无法运行恢复。如果数据库在DS或CDS中损坏,则只能删除它并重新创建它。 (从Himanshu Yadava的《伯克利DB图书》中逐字删除。) 最后,在Oracle网站上有一些视频教程,其中包括Margo Seltzer使用CDS的视频教程。     
但是,我的db_get命令是自由浮动的,没有锁,因为我认为读取不需要锁。 这个假设是错误的。正如http://pybsddb.sourceforge.net/ref/lock/page.html所述,BerkeleyDB必须在内部发出读取锁,因为如果读者尝试读取从其下方更改的数据,则可能会出现不确定的行为。因此,读取很容易陷入僵局。 在存在游标的情况下尤其如此。读游标会保持对所有已读内容的锁定,直到关闭游标为止。有关更多详细信息,请参见http://pybsddb.sourceforge.net/ref/lock/am_conv.html,该方法可以使您陷入僵局(实际上您甚至可以自己陷入僵局)。     
虽然不是BerkeleyDB解决方案,但是您可以通过Win32 :: Mutex使用底层Windows互斥锁来使用替代锁定。下面是一个非常简单的示例:
#!perl -w
use strict;
use warnings;

use Win32::Mutex; # from Win32::IPC

my $mutex = Win32::Mutex->new(0, \'MyAppBerkeleyLock\');

for (1..10) {
    $mutex->wait(10*1000) or die \"Failed to lock mutex $!\";
    print \"$$ has lock\\n\";
    sleep(rand(7));
    $mutex->release();
}
    

要回复问题请先登录注册