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。