mapdb java_MapDB使用入门
背景
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自動創建的,切記不要多此一舉,把文件也創建了,那樣會報錯的。
總結
以上是生活随笔為你收集整理的mapdb java_MapDB使用入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机系统性错误,《深入理解计算机系统-
- 下一篇: java写一个类吧,能不能自己写个jav