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自动创建的,切记不要多此一举,把文件也创建了,那样会报错的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/564249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++继承时名字的遮蔽

如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,那么就会遮蔽从基类继承过来的成员。所谓遮蔽,就是在派生类中使用该成员(包括在定义派生类时使用,也包括通过派生类对象访问该成员&#xf…

java项目 异常如何解决_Java项目中常见的异常处理

发生异常的情况有很多,其中包括以下几大类:1. 空指针异常;2. 用户输入异常;3. 多层异常捕获;想要知道Java是如何处理异常的,就需要掌握以下这三种异常的处理:1.检查性异常:最具代表的…

C++ 基类和派生类的构造函数

在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。 解决办法:在派生类的构造函…

java i/o 流详解_java I/O流详解

概况I/O流主要分为二大类别:字符流和字节流。字节流(基本流)1、字节输入流 类名:FileInputStream 特点:读(对文件进行读取操作) 父类:InputStream2、字节输出流 类名:FileOutputStream 特点:写…

C++ 基类和派生类的析构函数

和构造函数类似,析构函数也不能被继承。与构造函数不同的是,在派生类的析构函数中不用显式地调用基类的析构函数,因为每个类只有一个析构函数,编译器知道如何选择,无需程序员干涉。 另外析构函数的执行顺序和构造函数…

素描java字母_进行Java基本GUI设计需要用到的包是

【单选题】下列各类计算机程序语言中,不属于高级程序设计语言的是【填空题】以下程序从名为 filea.dat 的文本文件中逐个读入字符并显示在屏幕上。请填空。 #include main() { FILE *fp; char ch; fp fopen( 【3】 ); ch fgetc(fp); while (!feof(fp)) { putchar(ch); chfget…

C++ 多继承

派生类都只有一个基类,称为单继承(Single Inheritance)。除此之外,C也支持多继承(Multiple Inheritance),即一个派生类可以有两个或多个基类。 多继承的语法也很简单,将多个基类用逗…

C++ 向上转型

在 C 中经常会发生数据类型的转换,例如将 int 类型的数据赋值给 float 类型的变量时,编译器会先把 int 类型的数据转换为 float 类型再赋值;反过来,float 类型的数据在经过类型转换后也可以赋值给 int 类型的变量。 数据类型转换…

java拆分任意五位数_五位数拆分出各位 - osc_foo7glsg的个人空间 - OSCHINA - 中文开源技术交流社区...

5、输入一个五位数,输出一个反转的五位数输入-》12345 输出-》54321//这是五位数字拆分方法。System.out.println("请输入五位数的数字:"); // 比如:12345int g1num%10;int g2num/10%10;int g3num/100%10;int g4num/1000%10;int g…

C++ 纯虚函数和抽象类

C中,可以将虚函数声明为纯虚函数,语法格式为: virtual 返回值类型 函数名 (函数参数) 0;纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上0,表明此函数为纯虚函数。 最后的0并不表示函数返回值为…

python中保留两位小数的编写程序_P081 保留两位小数

所属年份:2011.3;2011.9请编一个函数 float fun(double h),该函数的功能是:使变量h中的值保留两位小数,并对第三位进行四舍五入(规定h中的值为正数)。例如,若h 值为1234.567,则函数返回1234.570000&#xf…

C++ 获取类型信息

typeid 运算符用来获取一个表达式的类型信息。类型信息对于编程语言非常重要,它描述了数据的各种属性: 对于基本类型(int、float 等C内置类型)的数据,类型信息所包含的内容比较简单,主要是指数据的类型。对…

java 8 lambda 申明_2019-02-03——Java8 Lambda

一.认识LambdaLambda表达式专门针对只有一个方法的接口(即函数式接口)lambda表达式的基本格式为(x,y...)—>{表达式...};(1)可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。(2)可选的参数圆括号:一个参数无需定义圆括号&…

C 数字排列组合

编辑程序让任意四个数字排列组合并且每一个排列组合的数字都不相同。 结果展示 完整代码 #include <stdio.h> //定义头文件 int main() {int i,j,k;printf("\n");for(i1;i<5;i){ //三重循环 for(j1;j<5;j){for(k1;k<5;k){ //确保 i j k 位置不同 i…

java 汇总_java基础汇总

1.关于Http和Hibernatet里面Session的区别HttpSessionHttpSession&#xff1a;是一个抽象接口&#xff0c;J2EE的Web程序在运行的时候&#xff0c;会给每一个新的访问者建立一个HttpSession&#xff0c;这个Session是用户身份的唯一标示&#xff0c;WEB 会话跟踪用的。【注:是容…

C 天数的计算

根据程序提示输入某年某月某日&#xff0c;判断这一天是这一年的第几天&#xff0c;并进行输出显示。 效果演示 完整代码 #include <stdio.h> int main() {int day,month,year,sum,leap;printf("\n请输入年、月、日&#xff0c;格式为&#xff1a;年,月,日&#…

java+filter加密_Javaweb之Filter案例练习-自动登录问题和MD5加密

自动登录问题和MD5加密前面已经完成了Filter的自动登录&#xff0c;但是有问题&#xff0c;我们在web.xml中Filter的url-mapping中配置的规则是/*, 也就是这个网站的所有请求都拦截。这肯定不合适。我们本来访问/login.jsp&#xff0c;本来就是去登录&#xff0c;结果也进行了拦…

C 从小到大排序

将数字按从小到大的的顺序进行排序输出&#xff0c;根据提示输入相应的数字个数&#xff0c;每输入一个数字要进行换行然后输入下一个数字&#xff0c;当三个数字完全输入之后点击回车&#xff0c;程序会根据从大到小的顺序进行数字的排序输出。 效果演示 完整代码 #include…

java class文件常量池_JAVA程序员谈谈class文件结构中的常量池-class文件

常量的类型有12种CONSTANT_Utf8_info1字面量UTF-8编码的字符串CONSTANT_Integer_info3字面量整型字面量CONSTANT_Float_info4字面量浮点型字面量CONSTANT_Long_info5字面量长整型字面量CONSTANT_Double_info6字面量双精度浮点型字面量CONSTANT_Class_info7符号引用类或接口的符…

C 输出图案

在C语言中使用 * 号输出各种图案。 用 * 输出字母C 效果 完整代码 #include <stdio.h>int main() {printf("用 * 号输出字母 C\n");printf(" ****\n");printf("*\n");printf("*\n");printf(" ****\n");} 用 * 输出…