介绍 

    Berkeley DB (DB)是一个高性能的,嵌入数据库,和C语言,C++,Java,Perl,Python,PHP,Tcl以及其他很多语言都有绑定。Berkeley DB可以保存任意类型的键/值对,而且可以为一个键保存多个数据。Berkeley DB可以支持数千的并发线程同时操作数据库,支持最大256TB的数据,广泛用于各种操作系统包括大多数Unix类操作系统和Windows操作系统以及实时操作系统。

    在工作中使用的是java版本的bdb( Berkeley DB 以下简称为bdb),主要用来存储计算结果提供给业务系统访问,比如数据挖掘的结果,这类数据定期会刷新,访问形式单一,存在数据库有点大材小用,另外就是存储大量的计算中间结果。总体来说bdb的性能非常不错,但在使用时还是遇到了一些问题,总结一些经验教训

问题1:定期build大量计算结果

实际使用时,写入千万数据后,写入性能急剧下降,但硬盘还在飞转,调节了很多的系统参数,关闭了事务,调大了btree索引缓存(根据bdb内部的DbCacheSize工具调节),jvm启动参数等等(怀疑是fgc的问题),都没有解决问题,最后调节了写入数据的顺序才解决了问题,一开始我们没有对写入数据的key排序,而随机写入大量数据导致了btree索引性能的下降,改为顺序写入后,问题解决。那如何解释硬盘飞转呢,其实是由于写操作频繁触发btree结构调整导致。

问题2:性能测试出现少量lock timeout,软硬件环境不同出现的次数也不同

默认的locktimeout是500ms,而我们在并发读写的性能测试时出现了少量的timeout情况,32位环境(jdk1.5)下较少,64位环境(jdk1.6)下较多,500ms的timeout,在非高性能要求的环境下,貌似还是短了点。问题是为什么对于不同的环境,出现timeout的次数差距较明显,多次测试发现32位环境出现的较少,64位环境出现的较多,com.sleepycat.je.util.DbCacheSize这个工具让我发现了一些线索,原来我一直以为这个工具就是粗略根据分支节点大概的大小,总数据量,key大小就可以帮助我们计算出大致的索引cache size,比如1000万数据,key长20byte的cache size:

Inputs: records=10000000 keySize=20 dataSize=8092 nodeMax=128 density=80% overhead=10%

    Cache Size      Btree Size  Description

   871,284,622     784,156,160  Minimum, internal nodes only  

 1,180,450,133   1,062,405,120  Maximum, internal nodes only  

91,537,951,288  82,384,156,160  Minimum, internal nodes and leaf nodes  

91,847,116,800  82,662,405,120  Maximum, internal nodes and leaf nodes  

Btree levels: 4 

这个是在mac下的测试结果,而在32位的ubuntu下测试minimum internal nodes需要501M,主要的原因是由于在不同的环境对应的java对象占用内存大小不一致,而java本身没有提供动态检测对象占用内存大小的方法,在bdb中MemoryBudget类里面定义了不同环境下对应的常用对象的大小,所以,如果用同样的cachesize配置,在不同的环境下会表现出不同的性能,因为如果设置小了会导致部分索引的操作无法命中缓存,性能会有所下降,所以我推测timeout的频率不同的原因是由于固定的cachesize在不同的环境性能也会不同而导致。

问题3:数据build需要注意的

对于大数据量写入bdb,同时需要把数据文件跨地区传输时,有个一个小的tips,就是在你build完数据以后,close environment后,重新开启一个只读的environment,设置启动参数:

 
  
  1. je.env.runCleaner = false 
  2. je.cleaner.minUtilization = 90 
  3.   
 
  
  1. while (environment.cleanLog() > 0) { 
  2.     System.err.println("clean log "); 
  3. CheckpointConfig cleanCheckpointConfig = new CheckpointConfig() 
  4. cleanCheckpointConfig.setForce(true
  5. environment.checkpoint(cleanCheckpointConfig); 
  6. environment.close(); 

加上如上代码,这样就可以清除掉大部分废弃的log信息,把文件缩小,这样传输时间会省不少,特别在带宽受限的情况下。