各个数据库存二进制大文件的性能测试

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。