天天看點

Berkeley DB Java Edition使用說明

在使用Berkeley DB c++ Edition的時候發現有些接口不是太了解,發現網上好多都是Java版本的資訊,對付發現他們的命名基本相同。是以轉載一下以便以後查找。

一、 簡介 

Berkeley DB Java Edition (JE)是一個完全用JAVA寫的,它适合于管理海量的,簡單的資料。 

l 能夠高效率的處理1到1百萬條記錄,制約JE資料庫的往往是硬體系統,而不是JE本身。 

l 多線程支援,JE使用逾時的方式來處理線程間的死瑣問題。 

l Database都采用簡單的key/value對應的形式。 

l 事務支援。 

l 允許建立二級庫。這樣我們就可以友善的使用一級key,二級key來通路我們的資料。 

l 支援RAM緩沖,這樣就能減少頻繁的IO操作。 

l 支援日志。 

l 資料備份和恢複。 

l 遊标支援。 

二、 擷取JE 

JE下載下傳位址: 

http://www.oracle.com/technology/software/products/berkeley-db/je/index.html 

解開包後 把JE_HOME/lib/je-<version>.jar 中的jar檔案添加到你的環境變量中就可以使用je了。 

相關幫助文檔可以參考 JE_HOME/docs/index.html 

源代碼見JE_HOME/src/*.* 

三、 JE常見的異常 

DatabaseNotFoundException 當沒有找到指定的資料庫的時候會傳回這個異常 

DeadlockException 線程間死鎖異常 

RunRecoveryException 回收異常,當發生此異常的時候,你必須得重新打開環境變量。 

四、 關于日志檔案必須了解的六項 

JE的日志檔案跟其他的資料庫的日志檔案不太一樣,跟C版的DBD也是有差別的 

l JE的日志檔案隻能APPEND,第一個日志檔案名是 00000000.jdb,當他增長到一定大小的時候(預設是10M),開始寫第二個日志檔案00000001.jdb,已此類推。 

l 跟C版本有所不同,JE的資料日志和事務日志是放在一起的,而不是分開放的。 

l JE cleaner負責清掃沒用到的磁盤空間,删除後,或者更新後新的記錄會追加進來,而原有的記錄空間就不在使用了,cleaner負責清理不用的空間。 

l 清理并不是立即進行的,當你關閉你的資料庫環境後,通過調用一個cleaner方法來清理。 

l 清理也不是隻動執行的,需要你自己手動調用cleaner 方法來定時清理的。 

l 日志檔案的删除僅發生在檢查點之後。cleaner準備出哪些log 檔案需要被删除,當檢查點過後,删掉一些不在被使用的檔案。每寫20M的日志檔案就執行一次檢查點,預設下。 

五、 建立資料庫環境 

JE要求在任何DATABASE操作前,要先打開資料庫環境,就像我們要使用資料庫的話必須得先建立連接配接一樣。你可以通過資料庫環境來建立和打開database,或者更改database名稱和删除database. 

可以通過Environments對象來打開環境,打開環境的時候設定的目錄必須是已經存在的目錄,否則會出錯誤。預設情況下,如果指定的database不存在則不會自動建立一個新的detabase,但可以通過設定setAllowCreate來改變這一情況。 

1. 打開database環境 

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17      
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
 
...
 
Environment myDbEnvironment = null;
 
try {
    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setAllowCreate(true);//如果不存在則建立一個
    myDbEnvironment = new Environment(new File("/export/dbEnv"), envConfig);
} catch (DatabaseException dbe) {
    // 錯誤處理
}      

2. 關閉database環境 

可以通過Environment.close()這個方法來關閉database環境,當你完成資料庫操作後一定要關閉資料庫環境。 

示例:

1
2
3
4
5
6
7
8
9
10
11
12      
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
 
...
 
try {
    if (myDbEnvironment != null) {
        myDbEnvironment.close();
    } 
} catch (DatabaseException dbe) {
    // Exception handling goes here
}      

3. 清理日志 

通常在關閉資料庫連接配接的時候,有必要清理下日志,用以釋放更多的磁盤空間。我們可以在Environment.close前執行下Environment.cleanLog()來達到此目的。 

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13      
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
 
...
 
try {
    if (myDbEnvironment != null) {
        myDbEnvironment.cleanLog(); // 在關閉環境前清理下日志
        myDbEnvironment.close();
    } 
} catch (DatabaseException dbe) {
    // Exception handling goes here
}      

4. Database環境的配置 

可以通過EnvironmentConfig這個對象來配置database環境。如果想得到目前環境的配置資訊則可以通過Environment.getConfig()方法得到目前環境的配置資訊。 

也可以使用EnvironmentMutableConfig來配置環境,其實 EnvironmentConfig是EnvironmentMutableConfig的子類,是以EnvironmentMutableConfig能夠使用的設定,EnvironmentConfig也同樣能夠使用。 

如果你要擷取目前環境的使用情況,那麼你可以通過使用EnvironmentStats.getNCacheMiss().來監視RAM cache命中率。EnvironmentStats可以由Environment.getStats()方法擷取。 

EnvironmentConfig常見方法介紹 

l EnvironmentConfig.setAllowCreate() ; 

如果設定了true則表示當資料庫環境不存在時候重新建立一個資料庫環境,預設為false. 

l EnvironmentConfig.setReadOnly() 

以隻讀方式打開,預設為false. 

l EnvironmentConfig.setTransactional() 

事務支援,如果為true,則表示目前環境支援事務處理,預設為false,不支援事務處理。 

EnvironmentMutableConfig的介紹 

l setCachePercent() 

設定目前環境能夠使用的RAM占整個JVM記憶體的百分比。 

l setCacheSize() 

設定目前環境能夠使用的最大RAM。機關BYTE 

l setTxnNoSync() 

當送出事務的時候是否把緩存中的内容同步到磁盤中去。 

true 表示不同步,也就是說不寫磁盤 

l setTxnWriteNoSync() 

當送出事務的時候,是否把緩沖的log寫到磁盤上 

true 表示不同步,也就是說不寫磁盤 

示例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21      
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
 
...
 
Environment myDatabaseEnvironment = null;
try {
    EnvironmentConfig envConfig = new EnvironmentConfig();
    //當環境不存在的時候自動建立環境
       envConfig.setAllowCreate(true);
       //設定支援事務
       envConfig.setTransactional(true);
    myDatabaseEnvironment = 
        new Environment(new File("/export/dbEnv"), envConfig);
} catch (DatabaseException dbe) {
   System.err.println(dbe.toString());
   System.exit(1);
}      

示例二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15      
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentMutableConfig;
import java.io.File;
...
try {
    Environment myEnv = new Environment(new File("/export/dbEnv"), null);
    EnvironmentMutableConfig envMutableConfig = 
        new EnvironmentMutableConfig();
    envMutableConfig.setTxnNoSync(true);
    myEnv.setMutableConfig(envMutableConfig); 
} catch (DatabaseException dbe) {
    // Exception handling goes here
}      

示例三:

1
2
3
4
5      
import com.sleepycat.je.Environment;
...
//沒有命中的CACHE
long cacheMisses = myEnv.getStats(null).getNCacheMiss();
...      

5. Database操作 

在BDB中,資料是以key/value方式成隊出現的。 

打開database 

可以通過environment.openDatabase()方法打開一個database,在調用這個方法的時候必須指定database的名稱。和databaseConfig() (注:資料庫設定) 

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26      
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
...
Environment myDbEnvironment = null;
Database myDatabase = null;
...
try {
    // 打開一個環境,如果不存在則建立一個
    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setAllowCreate(true);
    myDbEnvironment = new Environment(new File("/export/dbEnv"), envConfig);
 
    // 打開一個資料庫,如果資料庫不存在則建立一個
    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setAllowCreate(true);
    myDatabase = myDbEnvironment.openDatabase(null, 
"sampleDatabase", dbConfig); //打開一個資料庫,資料庫名為
                                   //sampleDatabase,資料庫的配置為dbConfig
} catch (DatabaseException dbe) {
    // 錯誤處理
}      

關閉database 

通過調用Database.close()方法來關閉資料庫,但要注意,在關閉資料庫前必須得先把遊标先關閉。 

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14      
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Database;
import com.sleepycat.je.Environment;
...
try {
        if (myDatabase != null) {
            myDatabase.close();
        }
        if (myDbEnvironment != null) {
            myDbEnvironment.close();
        }
} catch (DatabaseException dbe) {
    // 錯誤處理
}      

設定資料庫屬性 

其實設定資料庫屬性跟設定環境屬性差不多,JE中通過DatabaseConfig對象來設定資料庫屬性。你能夠設定的資料庫屬性如下。 

l DatabaseConfig.setAllowCreate() 

如果是true的話,則當不存在此資料庫的時候建立一個。 

l DatabaseConfig.setBtreeComparator() 

設定用于Btree比較的比較器,通常是用來排序 

l DatabaseConfig.setDuplicateComparator() 

設定用來比較一個key有兩個不同值的時候的大小比較器。 

l DatabaseConfig.setSortedDuplicates() 

設定一個key是否允許存儲多個值,true代表允許,預設false. 

l DatabaseConfig.setExclusiveCreate() 

以獨占的方式打開,也就是說同一個時間隻能有一執行個體打開這個database。 

l DatabaseConfig.setReadOnly() 

以隻讀方式打開database,預設是false. 

l DatabaseConfig.setTransactional() 

如果設定為true,則支援事務處理,預設是false,不支援事務。 

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19      
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
...
// Environment open omitted for brevity
...
Database myDatabase = null;
try {
    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setAllowCreate(true);
    dbConfig.setSortedDuplicates(true);
    myDatabase = 
        myDbEnv.openDatabase(null, 
                             "sampleDatabase", 
                             dbConfig); 
} catch (DatabaseException dbe) {
    // Exception handling goes here.
}      

一些用來管理的方法 

l Database.getDatabaseName() 

取得資料庫的名稱 

如:String dbName = myDatabase.getDatabaseName(); 

l Database.getEnvironment() 

取得包含這個database的環境資訊 

如:Environment theEnv = myDatabase.getEnvironment(); 

l Database.preload() 

預先加載指定bytes的資料到RAM中。 

如:myDatabase.preload(1048576l); // 1024*1024 

l Environment.getDatabaseNames() 

傳回目前環境下的資料庫清單 

如:

1
2
3
4
5      
import java.util.List;
List myDbNames = myDbEnv.getDatabaseNames();
for(int i=0; i < myDbNames.size(); i++) {
    System.out.println("Database Name: " + (String)myDbNames.get(i));
}      

l Environment.removeDatabase() 

删除目前環境中指定的資料庫。 

如:

1
2
3      
String dbName = myDatabase.getDatabaseName();
myDatabase.close();
myDbEnv.removeDatabase(null, dbName);      

l Environment.renameDatabase() 

給目前環境下的資料庫改名 

如:

1
2
3
4      
String oldName = myDatabase.getDatabaseName();   
String newName = new String(oldName + ".new", "UTF-8");
myDatabase.close();
myDbEnv.renameDatabase(null, oldName, newName);      

l Environment.truncateDatabase() 

清空database内的所有資料,傳回清空了多少條記錄。 

如:

1
2
3      
Int numDiscarded= myEnv.truncate(null,
myDatabase.getDatabaseName(),true); 
System.out.println("一共删除了 " + numDiscarded +" 條記錄 從資料庫 " + myDatabase.getDatabaseName());      

6. Database 記錄 

JE的記錄包含兩部分,key鍵值和value資料值,這兩個值都是通過DatabaseEntry對象封裝起來,是以說如果要使用記錄,則你必須建立兩個DatabaseEntry對象,一個是用來做為key,另外一個是做為value. 

DatabaseEntry能夠支援任何的能夠轉換為bytes數組形式的基本資料。包括所有的JAVA基本類型和可序列化的對象. 

使用記錄 

示例一:把字元串轉換DatabaseEntry

1
2
3
4
5
6
7
8
9
10
11
12      
package je.gettingStarted;
import com.sleepycat.je.DatabaseEntry;
...
String aKey = "key"; 
String aData = "data";
try {
 //設定key/value,注意DatabaseEntry内使用的是bytes數組
 DatabaseEntry theKey=new DatabaseEntry(aKey.getBytes("UTF-8"));
 DatabaseEntry theData=new DatabaseEntry(aData.getBytes("UTF-8"));
} catch (Exception e) {
    // 錯誤處理
}      

示例二:把DatabaseEntry裡的資料轉換成字元串

1
2
3
4      
byte[] myKey = theKey.getData();
byte[] myData = theData.getData();
String key = new String(myKey, "UTF-8");
String data = new String(myData, "UTF-8");      

讀和寫database 記錄 

讀和寫database記錄的時候大體是基本一樣的,唯一有差別的是每個key寫是否允許寫多條記錄,預設情況下是不支援多條記錄的。 

a) 你可以使用如下方法向database 裡添加記錄 

l Database.put() 

向database中添加一條記錄。如果你的database不支援一個key對應多個data或目前database中已經存在該key了,則使用此方法将使用新的值覆寫舊的值。 

l Database.putNoOverwrite() 

向database中添加新值但如果原先已經有了該key,則不覆寫。不管database是否允許支援多重記錄(一個key對應多個value),隻要存在該key就不允許添加,并且傳回perationStatus.KEYEXIST資訊。 

l Database.putNoDupData() 

想database中添加一條記錄,如果database中已經存在了相同的 key和value則傳回 OperationStatus.KEYEXIST. 

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13      
package je.gettingStarted;
    import com.sleepycat.je.Database;
    import com.sleepycat.je.DatabaseEntry;
    ...
    String aKey = "myFirstKey";
    String aData = "myFirstData";
    try {
        DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
        DatabaseEntry theData = new DatabaseEntry(aData.getBytes("UTF-8"));
        myDatabase.put(null, theKey, theData);
    } catch (Exception e) {
        // Exception handling goes here
    }      

b) 你可以使用如下方法從database 裡讀取記錄 

1. Database.get() 

基本的讀記錄的方法,通過key的方式來比對,如果沒有改記錄則傳回OperationStatus.NOTFOUND。 

l Database.getSearchBoth() 

通過key和value來同時比對,同樣如果沒有記錄比對key和value則會傳回OperationStatus.NOTFOUND。 

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24      
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
...
String aKey = "myFirstKey";
 
try {
    DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();
    
    if (myDatabase.get(null, theKey, theData, LockMode.DEFAULT) ==
        OperationStatus.SUCCESS) {
        byte[] retData = theData.getData();
        String foundData = new String(retData, "UTF-8");
        System.out.println("For key: '" + aKey + "' found data: '" + 
                            foundData + "'.");
    } else {
        System.out.println("No record found for key '" + aKey + "'.");
    } 
} catch (Exception e) {
    // Exception handling goes here
}      

c) 删除記錄 

可以使用Database.delete()這個方法來删除記錄。如果你的database支援多重記錄,則目前key下的所有記錄都會被删除,如果隻想删除多重記錄中的一條則可以使用遊标來删除。 

當然你也可以使用Environment.truncateDatabase()這個方法來清空database 中的所有記錄。 

使用示例:

1
2
3
4
5
6
7
8
9
10      
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
...
try {
    String aKey = "myFirstKey";
    DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
    myDatabase.delete(null, theKey); 
} catch (Exception e) {
}      

d) 送出事務 

當你對database進行了寫操作的時候,你的修改不一定馬上就能生效,有的時候他僅僅是緩存在RAM中,如果想讓你的修改立即生效,則可以使用Environment.sync()方法來把資料同步到磁盤中去。 

e) 不同類型的資料的處理 

1. 你可以使用DatabaseEntry來綁定基本的JAVA資料類型,主要有String、Character、Boolean、Byte、Short、Integer、Long、Float、Double. 

使用示例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19      
package je.gettingStarted;
              import com.sleepycat.bind.EntryBinding;
              import com.sleepycat.bind.tuple.TupleBinding;
              import com.sleepycat.je.DatabaseEntry;
              ...
              try {
                  String aKey = "myLong";
                  DatabaseEntry theKey = new
                  DatabaseEntry(aKey.getBytes("UTF-8"));    
 
                  Long myLong = new Long(123456789l);
                  DatabaseEntry theData = new DatabaseEntry();
                  EntryBinding myBinding =        
                  TupleBinding.getPrimitiveBinding(Long.class);
                  myBinding.objectToEntry(myLong, theData);
                  myDatabase.put(null, theKey, theData);
              } catch (Exception e) {
                  // Exception handling goes here
              }      

使用示例二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30      
package je.gettingStarted;
       import com.sleepycat.bind.EntryBinding;
       import com.sleepycat.bind.tuple.TupleBinding;
       import com.sleepycat.je.Database;
       import com.sleepycat.je.DatabaseEntry;
       import com.sleepycat.je.LockMode;
       import com.sleepycat.je.OperationStatus;
       ...
       Database myDatabase = null;
       try {
           String aKey = "myLong";
           DatabaseEntry theKey = new 
              DatabaseEntry(aKey.getBytes("UTF-8"));
           DatabaseEntry theData = new DatabaseEntry();
           EntryBinding myBinding =        
              TupleBinding.getPrimitiveBinding(Long.class);
           OperationStatus retVal = myDatabase.get(null, theKey, theData, 
              LockMode.DEFAULT);
           String retKey = null;
           if (retVal == OperationStatus.SUCCESS) {
        Long theLong = (Long) myBinding.entryToObject(theData);
               retKey = new String(theKey.getData(), "UTF-8");
               System.out.println("For key: '" + retKey + "' found Long: '" + 
                            theLong + "'.");
           } else {
               System.out.println("No record found for key '" + retKey + "'.");
           }
       } catch (Exception e) {
           // Exception handling goes here
       }      

From:http://cllovelf.blog.chinajavaworld.com/entry/282