2011-04-22 48 views
6

我一直在使用BerkeleyDB有一些問題。我有相同代碼的多個實例指向單個數據庫文件存儲庫,並且一切運行良好5-32小時,然後突然間出現死鎖。在執行db_get或db_put或遊標創建調用之前,命令會提示停止。所以我只是要求正確的方式來處理這些電話。這裏是我的總體佈局:用Perl訪問BerkeleyDB的正確方法是什麼?

這是怎樣的環境和DB創建:

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"; 

這個代碼的單個實例運行,去一個網站,網址保存到另一個實例解析(我有的標誌設置,使得當一個被鎖定的每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',然後對其執行操作,然後將數據庫條目完全移到另一個數據庫中從無序刪除並存儲在另一個DB):

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; 

,並在每其他地方,如果我叫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死鎖(甚至診斷死鎖)?

UPDATE:具體而言,該問題發生在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 

怎麼會出現這些死鎖和他們怎麼解決的? (代碼的所有部分仍在運行)。在這種情況下究竟是一個死鎖?

回答

3

總之,你需要做死鎖檢測。我可以看到兩種可能性。首先,您可以使用db_deadlock utility。其次,也許更方便的是,您可以在打開環境時指定-LockDetect標誌,該標誌在Perl docs for BerkeleyDB.pm中的深度標識不完全清楚。

在4.5.20版本中,這兩種方式似乎都適合我。 (順便問一句,你的version是什麼?)

現在的細節。

指定-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 

下面是來自db_deadlock手動報價:

該實用程序應該運行作爲後臺守護進程,或底層的Berkeley DB應該以其他方式調用死鎖檢測接口,只要有多個線程或進程訪問數據庫並且其中至少有一個正在修改它。

我得出的結論是這兩種方式都通過反覆進行兩個作家,一個閱讀器的測試,而在快速連續在數據庫中把新的條目(每秒100條),這將死鎖幾次做工精細,或者通過數據庫中所有鍵的遊標。

該標誌方法似乎很快處理死鎖,它們在我的測試中並沒有變得明顯。

在另一方面,運行db_deadlock實用與paralles詳細輸出與腳本是在你看到他們是如何阻止,然後繼續後,當與db_stat utility結合儲物櫃已被中止,尤其是有啓發:

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 

我缺乏解釋所有細節的專業知識,但是您可以看到,在被阻止的情況下,某些條目存在,而在其他情況下則沒有。另請參閱Berkeley DB Programmer's Reference Guide中標題爲Berkeley DB Concurrent Data Store locking conventions(什麼是IWRITE?)。

你在問這些死鎖是如何發生的。不能確切地說,但我確實看到他們與併發訪問發生。你也在問他們是如何解決的。我不知道。在我的測試場景中,被阻止的腳本會簡單地掛起。也許在你的場景中,有人在你不知情的情況下運行死鎖檢測?

爲了完整性,您的應用程序可能會簡單地將掛起,因爲線程在退出之前沒有關閉資源。如果您只是按Ctrl-C進程並且沒有清理處理程序來關閉資源,則可能會發生。但這似乎不是你的問題。

如果確實成爲您的問題,您應該參閱參考指南中的Handling failure in Data Store and Concurrent Data Store applications部分。

CDS和DS沒有恢復的概念。由於CDS和DS不支持事務並且不維護恢復日誌,因此它們無法運行恢復。如果數據庫在DS或CDS中損壞,則只能將其刪除並重新創建。 (從Berkeley DB Book by Himanshu Yadava中逐字逐字讀取。)

最後,在Oracle站點上有視頻教程,其中包括one on using CDS by Margo Seltzer

1

雖然不是BerkeleyDB解決方案,但您可以使用替代鎖定,但使用基礎Windows互斥鎖的Win32 :: Mutex。一個很簡單的例子如下:

#!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(); 
} 
+0

對於Mutex的工作,特別是與伯克利有關的工作,我有點困惑,你能舉一個例子說明如何使用Mutex將項目放入數據庫?它如何防止其他進程訪問數據庫? – 2011-05-02 21:03:53

+1

這是windows所具有的「事物」,機器上任何進程中的任何線程都可以嘗試鎖定。 wait()調用會阻塞,直到線程鎖定。然後,您將完成所有數據庫訪問,而不是我的sleep()調用。基本上,你做的是鎖定而不是BDB。嘗試運行上面的腳本的幾個進程,看看它是如何工作的。 – Alex 2011-05-02 23:35:00

4

但是,我db_get命令是自由浮動的,沒有鎖,因爲我不認爲閱讀需要一個鎖。

這個假設是錯誤的。正如http://pybsddb.sourceforge.net/ref/lock/page.html所說,BerkeleyDB必須在內部發出讀鎖,否則如果讀者試圖讀取正在從其下面更改的數據,您可能會得到未定義的行爲。因此讀取可能很容易成爲死鎖情況的一部分。

這在遊標的存在下尤其如此。讀取遊標在讀取的所有內容上保持鎖定,直到遊標關閉。有關更多詳細信息,請參見http://pybsddb.sourceforge.net/ref/lock/am_conv.html,這些方法可能會導致死鎖(實際上,甚至可能導致自己死鎖)。

相關問題