使用Berkeley DB构建持久化队列

本文介绍了BerkeleyDB(Bdb)的Java版(JE),一种嵌入式的键值数据库。包括如何初始化数据库环境、配置参数、使用不同的持久化层进行数据操作等内容。特别展示了如何构建一个持久化的队列。
AI助手已提取文章相关产品:

Berkeley DB简介

Berkeley DB(以下简称Bdb)是一个嵌入式的键值数据库。Bdb目前有两个版本,一个是使用c++构建的版本,还有一个java版本。c++版本支持在众多的语言中使用,Berkeley DB Java Edition(以下简称JE)完全用java语言编写。JE执行在应用程序中,完全不需要Client/Server的通信。JE更容易部署和嵌入到java程序中,所以我选择了使用Berkeley DB Java Edition(sleep cat)。

 

Berkeley DB编程接口  

在Java程序中使用JE,首先需要初始化一个数据库环境。

File home = new File(envHome);
EnvironmentConfig environmentConfig = new EnvironmentConfig();
environmentConfig.setTransactional(true);
environmentConfig.setAllowCreate(true);
environmentConfig.setDurability(Durability.COMMIT_WRITE_NO_SYNC);
Environment environment = new Environment(home, environmentConfig);


home是一个存在的目录,JE将把自己所有的数据库文件存储到这个目录下。setTransactional设置数据库支持事务。setAllowCreate设置数据库不存在时,是否创建数据库。setDurability设置Bdb的写数据的方式,这个很重要,对性能和数据安全的影响和很大。Bdb数据为了性能考虑,写入的数据不会立即写回到磁盘中,必须手动同步和关闭数据库的时候才回写回数据。可以通过设置Durability修改默认行为。Durability预设值了以下几种策略

COMMIT_SYNC  
事务提交时,同步数据到磁盘,并等待磁盘完成,这是最安全的策略,也是最耗时的策略。    
COMMIT_WRITE_NO_SYNC  
事务提交时,写数据到磁盘,不等待磁盘完成,安全和速度折中的策略。
COMMIT_NO_SYNC 
COMMIT_WRITE_NO_SYNC
READ_ONLY_TXN

 
COMMIT_SYNC   能保证应用或者系统挂掉而不丢失数据,保证了事务的完整性。而COMMIT_WRITE_NO_SYNC   只能保证应用挂掉不丢失数据,不能保证系统挂掉时的事务完整性。

JE提供了两个层次的编程接口,高层的Direct Persistence Layer(DPL),DPL适合直接保存和读取一个Java对象的场景。DPL读取使用annotation配置的元信息保存和读取数据。

 

 

Direct Persistence Layer  

待存取的用户类

@Entity
public class User {

    @PrimaryKey(sequence = "SEQ_USER_ID")
    private Integer userId;

    @SecondaryKey(relate = MANY_TO_ONE)
    private String nick;

    //constructor, getter and setter
}


PrimaeyKey配置userId为主键,sequence配置了一个自增序列,@SecondaryKey配置了可查询的索引。MANY_TO_ONE表示,nick的值在数据库中可重复,多个user可使用同一个nick,按nick查询会返回一个列表。

    private static EntityStore createStore(Environment envionment) throws DatabaseException {
        StoreConfig storeConfig = new StoreConfig();
        storeConfig.setAllowCreate(true);
        storeConfig.setTransactional(true);
        return new EntityStore(envionment, "store1", storeConfig);
    }


创建一个EntityStore

PrimaryIndex<Integer, User> primaryKey = entityStore.getPrimaryIndex(Integer.class, User.class);

primaryKey.put(new User("user" + i));
primaryKey.get(1);


通过EntityStore获得对应的PrimaryIndex,即可保存,读取,查询Java对象了。

 

 

Base API  

同样,首先创建一个Database。

 DatabaseConfig myDbConfig = new DatabaseConfig();
 myDbConfig.setAllowCreate(true);
 myDbConfig.setTransactional(true);
 myDbConfig.setSortedDuplicates(true);

 Database myDb = myEnv.openDatabase(null,     // txn handle
           dbName,   // Database file name
           myDbConfig);


使用Database保存数据时,提供的Key和Value都需要序列化为DatabaseEntry。

 

 

Bdb JE Java Collections  

JE提供了一组高层的Collection API,通过java的Map,List等API封装了底层存取操作。比较适合用来构建持久化的数据结构。

 

 

