1前言
有个项目软件前端将二进制大文件存在了indexDB,每次给后端传文件(需要传到底层C++进行调用)都会导致内存占用飙升,想着使用前后端都能共同操作的数据库来解决这个内存占用的问题,并且希望这个更具尽可能的轻量,可以嵌入到程序中是最好的,通过一个安装包进行安装。
2各个数据库的性能比较
2.1 MySQL
预先准备35MB的Byte数组数据,将其存到数据库的格式为blob,测试MySQL存储100块35MB的数据需要300秒,平均一块需要3s时间。
2.2 MongoDB
MongoDB的数据是采用键值对的形式进行存储,一样是采用100块35MB的数据进行写入,测试需要45s时间,平均一块是450ms。
2.3 SQLite测试及优化过程
2.3.1 概述
SQLite使用Mybatis作为ORM框架进行100块35MB的写入速度,100块使用了12s,平均一块35MB写入速度是120ms,远快于前面两种使用Mybatis读数据库blob格式出现不支持的问题,将ORM换到JdbcTemplate后,支持blob格式,进行读,一块35MB读了40s时间,猜测是JdbcTemplate是比较落后的方案,没有针对大文件优化。
2.3.2 优化流程
(一)表结构优化
主键id改为PrimaryKey,并且设置自增,使得每次都可以B+树主键索引命中查询内容。
(二)Mybatis优化
依然是换回springboot最主流的mybatis框架,手写mapper.xml文件处理blob格式,指定handler处理器编写对blob的处理,最终实现对blob的读取。优化后,对读取的数据不进行解析的话,平均一块35MB的数据读取50ms,涉及到数据解析(需要用byte数组接blob,并对整个数组中的数据解析取值)会再多一些时间。
(三)使用JPA
Mybatis除了对blob数据读取支持不好外,操作sqlite数据库时,使用自增id无法正常生成id的值,决定换到JPA进行尝试。
在JPA下,写100块35MB的数据如下:
可见写入速度接近于Mybatis和JdbcTemplate,都是100+ms。
测试读100块35MB的数据:
读的速度得到很大提升,30ms左右,快于mybatis下的50ms。
在测试35块100MB大小的数据的写操作:
写操作的时间基本成线性增长,100MB一块写入需要400ms+。
再测试读操作:
100MB的读操作性能还是很高,读一块100MB大小的分块是100+ms。(上述测试涉及到的读操作都是没有对数据进行解析的)
(四)针对写过程进行优化
当前场景下,需要不断对数据库执行多个插入操作,默认情况下,每次插入一个数据都会开始事务和提交事务,多次提交会重复这个流程,磁盘I/O频率高,尝试开启事务,在数据都准备好情况下再进行提交事务。在插入的方法上加入注解@Transactional,下面测试有无@Transactional情况下存10个100MB数据的情况。
左边是使用了注解后的结果,右边是没有,开启事务后提升是很明显的。一块100MB的分块写入从400ms降低到300ms,但会导致另外一个问题,如果35个100MB的数据同时存进去会出现java堆溢出,因为这3.5g在事务commit前都是放在内存里面导致溢出。
(五)针对读过程尝试进行优化
对读过程尝试调节了SQLite一些参数,但是基本没有提升。
设置PRAGMA synchronous = OFF,关闭写同步,PRAGMA synchronous = OFF,在 sqlite3 中 synchronous 有三种模式,分别是 FULL,NORMAL 和 OFF,在系统意外终止的时候,安全性逐级减弱,FULL模式下,保证数据不会损坏,安全性最高,写入速度也最慢。
设置PRAGMA page_size = 8192,通过设置PAGE_SIZE参数,调整最小存储单元PAGE的大小,由于存入的数据较大,尝试改成8192,甚至16384。
设置完没有提升,猜测上面参数是针对数量条数很大的情况才有提升,当前情况是数据条数不多,但单独的每条数据占用内存大。
2.4 LevelDB
Leveldb也是轻量级数据库,属于NOSQL,是以key-value形式进行存储,Java和node.js都能同时使用,但是经过测试,同样存入35个100MB分块大小的数据,每一块存入时间是400ms,每一块读取时间也接近400ms,相比较jpa下的sqlite表现,并没有提升。
2.5 IndexDB(原有方案)
读100MB的分块是50毫秒,读性能非常的好。
写入100MB分块的时候,算上json转blob 以及入库的时间,大概是100-200ms,时间远短于尚未做数据处理的sqlite(事务下300-400ms)。
3 结论
上述所测试的数据库性能都不如原有方案下的indexDB,最接近indexDB的sqlite,读写性能数据比较如下:
100MB分块的写入,包括json数据解析,indexDB是100-200ms,sqlite不包括数据解析在开启事务的情况下是300-400ms,不开启事务则是400-500ms。
100MB分块的读取,不包括数据解析:indexDB是50ms,sqlite是100-200ms。