项目中用到了nosql数据库BDB,借此机会研究了一下它的用法。它的官方示例和文档比较丰富,感觉比较容易学习
在开发过程中出现了一个需求,要把数据根据插入时间遍历,个人认为通过第二主键(SecondKey)比较容易实现。以下是我的基本实现过程1.在ValueBean中加入insertTime属性
其中的hostName属性在主从同步和生成插入时间时用到,value属性就是key-value中的值public class ValueBean{ private String insertTime; private String hostName; private byte[] value; public String getHostName() { return hostName; } public void setHostName(String hostName) { this.hostName = hostName; } public String getInsertTime() { return insertTime; } public void setInsertTime(String insertTime) { this.insertTime = insertTime; } public byte[] getValue() { return value; } public void setValue(byte[] value) { this.value = value; } }
2.TupleBinding类
public class ValueBeanBinding extends TupleBinding<ValueBean> { @Override public ValueBean entryToObject(TupleInput input) { String time = input.readString(); String name = input.readString(); byte[] value = new byte[input.getBufferLength()-input.getBufferOffset()];//获得value长度 input.read(value); ValueBean data = new ValueBean(); data.setInsertTime(time); data.setHostName(name); data.setValue(value); return data; } @Override public void objectToEntry(ValueBean object, TupleOutput output) { ValueBean value = object; output.writeString(value.getInsertTime()); output.writeString(value.getHostName()); output.write(value.getValue()); } }
此类用于将ValueBean和DatabaseEntry进行转换,两个方法中的属性读写顺序要统一。
3.SecondaryKeyCreator,第二主键生成器
public class SecondKeyCreator implements SecondaryKeyCreator{ private TupleBinding<ValueBean> theBinding; SecondKeyCreator(TupleBinding<ValueBean> theBinding) { this.theBinding = theBinding; } @Override public boolean createSecondaryKey(SecondaryDatabase secondary, DatabaseEntry key, DatabaseEntry data, DatabaseEntry result) { ValueBean v = (ValueBean) theBinding.entryToObject(data); String time=v.getInsertTime(); result.setData(time.getBytes()); return true; } }
指定insertTime属性作为第二主键
在插入一个新数据时生成insertTime十分关键,尤其在高并发和互为主从同步时极易出现“第二主键重复”的错误,造成数据无法插入,我了使用当前时间毫秒数+AtomicInteger自增+hostName的asc码之和,保证insertTime的前后大小顺序。
System.currentTimeMillis()*1000000+(add_num.getAndIncrement()%1000)*1000 + host_key
4.创建第二数据库,用于存储secondkey
SecondaryConfig mySecConfig = new SecondaryConfig(); mySecConfig.setAllowCreate(true); mySecConfig.setSortedDuplicates(false); TupleBinding<ValueBean> tb =new ValueBeanBinding(); SecondKeyCreator keyCreator = new SecondKeyCreator(tb); mySecConfig.setKeyCreator(keyCreator); mySecConfig.setTransactional(envConfig.getTransactional()); String secDbName = "mySecondaryDatabase"; mySecDb = myEnv.openSecondaryDatabase(null, secDbName, storeDb, mySecConfig);
到此,便可以使用SecondaryCursor的getNext()和getPrev()前后遍历了,getSearchKey()可以找到你想要的位置
=========================================================================================================
今天发了一个问题,cursor在未关闭之前会把所指的记录锁住。在修改被锁数据的时候会报错。
com.sleepycat.je.LockTimeoutException: (JE 4.1.10) Lock expired. Locker 25268096 7_New I/O server worker #1-2_Txn: waited for lock on database=testdb LockAddr:15430449 node=21 type=WRITE grant=WAIT_NEW timeoutMillis=1000 startTime=1323757856515 endTime=1323757857515
Owners: [<LockInfo locker="8385974 -1_New I/O server worker #1-1_ThreadLocker" type="READ"/>]
Waiters: []
所以在打开指针时要指定锁模式
问题代码:
mySecCursor = sdb.openCursor(null, null);
修改为:
CursorConfig config = new CursorConfig(); config.setReadUncommitted(true); mySecCursor = sdb.openCursor(null, config);