首页 > 代码库 > Berkeley DB基础教程

Berkeley DB基础教程


一、Berkeley DB的介绍

(1)Berkeley DB是一个嵌入式数据库,它适合于管理海量的、简单的数据。如Google使用其来保存账户信息。Heritrix用其来保存froniter.

(2)key/value是Berkeley DB用来管理数据的基础,每一个key/value对代表一条记录。

(3)Berkeley DB在底层实现採用B树。可以看成可以存储大量数据的HashMap。

(4)它是Oracle公司的一个产品,C++版本号最新出现,之后JAVA等版本号也陆续出现。

它不支持SQL语句。应用程序通过API对数据库进行操作。

下面内容转载至百度文库

Berkeley DB是由美国Sleepycat Software公司开发的一套开放源代码的嵌入式数据库的程序库(database library)。它为应用程序提供可伸缩的、高性能的、有事务保护功能的数据管理服务。

Berkeley DB为数据的存取和管理提供了一组简洁的函数调用API接口。


    它是一个经典的C-library模式的toolkit,为程序猿提供广泛丰富的函数集,是为应用程序开发人员提供工业级强度的数据库服务而设计的。

其主要特点例如以下:
    嵌入式(Embedded):它直接链接到应用程序中,与应用程序执行于相同的地址空间中,因此。不管是在网络上不同计算机之间还是在同一台计算机的不同进程之间,数据库操作并不要求进程间通讯。
    Berkeley DB为多种编程语言提供了API接口,当中包含C、C++、Java、Perl、Tcl、Python和PHP,全部的数据库操作都在程序库内部发生。多个进程。或者同一进程的多个线程可同一时候使用数据库,有如各自单独使用,底层的服务如加锁、事务日志、共享缓冲区管理、内存管理等等都由程序库透明地运行。


    轻便灵活(Portable):它能够执行于差点儿全部的UNIX和Linux系统及其变种系统、Windows操作系统以及多种嵌入式实时操作系统之下。

它在32位和64位系统上均可执行,已经被好多高端的因特网server、台式机、掌上电脑、机顶盒、网络交换机以及其它一些应用领域所採用。一旦Berkeley DB被链接到应用程序中,终端用户一般根本感觉不到有一个数据库系统存在。


    可伸缩(Scalable):这一点表如今非常多方面。Database library本身是非常精简的(少于300KB的文本空间)。但它可以管理规模高达256TB的数据库。它支持高并发度。成千上万个用户可同一时候操纵同一个数据库。Berkeley DB能以足够小的空间占用量执行于有严格约束的嵌入式系统,也可以在高端server上耗用若干GB的内存和若干TB的磁盘空间。


    Berkeley DB在嵌入式应用中比关系数据库和面向对象数据库要好。有下面两点原因:    
    (1)由于数据库程序库同应用程序在同样的地址空间中执行,所以数据库操作不须要进程间的通讯。在一台机器的不同进程间或在网络中不同机器间进行进程通讯所花费的开销。要远远大于函数调用的开销;
    (2)由于Berkeley DB对全部操作都使用一组API接口,因此不须要对某种查询语言进行解析,也不用生成执行计划,大大提高了执行效.


BerkeleyDB系统结构

    Berkeley DB由五个基本的子系统构成.包含: 存取管理子系统、内存池管理子系统、事务子系统、锁子系统以及日志子系统。

当中存取管理子系统作为Berkeley DB数据库进程包内部核心组件,而其它子系统都存在于Berkeley DB数据库进程包的外部。  
    每一个子系统支持不同的应用级别。


    1.数据存取子系统
    数据存取(Access Methods)子系统为创建和訪问数据库文件提供了多种支持。

Berkeley DB提供了下面四种文件存储方法:
   哈希文件、B树、定长记录(队列)和变长记录(基于记录号的简单存储方式)。应用程序能够从中选择最适合的文件组织结构。


   程序猿创建表时能够使用随意一种结构。而且能够在同一个应用程序中对不同存储类型的文件进行混合操作。


    在没有事务管理的情况下,该子系统中的模块可单独使用,为应用程序提供高速高效的数据存取服务。
   数据存取子系统适用于不需事务仅仅需高速格式文件訪问的应用。
    2.内存池管理子系统
    内存池(Memory pool)子系统对Berkeley DB所使用的共享缓冲区进行有效的管理。它同意同一时候訪问数据库的多个进程或者进程的多个线程共享一个快速缓存。负责将改动后的页写回文件和为新调入的页分配内存空间。    它也能够独立于Berkeley DB系统之外,单独被应用程序使用。为其自己的文件和页分配内存空间。

