从5.2开始, BerkeleyDB新提供了一种称为Heap的存储方法, 为用户存储数据提供了一种新的选择.
一般而言, BerkeleyDB存在空间使用效率不高的问题
在Btree中, 节点只有添加时的分裂而没有删除后的合并, 所以当删除很多记录后再插入很多记录, 删除记录所产生的空闲区域经常不能被新纪录有效利用, 导致数据库在使用中越来越大, 即使数据容量本身没有发生什么变化. Recno是基于Btree, 所以也存在同样的问题, 而Hash也存在类似的问题.
Queue则要求记录长度固定, 且关键字为记录号, 当插入一个大的记录号时, 前面的记录号极其记录会自动产生(虽然无法访问), 导致有许多无用记录占用宝贵磁盘空间.
Heap则不同, 其可以有效利用先前删除记录所产生的空闲空间, 而且不会自动产生无用记录. 所以, 对于数据容量变化不大的程序而言, Heap可以有效的使数据库文件的大小保持在一个合理的范围内. 对于严格空间受限的嵌入式程序而言, Heap将会是一个不错的存储数据的选择.
当然, Heap在使用方式上不如Btree/Hash/Recno/Queue方便. 不同于其他访问方式可以自己定义关键字, Heap中记录的关键字是系统产生的, 并且返回给用户. 用户只有使用返回的关键字才能对指定的记录进行更新, 查询,删除等操作.
所以一般使用Heap的标准方式为:
* 以追加的方式向Heap数据库总插入记录, 并且保存范围的记录关键字.
* 使用该关键字进行各种操作(更新,删除,查询).
Heap可以控制数据库文件大小的上限, 通过以下方法:
int DB->set_heapsize(DB *db, u_int32_t gbytes, u_int32_t bytes, u_int32_t flags); flags目前必须为0. 当一个操作会导致数据库文件超过限制时, 该操作将会失败, 且返回DB_HEAP_FULL. 当该方法未被调用时, 不会对数据库文件施加限制.
在Heap中, 记录关键字使用结构体DB_HEAP_RID表示的, 其定义如下:
struct __db_heap_rid {
db_pgno_t pgno; /* Page number. */
db_indx_t indx; /* Index in the offset table. */
}; 当以append方式添加记录时, 返回的key.data就是一个指向该结构体的指针.
下面是一个使用Heap的C的示例程序:
- #include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include "db.h"
-
-
#define CHK_RET(ret, str) do { \
-
int _ret = (ret); \
-
if (_ret) { \
-
fprintf(stderr, "Error(%d, %s): %s\n", \
-
_ret, (str), db_strerror(_ret)); \
-
exit(1); \
-
} \
-
} while(0)
-
-
void test_heap1(int cnt) {
-
DB *db;
-
DBC *dbc;
-
int ret, i, j;
-
DB_HEAP_RID *rids, *rid;
-
char *buf;
-
DBT key, data;
-
-
memset(&key, 0, sizeof(DBT));
-
memset(&data, 0, sizeof(DBT));
-
rids = malloc(cnt * sizeof(DB_HEAP_RID));
-
buf = malloc(16384 * sizeof(char));
-
-
CHK_RET((ret = db_create(&db, NULL, 0)), "db_create");
-
CHK_RET((ret = db->set_heapsize(db, 1, 0, 0)), "db->set_heapsize");
-
CHK_RET((ret = db->open(db, NULL, "heap1.db", NULL, DB_HEAP,
-
DB_CREATE | DB_TRUNCATE, 0644)), "db->open");
-
-
printf("1 Initial put:\n");
-
for (i = 0; i < cnt; i++) {
-
buf[0] = '\0';
-
for (j = 0; j < i; j++)
-
sprintf(buf + strlen(buf), "Value");
-
sprintf(buf + strlen(buf), "%d", i);
-
data.data = buf;
-
data.size = strlen(buf) + 1;
-
CHK_RET((ret = db->put(db, NULL, &key, &data, DB_APPEND)),
-
"db->put(DB_APPEND)");
-
memcpy(rids+i, key.data, sizeof(DB_HEAP_RID));
-
printf("Return id for %s : {%u, %u}\n", buf, rids[i].pgno,
-
rids[i].indx);
-
}
-
-
memset(&key, 0, sizeof(DBT));
-
memset(&data, 0, sizeof(DBT));
-
printf("2 First traverse using the record ids:\n");
-
for (i = 0; i < cnt; i++) {
-
key.data = &rids[i];
-
key.size = sizeof(DB_HEAP_RID);
-
CHK_RET((ret = db->get(db, NULL, &key, &data, 0)),
-
"db->get");
-
printf("Data for {%u, %u}: %s\n", rids[i].pgno, rids[i].indx,
-
(char *)data.data);
-
}
-
-
memset(&key, 0, sizeof(DBT));
-
memset(&data, 0, sizeof(DBT));
-
-
printf("3 Overwrite the values:\n");
-
for (i = 0; i < cnt; i++) {
-
key.data = &rids[i];
-
key.size = sizeof(DB_HEAP_RID);
-
buf[0] = '\0';
-
for (j = 0; j < i; j++)
-
sprintf(buf + strlen(buf), "NewValue");
-
sprintf(buf + strlen(buf), "%d", i);
-
data.data = buf;
-
data.size = strlen(buf) + 1;
-
CHK_RET((ret = db->put(db, NULL, &key, &data, 0)), "db->put");
-
}
-
-
printf("4 Second traverse using the record ids:\n");
-
for (i = 0; i < cnt; i++) {
-
key.data = &rids[i];
-
key.size = sizeof(DB_HEAP_RID);
-
CHK_RET((ret = db->get(db, NULL, &key, &data, 0)),
-
"db->get");
-
printf("Data for {%u, %u}: %s\n", rids[i].pgno, rids[i].indx,
-
(char *)data.data);
-
}
-
-
printf("5 Delete some values:\n");
-
for (i = 0; i < cnt; i+= 2) {
-
key.data = &rids[i];
-
key.size = sizeof(DB_HEAP_RID);
-
CHK_RET((ret = db->del(db, NULL, &key, 0)), "db->del");
-
printf("delete record for {%u, %u}\n", rids[i].pgno, rids[i].indx);
-
}
-
-
printf("6 Third traverse using the record ids:\n");
-
for (i = 0; i < cnt; i++) {
-
key.data = &rids[i];
-
key.size = sizeof(DB_HEAP_RID);
-
ret = db->get(db, NULL, &key, &data, 0);
-
if (ret == DB_NOTFOUND)
-
printf("No data for {%u, %u}\n", rids[i].pgno, rids[i].indx);
-
else if(ret == 0) {
-
printf("Data for {%u, %u}: %s\n", rids[i].pgno, rids[i].indx,
-
(char *)data.data);
-
} else {
-
CHK_RET(ret, "db->get");
-
}
-
}
-
-
printf("7 Final traverse using cursor:");
-
CHK_RET((ret = db->cursor(db, NULL, &dbc, 0)), "db->cursor");
-
memset(&key, 0, sizeof(DBT));
-
memset(&data, 0, sizeof(DBT));
-
i = 0;
-
while ((ret = dbc->get(dbc, &key, &data, DB_NEXT)) == 0) {
-
rid = key.data;
-
printf("Record %d --- {%u,%u}: %s\n", i++,
-
rid->pgno, rid->indx, (char *)data.data);
-
}
-
-
CHK_RET((db->close(db, 0)), "db->close");
-
free(rids);
-
}
-
-
int main(int argc, char *argv[]) {
-
-
test_heap1(10);
-
-
}
其对应的Java程序为:
- import com.sleepycat.db.*;
-
import java.util.*;
-
-
public class TestHeap {
-
-
public static int test_heap1(int cnt) throws Exception{
-
DatabaseConfig config1 = new DatabaseConfig();
-
config1.setType(DatabaseType.HEAP);
-
config1.setAllowCreate(true);
-
config1.setHeapsize(100000000L);
-
-
Database db1 = new Database("heap1.db", null, config1);
-
ArrayList<HeapRecordId> list1 = new ArrayList<HeapRecordId>();
-
-
System.out.println("1 Initial put:");
-
for (int i = 0; i < cnt; i++) {
-
String str1 = ""+i;
-
for(int j = 0; j < i; j++)
-
str1 = "Value"+str1;
-
DatabaseEntry k1 = new DatabaseEntry();
-
DatabaseEntry d1 = new DatabaseEntry(str1.getBytes());
-
OperationStatus st1 = db1.append(null, k1, d1);
-
if (st1 != OperationStatus.SUCCESS){
-
System.out.println("Error to put " + str1);
-
} else {
-
HeapRecordId hid1 = HeapRecordId.fromArray(k1.getData());
-
System.out.println("Return id for " + str1 +" :{" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}");
-
list1.add(hid1);
-
}
-
}
-
-
int len = list1.size();
-
-
System.out.println("2 First traverse using the record ids:");
-
for(int i = 0; i < len; i++) {
-
HeapRecordId hid1 = list1.get(i);
-
DatabaseEntry k1 = new DatabaseEntry(hid1.toArray());
-
DatabaseEntry d1 = new DatabaseEntry();
-
OperationStatus st1 = db1.get(null, k1, d1, null);
-
if (st1 != OperationStatus.SUCCESS){
-
System.out.println("Error to get " + "{" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}");
-
continue;
-
}
-
String str1 = new String(d1.getData());
-
System.out.println("Data for " + " {" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}: " + str1);
-
}
-
-
System.out.println("3 Overwrite the values:");
-
for(int i = 0; i < list1.size();i++) {
-
HeapRecordId hid1 = list1.get(i);
-
DatabaseEntry k1 = new DatabaseEntry(hid1.toArray());
-
String str1 = ""+i;
-
for(int j = 0; j < i; j++)
-
str1 = "NewValue"+str1;
-
DatabaseEntry d1 = new DatabaseEntry(str1.getBytes());
-
OperationStatus st1 = db1.put(null, k1, d1);
-
if (st1 != OperationStatus.SUCCESS){
-
System.out.println("Error to put " + str1);
-
}
-
}
-
-
System.out.println("4 Second traverse using the record ids:");
-
for(int i = 0; i < len; i++) {
-
HeapRecordId hid1 = list1.get(i);
-
DatabaseEntry k1 = new DatabaseEntry(hid1.toArray());
-
DatabaseEntry d1 = new DatabaseEntry();
-
OperationStatus st1 = db1.get(null, k1, d1, null);
-
if (st1 != OperationStatus.SUCCESS){
-
System.out.println("Error to get " + "{" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}");
-
continue;
-
}
-
String str1 = new String(d1.getData());
-
System.out.println("Data for " + " {" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}: " + str1);
-
}
-
-
System.out.println("5 Delete some values:");
-
for(int i = 0; i < len; i+=2) {
-
HeapRecordId hid1 = list1.get(i);
-
DatabaseEntry k1 = new DatabaseEntry(hid1.toArray());
-
OperationStatus st1 = db1.delete(null, k1);
-
if (st1 != OperationStatus.SUCCESS){
-
System.out.println("Error to delete " + "{" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}");
-
} else {
-
System.out.println("Delete " + "{" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}");
-
}
-
}
-
-
System.out.println("6 Third traverse using the record ids:");
-
for(int i = 0; i < len; i++) {
-
HeapRecordId hid1 = list1.get(i);
-
DatabaseEntry k1 = new DatabaseEntry(hid1.toArray());
-
DatabaseEntry d1 = new DatabaseEntry();
-
OperationStatus st1 = db1.get(null, k1, d1, null);
-
if (st1 != OperationStatus.SUCCESS){
-
System.out.println("Error to get " + "{" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}");
-
continue;
-
}
-
String str1 = new String(d1.getData());
-
System.out.println("Data for " + " {" +hid1.getPageNumber()+
-
","+hid1.getIndex()+"}: " + str1);
-
}
-
-
System.out.println("7 Final traverse using cursor:");
-
Cursor cursor1 = db1.openCursor(null, new CursorConfig());
-
DatabaseEntry ck1 = new DatabaseEntry();
-
DatabaseEntry cd1 = new DatabaseEntry();
-
int no1 = 0;
-
while(cursor1.getNext(ck1, cd1, null) == OperationStatus.SUCCESS) {
-
HeapRecordId hid1 = HeapRecordId.fromArray(ck1.getData());
-
String str1 = new String(cd1.getData());
-
System.out.println("Record " + no1 + " -- {" +
-
hid1.getPageNumber()+","+hid1.getIndex()+"}: " + str1);
-
no1++;
-
}
-
cursor1.close();
-
-
db1.close();
-
-
return 0;
-
}
-
-
public static void main(String[] args) throws Exception{
-
int ret1 = test_heap1(5);
-
}
-
-
}
这两个程序都输出一下内容:
1 Initial put:
Return id for 0 : {2, 0}
Return id for Value1 : {2, 1}
Return id for ValueValue2 : {2, 2}
Return id for ValueValueValue3 : {2, 3}
Return id for ValueValueValueValue4 : {2, 4}
Return id for ValueValueValueValueValue5 : {2, 5}
Return id for ValueValueValueValueValueValue6 : {2, 6}
Return id for ValueValueValueValueValueValueValue7 : {2, 7}
Return id for ValueValueValueValueValueValueValueValue8 : {2, 8}
Return id for ValueValueValueValueValueValueValueValueValue9 : {2, 9}
2 First traverse using the record ids:
Data for {2, 0}: 0
Data for {2, 1}: Value1
Data for {2, 2}: ValueValue2
Data for {2, 3}: ValueValueValue3
Data for {2, 4}: ValueValueValueValue4
Data for {2, 5}: ValueValueValueValueValue5
Data for {2, 6}: ValueValueValueValueValueValue6
Data for {2, 7}: ValueValueValueValueValueValueValue7
Data for {2, 8}: ValueValueValueValueValueValueValueValue8
Data for {2, 9}: ValueValueValueValueValueValueValueValueValue9
3 Overwrite the values:
4 Second traverse using the record ids:
Data for {2, 0}: 0
Data for {2, 1}: NewValue1
Data for {2, 2}: NewValueNewValue2
Data for {2, 3}: NewValueNewValueNewValue3
Data for {2, 4}: NewValueNewValueNewValueNewValue4
Data for {2, 5}: NewValueNewValueNewValueNewValueNewValue5
Data for {2, 6}: NewValueNewValueNewValueNewValueNewValueNewValue6
Data for {2, 7}: NewValueNewValueNewValueNewValueNewValueNewValueNewValue7
Data for {2, 8}: NewValueNewValueNewValueNewValueNewValueNewValueNewValueNewValue8
Data for {2, 9}: NewValueNewValueNewValueNewValueNewValueNewValueNewValueNewValueNewValue9
5 Delete some values:
delete record for {2, 0}
delete record for {2, 2}
delete record for {2, 4}
delete record for {2, 6}
delete record for {2, 8}
6 Third traverse using the record ids:
No data for {2, 0}
Data for {2, 1}: NewValue1
No data for {2, 2}
Data for {2, 3}: NewValueNewValueNewValue3
No data for {2, 4}
Data for {2, 5}: NewValueNewValueNewValueNewValueNewValue5
No data for {2, 6}
Data for {2, 7}: NewValueNewValueNewValueNewValueNewValueNewValueNewValue7
No data for {2, 8}
Data for {2, 9}: NewValueNewValueNewValueNewValueNewValueNewValueNewValueNewValueNewValue9
7 Final traverse using cursor:Record 0 --- {2,1}: NewValue1
Record 1 --- {2,3}: NewValueNewValueNewValue3
Record 2 --- {2,5}: NewValueNewValueNewValueNewValueNewValue5
Record 3 --- {2,7}: NewValueNewValueNewValueNewValueNewValueNewValueNewValue7
Record 4 --- {2,9}: NewValueNewValueNewValueNewValueNewValueNewValueNewValueNewValueNewValue9
阅读(1919) | 评论(0) | 转发(0) |