当前位置: 首页 > 工具软件 > MapDB > 使用案例 >

mapdb java_MapDB使用入门

裴华荣
2023-12-01

背景

MapDB官网:http://www.mapdb.org

官方翻译之后的话:MapDB基于堆外存储、磁盘存储提供了Java的Maps、Sets、Lists、Queues等功能。它混合了Java集合框架和数据库引擎。它是基于Apache许可的免费的、开源的。

个人觉得:MapDB是一个轻量级的本地缓存的框架,它既可以使用对外存储,也可以使用磁盘存储(重启时数据不丢失)。它还提供事务的功能。

开发文档:https://jankotek.gitbooks.io/mapdb/content/quick-start/

开发机器配置:i5-9400 6c6t,32g内存,固态硬盘

MapDB入门实战

1、引入jar包

org.mapdb

mapdb

3.0.7

2、基于堆外存储的Hello,Simple

/*** 堆外内存map*/

public static voidoffHeapMapTest1() {

DB db=DBMaker.memoryDB().make();

ConcurrentMap map= db.hashMap("map").createOrOpen();

String key= "Hello";

String val= "simple";

map.put(key, val);

System.out.println("第1次取值," +map.get(key));

}

执行结果:

--Hello,simple----

第1次取值,simple

2.1、插曲

刚开始执行的时候,总是报下面的异常

Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/collections/impl/list/mutable/primitive/LongArrayList

at org.mapdb.StoreDirectAbstract.(StoreDirectAbstract.kt:41)

at org.mapdb.StoreDirect.(StoreDirect.kt:30)

at org.mapdb.StoreDirect$Companion.make(StoreDirect.kt:57)

at org.mapdb.StoreDirect$Companion.make$default(StoreDirect.kt:56)

at org.mapdb.DBMaker$Maker.make(DBMaker.kt:450)

at me.lovegao.mapdb.hello.HelloWorldDemo.offHeapMapTest1(HelloWorldDemo.java:18)

at me.lovegao.mapdb.hello.HelloWorldDemo.main(HelloWorldDemo.java:11)

Caused by: java.lang.ClassNotFoundException: org.eclipse.collections.impl.list.mutable.primitive.LongArrayList

at java.net.URLClassLoader.findClass(URLClassLoader.java:381)

at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)

at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

... 7 more

我以为是jar包没加全,又加了Eclipse相关的jar包,发现还是这样。对着项目又是清理,又是重新编译,又是重新导包,发现都不行。

仔细看了maven的依赖,都是存在的,看MapDB的文档,也说上面那个配置包含全部的。

最后,索性把本地相关的jar包都删了,让maven重新下,最终正常了。

3、基于磁盘的Hello,Simple

基于磁盘存储的,为了保证数据的完整性,需要在关闭虚拟机前关闭DB。

public static voidfileMapTest1() {

DB db= DBMaker.fileDB("file.db").make();

ConcurrentMap map= db.hashMap("map").createOrOpen();

String key= "something";

String val= "here";

map.put(key, val);

System.out.println("第1次取值," +map.get(key));

db.close();

System.out.println("----------重新打开----------");

db= DBMaker.fileDB("file.db").make();

map= db.hashMap("map").createOrOpen();

System.out.println("第2次取值," +map.get(key));

db.close();

}

执行结果:

--Hello,simple----

第1次取值,simple

----------重新打开----------

第2次取值,simple

结果符合预期。

3.1、基于磁盘的,内存映射的使用

/*** 在64位操作系统中,开启内存映射

* 个性化序列化*/

public static voidfileMapMemoryMapTest() {

DB db=DBMaker

.fileDB("file.db")

.fileMmapEnable()

.make();

ConcurrentMap map =db

.hashMap("mapsl", Serializer.STRING, Serializer.LONG)

.createOrOpen();long val = 51;

map.put(DEMO_KEY, val);

System.out.println("第1次取值,期望值:" + val + ",取到的值:" +map.get(DEMO_KEY));

db.close();

db=DBMaker

.fileDB("file.db")

.fileMmapEnable()

.make();

map= db.hashMap("mapsl", Serializer.STRING, Serializer.LONG)

.createOrOpen();

System.out.println("第2次取值,期望值:" + val + ",取到的值:" +map.get(DEMO_KEY));

db.close();

}

执行结果:

--Hello,simple----

第1次取值,期望值:51,取到的值:51

第2次取值,期望值:51,取到的值:51

4、性能对比

1)测试代码

