BerkeleyDB JE 随机访问时间非线性增加
Posted
技术标签:
【中文标题】BerkeleyDB JE 随机访问时间非线性增加【英文标题】:BerkeleyDB JE random access time increases non-linearly 【发布时间】:2012-06-19 09:24:11 【问题描述】:我正在测试 BerkeleyDB Java 版以了解我是否可以在我的项目中使用它。
我创建了一个非常简单的程序,它与 com.sleepycat.je.Database 类的对象一起工作:
写入 N 条记录,每条 5-15kb,生成的键类似于 Integer.toString(random.nextInt());
读取这些记录并使用方法 Database#get 按照它们创建的顺序获取它们;
使用方法 Database#get 以随机顺序读取相同数量的记录。
我现在看到了奇怪的事情。第三次测试的执行时间随着记录数的增加呈非线性增长。
N=80000,写入=55 秒,顺序读取=17 秒,随机读取=3 秒 N=100000,写入=60sec,顺序读取=20sec,随机读取=7sec N=120000,写入=68sec,顺序读取=27sec,随机读取=11sec N=140000,写入=82sec,顺序读取=32sec,随机读取=47sec(当然,我已经进行了多次测试。)
我想我做错了什么。以下是参考来源(抱歉,有点长),方法调用顺序相同:
private Environment env;
private Database db;
private Random random = new Random();
private List<String> keys = new ArrayList<String>();
private int seed = 113;
public boolean dbOpen()
EnvironmentConfig ec = new EnvironmentConfig();
DatabaseConfig dc = new DatabaseConfig();
ec.setAllowCreate(true);
dc.setAllowCreate(true);
env = new Environment(new File("mydbenv"), ec);
db = env.openDatabase(null, "moe", dc);
return true;
public int storeRecords(int i)
int j;
long size = 0;
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry val = new DatabaseEntry();
random.setSeed(seed);
for (j = 0; j < i; j++)
String k = Long.toString(random.nextLong());
byte[] data = new byte[5000 + random.nextInt(10000)];
keys.add(k);
size += data.length;
random.nextBytes(data);
key.setData(k.getBytes());
val.setData(data);
db.put(null, key, val);
System.out.println("GENERATED SIZE: " + size);
return j;
public int fetchRecords(int i)
int j, res;
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry val = new DatabaseEntry();
random.setSeed(seed);
res = 0;
for (j = 0; j < i; j++)
String k = Long.toString(random.nextLong());
byte[] data = new byte[5000 + random.nextInt(10000)];
random.nextBytes(data);
key.setData(k.getBytes());
db.get(null, key, val, null);
if (Arrays.equals(data, val.getData()))
res++;
else
System.err.println("FETCH differs: " + j);
System.err.println(data.length + " " + val.getData().length);
return res;
public int fetchRandom(int i)
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry val = new DatabaseEntry();
for (int j = 0; j < i; j++)
String k = keys.get(random.nextInt(keys.size()));
key.setData(k.getBytes());
db.get(null, key, val, null);
return i;
【问题讨论】:
数据是否随机都没有关系。不随机填充数据可以试试吗? 您有多少可用内存用于磁盘缓存?可能是 1 GB 左右? (100K 条目 * 每个 10K) 顺序获取对缓存非常友好。跳过整个数据库可能不是。您的应用程序是否是一种现实的访问模式?通常,键值存储是通过键而不是位置来访问的。 我当然可以(我可能会稍后再试),但是由于“无关紧要”数据包含什么(可能只有大小可能有一些重要性),所以我应该看到?如果它以同样的方式工作,那将是无用的实验。如果它会以不同的方式工作......问题只会更加模糊...... ;-) 我不认为我有 1GB 的磁盘缓存,但是性能下降对于 20% 的数据量增量来说太显着了......顺序访问和随机访问之间的区别是什么 - 我同意,但是我期望在较小的数据量下看到它的性能问题... 【参考方案1】:性能下降是非线性的,原因有两个:
-
BDB-JE 数据结构是一个 b-tree,它在检索一条记录时具有 O(log(n)) 性能。通过 get 方法检索所有内容是 O(n*log(n))。
大型数据集不适合 RAM,因此磁盘访问会减慢一切速度。随机访问的缓存局部性很差。
请注意,您可以通过放弃一些持久性来提高写入性能:ec.setTxnWriteNoSync(true);
您可能还想试试 Tupl,它是我一直在研究的开源 BerkeleyDB 替代品。它仍处于 alpha 阶段,但您可以在 SourceForge 上找到它。
为了公平比较 BDB-JE 和 Tupl,我将缓存大小设置为 500M,并在存储方法结束时执行显式检查点。
使用 BDB-JE:
N=80000,写入=11.0sec,读取=5.3sec N=100000,写入=13.6sec,读取=7.0sec N=120000,写入=16.4sec,读取=29.5sec N=140000,写入=18.8sec,读取=35.9sec N=160000,写入=21.5sec,读取=41.3sec N=180000,写入=23.9sec,读取=46.4sec使用 Tupl:
N=80000,写入=21.7 秒,读取=4.4 秒 N=100000,写入=27.6sec,读取=6.3sec N=120000,写入=30.2sec,读取=8.4sec N=140000,写入=35.4sec,读取=12.2sec N=160000,写入=39.9sec,读取=17.4sec N=180000,写入=45.4sec,读取=22.8sec由于其基于日志的格式,BDB-JE 写入条目的速度更快。然而,Tupl 的阅读速度更快。以下是 Tupl 测试的来源:
导入 java.io.; 导入 java.util.;
导入 org.cojen.tupl.*;
公共类 TuplTest public static void main(final String[] args) 抛出异常 最终 RandTupl rt = new RandTupl(); rt.dbOpen(args[0]);
long start = System.currentTimeMillis();
rt.storeRecords(Integer.parseInt(args[1]));
long end = System.currentTimeMillis();
System.out.println("store duration: " + (end - start));
long start = System.currentTimeMillis();
rt.fetchRecords(Integer.parseInt(args[1]));
long end = System.currentTimeMillis();
System.out.println("fetch duration: " + (end - start));
private Database db;
private Index ix;
private Random random = new Random();
private List<String> keys = new ArrayList<String>();
private int seed = 113;
public boolean dbOpen(String home) throws Exception
DatabaseConfig config = new DatabaseConfig();
config.baseFile(new File(home));
config.durabilityMode(DurabilityMode.NO_FLUSH);
config.minCacheSize(500000000);
db = Database.open(config);
ix = db.openIndex("moe");
return true;
public int storeRecords(int i) throws Exception
int j;
long size = 0;
random.setSeed(seed);
for (j = 0; j < i; j++)
String k = Long.toString(random.nextLong());
byte[] data = new byte[5000 + random.nextInt(10000)];
keys.add(k);
size += data.length;
random.nextBytes(data);
ix.store(null, k.getBytes(), data);
System.out.println("GENERATED SIZE: " + size);
db.checkpoint();
return j;
public int fetchRecords(int i) throws Exception
int j, res;
random.setSeed(seed);
res = 0;
for (j = 0; j < i; j++)
String k = Long.toString(random.nextLong());
byte[] data = new byte[5000 + random.nextInt(10000)];
random.nextBytes(data);
byte[] val = ix.load(null, k.getBytes());
if (Arrays.equals(data, val))
res++;
else
System.err.println("FETCH differs: " + j);
System.err.println(data.length + " " + val.length);
return res;
public int fetchRandom(int i) throws Exception
for (int j = 0; j < i; j++)
String k = keys.get(random.nextInt(keys.size()));
ix.load(null, k.getBytes());
return i;
【讨论】:
以上是关于BerkeleyDB JE 随机访问时间非线性增加的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 berkeleydb je“从 xz WHERE xz.a > value 中选择 COUNT(*)”