内存池管理子系统适用于须要灵活的、面向页的、缓冲的共享文件訪问的应用。


    3.事务子系统
    事务(Transaction)子系统为Berkeley DB提供事务管理功能。

它同意把一组对数据库的改动看作一个原子单位,这组操作要么全做,要么全不做。在默认的情况下。系统将提供严格的ACID事务属性,可是应用程序能够选择不使用系统所作的隔离保证。该子系统使用两段锁技术和先写日志策略来保证数据库数据的正确性和一致性。

    它也能够被应用程序单独使用来对其自身的数据更新进行事务保护。事务子系统适用于须要事务保证数据的改动的应用。
    
    4.锁子系统
    锁(Locking)子系统为Berkeley DB提供锁机制。为系统提供多用户读取和单用户改动同一对象的共享控制。数据存取子系统可利用该子系统获得对页或记录的读写权限。事务子系统利用锁机制来实现多个事务的并发控制。   该子系统也可被应用程序单独採用。锁子系统适用于一个灵活的、高速的、可设置的锁管理器。
    
    5.日志子系统    
    日志(Logging)子系统採用的是先写日志的策略。用于支持事务子系统进行数据恢复。保证数据一致性。它不大可能被应用程序单独使用,仅仅能作为事务子系统的调用模块。

    以上几部分构成了整个Berkeley DB数据库系统。各部分的关系例如以下图所看到的:
    
    在这个模型中,应用程序直接调用的是数据存取子系统和事务管理子系统,这两个系统进而调用更下层的内存管理子系统、锁子系统和日志子系统。
    
    因为几个子系统相对照较独立,所以应用程序在開始的时候能够指定哪些数据管理服务将被使用。能够所有使用,也能够仅仅用当中的一部分。

比如。假设一个应用程序须要支持多用户并发操作。但不须要进行事务管理,那它就能够
仅仅用锁子系统而不用事务。有些应用程序可能须要高速的、单用户、没有事务管理功能的B树存储结构。那么应用程序能够使锁子系统和事务子系统失效,这样就会降低开销。


BerkeleyDB存储功能概述     
    
    Berkeley DB所管理数据的逻辑组织单位是若干个独立或有一定关系的数据库(database),每个数据库由若干记录组成,这些记录全都被表示成(key,value)的形式.    假设把一组相关的(key。value)对也看作一个表的话,那么每个数据库仅仅同意存放一个table,这一点不同于一般的关系数据库。实际上,在Berkeley DB中所提到的“数据库”,相当于一般关系数据库系统中的表。而“key/data”对相当于关系数据库系统中的行(rows)。Berkeley DB不提供关系数据库中列直接訪问的功能,而是在“key/data”对中的data项中通过实际应用来封装字段(列)。
    在物理组织上,每个数据库在创建的时候能够由应用程序依据其数据特点来选择一种合适的存储结构。可供选择的四种文件存储结构各自是:哈希文件、B树、定长记录(队列)和变长记录(基于记录号的简单存储方式)。


    一个物理的文件里能够仅仅存放一个单独的数据库,也能够存放若干相关或不相关的数据库。并且这些数据库能够分别採用除队列之外随意不同的组织方式,以队列组织的数据库仅仅能单独存放于一个文件,不能同其它存储类型混合存放。


    一个文件除了受最大文件长度和存储空间的约束之外,理论上能够存储随意多个数据库。因此系统定位一个数据库通常须要两个參数——“文件名称”和“数据库名”。这也是Berkeley DB不同于
一般关系数据库的地方。


   Berkeley DB存储系统为应用程序提供了一系列的接口函数,用于对数据库的管理和操作。当中包含:

      (1)数据库的创建、打开、关闭、删除、重命名等,以及对数据的检索和增删改操作;
      (2)提供一些附加的功能,比如读取数据库状态信息、读取所在文件的信息、读取所在数据库环境的信息、清空数据库的内容、数据库的同步备份、版本号升级、提示出错信息等等;
      (3)系统还提供了游标机制,用于存取和訪问成组的数据,以及对两个或多个相关数据库进行关联和等值连接操作;
      (4)系统还给出了一些接口函数用于对存取策略进行优化配置,比方应用程序能够自己设置B树的排序比較函数、每页中存放key的最少数目。哈希桶的填充因子、哈希函数、哈希表最大长度,队列的最大长度,数据库存放的字节顺序,