packageme.lovegao.mapdb.hello;importjava.util.ArrayList;importjava.util.HashMap;importjava.util.Iterator;importjava.util.List;importjava.util.Map;importjava.util.Map.Entry;importjava.util.concurrent.ConcurrentMap;importorg.eclipse.collections.impl.map.mutable.ConcurrentHashMap;importorg.mapdb.DB;importorg.mapdb.DBMaker;importorg.mapdb.Serializer;public classMapDBSpeedTest {private final static String DEMO_KEY = "Hello";private final static String DEMO_VAL = "simple";public static voidmain(String[] args) {

System.out.println("--Hello,simple----");//fileMapMemoryMapTest();

mapTest();

}public static voidmapTest() {int dataNum = 10000;

List dbList = newArrayList();

Map> testMap = newHashMap();

Map dataMap =generateTestData(dataNum);//java原生-堆内map

ConcurrentMap inHeapDbMap = newConcurrentHashMap();

testMap.put("原生map", inHeapDbMap);//堆外map

DB offHeapDb =DBMaker.memoryDB().make();

dbList.add(offHeapDb);

ConcurrentMap offHeapDbMap= offHeapDb.hashMap("map").createOrOpen();

testMap.put("堆外map", offHeapDbMap);//基于磁盘map

DB fileDb = DBMaker.fileDB("file1.db").make();

dbList.add(fileDb);

ConcurrentMap fileDbMap =fileDb

.hashMap("map1", Serializer.STRING, Serializer.LONG)

.createOrOpen();

testMap.put("基于磁盘map", fileDbMap);//基于磁盘-内存映射map

DB fileMmapDb =DBMaker

.fileDB("file2.db")

.fileChannelEnable()//By default MapDB uses RandomAccessFile to access disk storage. Outside fast mmap files there is third option based on FileChannel. It should be faster than RandomAccessFile

.fileMmapEnable() //Always enable mmap//.fileMmapEnableIfSupported()//Only enable mmap on supported platforms,对性能影响较大

.fileMmapPreclearDisable() //Make mmap file faster//.allocateStartSize( 10 * 1024*1024*1024)//10GB,初始容量//.allocateIncrement(512 * 1024*1024)//512MB,每次增加容量

.make();//optionally preload file content into disk cache

fileMmapDb.getStore().fileLoad();

dbList.add(fileMmapDb);

ConcurrentMap fileMmapMap =fileMmapDb

.hashMap("map2", Serializer.STRING, Serializer.LONG)

.createOrOpen();

testMap.put("基于磁盘-内存映射map", fileMmapMap);

System.out.println("-----------put---数据量:"+dataNum+"------");for(String mapType : testMap.keySet()) {

putGetMapTest(mapType, testMap.get(mapType), dataMap,true);

}

System.out.println("-----------------------------------------\n");

System.out.println("-----------get---数据量:"+dataNum+"------");for(String mapType : testMap.keySet()) {

putGetMapTest(mapType, testMap.get(mapType), dataMap,false);

}for(DB db : dbList) {

db.close();

}

}/*** putGet测试

*@parammap

*@paramdataMap

*@paramput

*@return*/

public static TwoTuple putGetMapTest(String mapType, Map map, Map dataMap, booleanput) {long useTime = 0L;long errorNum = 0L;

Iterator> entryIt =dataMap.entrySet().iterator();while(entryIt.hasNext()) {

Entry entry =entryIt.next();if(put) {long t1 =System.nanoTime();

map.put(entry.getKey(), entry.getValue());

useTime= System.nanoTime() -t1;

}else{long t1 =System.nanoTime();long val =map.get(entry.getKey());

useTime= System.nanoTime() -t1;if(val !=entry.getValue()) {

errorNum++;

}

}

}double avgUseTime = (double)useTime /dataMap.size();

String fmtStr= "map类型:%s,总耗时:%dns,平均耗时%ens,异常数量:%d";

System.out.println(String.format(fmtStr, mapType, useTime, avgUseTime, errorNum));return new TwoTuple(useTime, errorNum);

}/*** 生成测试数据

*@paramsize

*@return

*/

public static Map generateTestData(intsize) {

Map map = newHashMap();int arrLength = 26;char[] words = new char[arrLength];for(int i=0; i

words[i]= (char) ('a' +i);

}

System.out.println(words);

String demoWord= newString(words);for(int i=0; i

String key= demoWord.substring(i%arrLength, i%arrLength) +i;long val =i;

map.put(key, val);

}returnmap;

}/*** 对外内存map*/

public static voidoffHeapMapTest1() {

DB db=DBMaker.memoryDB().make();

ConcurrentMap map= db.hashMap("map").createOrOpen();

map.put(DEMO_KEY, DEMO_VAL);

System.out.println("第1次取值," +map.get(DEMO_KEY));

}/*** 基于磁盘的存储*/

public static voidfileMapTest1() {

DB db= DBMaker.fileDB("file.db").make();

ConcurrentMap map= db.hashMap("map").createOrOpen();

map.put(DEMO_KEY, DEMO_VAL);

System.out.println("第1次取值," +map.get(DEMO_KEY));

db.close();

System.out.println("----------重新打开----------");

db= DBMaker.fileDB("file.db").make();

map= db.hashMap("map").createOrOpen();

System.out.println("第2次取值," +map.get(DEMO_KEY));

db.close();

}/*** 在64位操作系统中,开启内存映射

* 个性化序列化*/

public static voidfileMapMemoryMapTest() {

DB db=DBMaker

.fileDB("file.db")

.fileMmapEnable()

.make();

ConcurrentMap map =db

.hashMap("mapsl", Serializer.STRING, Serializer.LONG)

.createOrOpen();long val = 51;

map.put(DEMO_KEY, val);

System.out.println("第1次取值,期望值:" + val + ",取到的值:" +map.get(DEMO_KEY));

db.close();

db=DBMaker

.fileDB("file.db")

.fileMmapEnable()

.make();

map= db.hashMap("mapsl", Serializer.STRING, Serializer.LONG)

.createOrOpen();

System.out.println("第2次取值,期望值:" + val + ",取到的值:" +map.get(DEMO_KEY));

db.close();

}

}