构建持久化队列


public class BdbMessageQueue<T> {

    private static Logger log = Logger.getLogger(BdbMessageQueue.class);

    private static final String MESSAGE_STORE = "message_store";

    private Database messageDb;
    private StoredSortedMap<Long, T> messageMap;

    private TransactionRunner transactionRunner;

    //EnqueueWorker和DequeueWorker的同步对象
    private Object syncObject = new Object();

    public BdbMessageQueue(BdbEnvironment bdbEnvironment, String queueName)
            throws DatabaseException {
        try {
            // Set the Berkeley DB config for opening all stores.
            DatabaseConfig dbConfig = new DatabaseConfig();
            dbConfig.setTransactional(true);
            dbConfig.setAllowCreate(true);


            // Open the Berkeley DB database for the part, supplier and shipment
            // stores.  The stores are opened with no duplicate keys allowed.
            messageDb = bdbEnvironment.getEnvironment().openDatabase(null, MESSAGE_STORE + queueName, dbConfig);

            EntryBinding messageKeyBinding =
                    new SerialBinding(bdbEnvironment.getJavaCatalog(), Long.class);
            EntryBinding messageValueBinding =
                    new SerialBinding(bdbEnvironment.getJavaCatalog(), Object.class);

            messageMap = new StoredSortedMap(messageDb, messageKeyBinding, messageValueBinding, true);

            // Create transactionRunner for the transactional operation
            transactionRunner = new TransactionRunner(bdbEnvironment.getEnvironment());

        } catch (DatabaseException dbe) {
            throw new VipServiceException(VipErrorCode.DBD_ERROR, dbe);
        }
    }


    /**
     * 安全关闭berkeley 数据库
     */
    public void close() {
        try {
            if (messageDb != null) messageDb.close();
        } catch (DatabaseException dbe) {
            throw new VipServiceException(VipErrorCode.DBD_ERROR, dbe);
        }
    }

    /**
     * 出队
     *
     * @return
     */
    public void dequeue(TaskCallback task) {
        try {
            DequeueWorker worker = new DequeueWorker(task);
            transactionRunner.run(worker);
        } catch (Exception e) {
            throw new VipServiceException(VipErrorCode.DBD_QUEUE_ERROR, e);
        }
    }

    /**
     * 入队
     *
     * @param message
     */
    public void enqueue(T message) {
        try {
            EnqueueWorker worker = new EnqueueWorker(message);
            transactionRunner.run(worker);
        } catch (Exception e) {
            throw new VipServiceException(VipErrorCode.DBD_QUEUE_ERROR, e);
        }
    }

    /**
     * 出队列事务Worker,内部类
     */
    private class DequeueWorker implements TransactionWorker {

        private TaskCallback task;

        private DequeueWorker(TaskCallback task) {
            this.task = task;
        }

        public void doWork() throws Exception {

            Long firstKey;
            T message;

            synchronized (syncObject) {
                //没有获得消息就不起床
                while ((firstKey = messageMap.firstKey()) == null ||
                        (message = messageMap.get(firstKey)) == null) {
                    syncObject.wait();
                }
            }
            //如果执行任务的时候,抛出了RuntimeException,消息不会从队列中删除。BDB事务也会回滚。
            task.handelMessage(message);

            messageMap.remove(firstKey);

            if (log.isDebugEnabled()) {
                log.debug(String.format("DequeueWorker dequeue %1$s. ", message));
            }
        }

    }

    /**
     * 入队列事务Worker,内部类
     */
    private class EnqueueWorker implements TransactionWorker {
        private T message;

        private EnqueueWorker(T message) {
            this.message = message;
        }

        public void doWork() throws Exception {
            synchronized (syncObject) {
                Long lastKey = messageMap.lastKey();
                lastKey = (lastKey == null) ? 1L : lastKey + 1;
                messageMap.put(lastKey, message);

                syncObject.notify();
            }

            if (log.isDebugEnabled()) {
                log.debug(String.format("EnqueueWorker enqueue %1$s. ", message));
            }
        }
    }
}


代码很长,代码中使用的JE提供的StoredMap高层次的API。

文本只是一个code show,如果你对Berkeley db感兴趣,请阅读Oracle的官方文档。

Getting Started Guide

Writing Transactional Applications

Java Collections Tutorial

Getting Started with BDB JE High Availability

 

您可能感兴趣的与本文相关内容

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值