底层存储页的大小。内存分配函数,快速缓存的大小,定长记录的大小和填充位。变长记录所用的分隔符等等。



二、Berkeley DB的应用 

1、从官方站点http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/overview/index.html下载Berkeley DB的安装文件及JAVA开发包。

 

2、在windows安装Berkeley DB,一直按下一步就可以。为开发方便,安装了windows版本号,正式执行时应该使用Linux版本号。

(设置path时出错,须要以管理员身份执行安装程序)。

 

3、将JAVA开发包中的jar文件放入buildpath中。主要包含je-6.0.11.jar、JEJConsole.jar、epJEJConsole.jar三个包。


測试程序:

package com.ljh.test;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

public class BerkeleyDBUtilTest {
	
	private BerkeleyDBUtil dbUtil = null;
	
	@Before
	public void setup() {
		dbUtil = new BerkeleyDBUtil("D:/tmp");
	} 
	

	@Test
	public void testWriteToDatabase() {
		for (int i = 0; i < 10; i++){
		dbUtil.writeToDatabase(i+"", "学生"+i, true);
		}
	}

	@Test
	public void testReadFromDatabase() {
		String value = http://www.mamicode.com/dbUtil.readFromDatabase("2");
		assertEquals(value, "学生2");
	}

	@Test
	public void testGetEveryItem() {
		int size = dbUtil.getEveryItem().size();
		assertEquals(size, 10);
	}

	@Test
	public void testDeleteFromDatabase() {
		dbUtil.deleteFromDatabase("4");
		assertEquals(9, dbUtil.getEveryItem().size());
	}
	
	public void cleanup() {
		dbUtil.closeDB();
	}

}


Berkeley DB的基本操作:

包含下面部分

(1)打开数据库

(2)向数据库写入数据

(3)依据Key值读取某个数据

(4)读取全量数据列表

(5)依据Key值删除某个数据

(6)关闭数据库

注意:因为各个操作可能相应同一个数据库,因此是否须要使用单例模式?

package com.ljh.test;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockConflictException;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.TransactionConfig;

public class BerkeleyDBUtil {

	// 数据库环境
	private Environment env = null;

	// 数据库
	private static Database frontierDatabase = null;

	// 数据库名
	private static String dbName = "frontier_database";

	public BerkeleyDBUtil(String homeDirectory) {

		// 1、创建EnvironmentConfig
		EnvironmentConfig envConfig = new EnvironmentConfig();
		envConfig.setTransactional(true);
		envConfig.setAllowCreate(true);

		// 2、使用EnvironmentConfig配置Environment
		env = new Environment(new File(homeDirectory), envConfig);

		// 3、创建DatabaseConfig
		DatabaseConfig dbConfig = new DatabaseConfig();
		dbConfig.setTransactional(true);
		dbConfig.setAllowCreate(true);

		// 4、使用Environment与DatabaseConfig打开Database
		frontierDatabase = env.openDatabase(null, dbName, dbConfig);

	}

	/*
	 * 向数据库中写入记录。并推断能否够有反复数据。 传入key和value
	 * 若能够有反复数据。则直接使用put()就可以。若不能有反复数据,则使用putNoOverwrite()。

*/ public boolean writeToDatabase(String key, String value, boolean isOverwrite) { try { // 设置key/value,注意DatabaseEntry内使用的是bytes数组 DatabaseEntry theKey = new DatabaseEntry(key.getBytes("UTF-8")); DatabaseEntry theData = http://www.mamicode.com/new DatabaseEntry(value.getBytes("UTF-8")); OperationStatus status = null; Transaction txn = null; try { // 1、Transaction配置 TransactionConfig txConfig = new TransactionConfig(); txConfig.setSerializableIsolation(true); txn = env.beginTransaction(null, txConfig); // 2、写入数据 if (isOverwrite) { status = frontierDatabase.put(txn, theKey, theData); } else { status = frontierDatabase.putNoOverwrite(txn, theKey, theData); } txn.commit(); if (status == OperationStatus.SUCCESS) { System.out.println("向数据库" + dbName + "中写入:" + key + "," + value); return true; } else if (status == OperationStatus.KEYEXIST) { System.out.println("向数据库" + dbName + "中写入:" + key + "," + value + "失败,该值已经存在"); return false; } else { System.out.println("向数据库" + dbName + "中写入:" + key + "," + value + "失败"); return false; } } catch (LockConflictException lockConflict) { txn.abort(); System.out.println("向数据库" + dbName + "中写入:" + key + "," + value + "出现lock异常"); return false; } } catch (Exception e) { // 错误处理 System.out.println("向数据库" + dbName + "中写入:" + key + "," + value + "出现错误"); return false; } } /* * 从数据库中读出数据 传入key 返回value */ public String readFromDatabase(String key) { try { DatabaseEntry theKey = new DatabaseEntry(key.getBytes("UTF-8")); DatabaseEntry theData = http://www.mamicode.com/new DatabaseEntry();"UTF-8"); System.out.println("从数据库" + dbName + "中读取:" + key + "," + value); return value; } else { System.out .println("No record found for key ‘" + key + "‘."); return ""; } } catch (LockConflictException lockConflict) { txn.abort(); System.out.println("从数据库" + dbName + "中读取:" + key + "出现lock异常"); return ""; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); return ""; } } /* * 遍历数据库中的全部记录。返回list */ public ArrayList<String> getEveryItem() { // TODO Auto-generated method stub System.out.println("===========遍历数据库" + dbName + "中的全部数据=========="); Cursor myCursor = null; ArrayList<String> resultList = new ArrayList<String>(); Transaction txn = null; try { txn = this.env.beginTransaction(null, null); CursorConfig cc = new CursorConfig(); cc.setReadCommitted(true); if (myCursor == null) myCursor = frontierDatabase.openCursor(txn, cc); DatabaseEntry foundKey = new DatabaseEntry(); DatabaseEntry foundData = http://www.mamicode.com/new DatabaseEntry();"UTF-8"); String theData = http://www.mamicode.com/new String(foundData.getData(),"UTF-8"); resultList.add(theKey); System.out.println("Key | Data : " + theKey + " | " + theData + ""); while (myCursor.getNext(foundKey, foundData, LockMode.DEFAULT) == OperationStatus.SUCCESS) { theKey = new String(foundKey.getData(), "UTF-8"); theData = http://www.mamicode.com/new String(foundData.getData(),"UTF-8"); resultList.add(theKey); System.out.println("Key | Data : " + theKey + " | " + theData + ""); } } myCursor.close(); txn.commit(); return resultList; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } catch (Exception e) { System.out.println("getEveryItem处理出现异常"); txn.abort(); if (myCursor != null) { myCursor.close(); } return null; } } /* * 依据key值删除数据库中的一条记录 */ public boolean deleteFromDatabase(String key) { boolean success = false; long sleepMillis = 0; for (int i = 0; i < 3; i++) { if (sleepMillis != 0) { try { Thread.sleep(sleepMillis); } catch (InterruptedException e) { e.printStackTrace(); } sleepMillis = 0; } Transaction txn = null; try { // 1、使用cursor.getPrev方法来遍历游标获取数据 TransactionConfig txConfig = new TransactionConfig(); txConfig.setSerializableIsolation(true); txn = env.beginTransaction(null, txConfig); DatabaseEntry theKey; theKey = new DatabaseEntry(key.getBytes("UTF-8")); //2、删除数据 并提交 OperationStatus res = frontierDatabase.delete(txn, theKey); txn.commit(); if (res == OperationStatus.SUCCESS) { System.out.println("从数据库" + dbName + "中删除:" + key); success = true; return success; } else if (res == OperationStatus.KEYEMPTY) { System.out.println("没有从数据库" + dbName + "中找到:" + key + "。

无法删除"); } else { System.out.println("删除操作失败,因为" + res.toString()); } return false; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return false; } catch (LockConflictException lockConflict) { System.out.println("删除操作失败,出现lockConflict异常"); sleepMillis = 1000; continue; } finally { if (!success) { if (txn != null) { txn.abort(); } } } } return false; } public void closeDB() { if (frontierDatabase != null) { frontierDatabase.close(); } if (env != null) { env.close(); } } }





Berkeley DB基础教程