2)测试结果

map类型

测试数据量

测试类型

总耗时(ns)

平均耗时(ns)

原生map

10000

put

100

1.000000e-02

堆外map

同上

put

4800

4.800000e-01

基于磁盘map

同上

put

345000

3.450000e+01

基于磁盘-内存映射map

同上

put

6000

6.000000e-01

原生map

同上

get

100

1.000000e-02

堆外map

同上

get

2000

2.000000e-01

基于磁盘map

同上

get

75400

7.540000e+00

基于磁盘-内存映射map

同上

get

1100

1.100000e-01

3)结论

①原生的基于堆的map速度始终是最快的

②堆外map和基于磁盘且开启了内存映射的map相比,优势较小。至于原因,有待深入理解。

③对于“基于磁盘-内存映射map”,使用“fileMmapEnableIfSupported”配置,对性能影响较大,建议直接开启。配置“fileMmapPreclearDisable”对于put的性能提升较大(约一倍提升)。

MapDB事务

MapDB是支持事务的,具体使用如下:

packageme.lovegao.mapdb.hello;importjava.util.concurrent.ConcurrentMap;importorg.mapdb.DB;importorg.mapdb.DBMaker;importorg.mapdb.Serializer;public classMapDBTransaction {public static voidmain(String[] args) {

DB db=DBMaker

.fileDB("file3.db")

.fileMmapEnable()

.transactionEnable()

.closeOnJvmShutdown() //JVM关闭时关闭db

.make();

ConcurrentMap map =db

.hashMap("mapsl3", Serializer.STRING, Serializer.LONG)

.createOrOpen();

map.put("a", 1L);

map.put("b", 2L);

db.commit();

System.out.println(map.get("a"));

System.out.println(map.get("b"));

map.put("c", 3L);

System.out.println("rollback之前,c:" + map.get("c"));

db.rollback();

System.out.println("rollback之后,a:" + map.get("a"));

System.out.println("rollback之后,c:" + map.get("c"));

}

}

运行结果:

1

2

rollback之前,c:3

rollback之后,a:1

rollback之后,c:null

因为配置了closeOnJvmShutdown,所以再次运行时能够正常运行。

如果去掉了transactionEnable和closeOnJvmShutdown,再次运行时将出现以下异常:

Exception in thread "main" org.mapdb.DBException$DataCorruption: Header checksum broken. Store was not closed correctly and might be corrupted. Use `DBMaker.checksumHeaderBypass()` to recover your data. Use clean shutdown or enable transactions to protect the store in the future.

at org.mapdb.StoreDirectAbstract.fileHeaderCheck(StoreDirectAbstract.kt:113)

at org.mapdb.StoreDirect.(StoreDirect.kt:114)

at org.mapdb.StoreDirect$Companion.make(StoreDirect.kt:57)

at org.mapdb.StoreDirect$Companion.make$default(StoreDirect.kt:56)

at org.mapdb.DBMaker$Maker.make(DBMaker.kt:450)

at me.lovegao.mapdb.hello.MapDBTransaction.main(MapDBTransaction.java:17)

最后说以下,fileDB("file3.db")这里的路径可以指定其他目录,默认是在项目的根目录下。

路径是自己创建的,文件是MapDB自动创建的,切记不要多此一举,把文件也创建了,那样会报错的。

 类似资料: