開發一個大型Electron的應用,或許須要在客戶端存儲大量的數據,好比聊天應用或郵件客戶端html
可選的客戶端數據庫方案看似不少,但一一對比下來,最優解只有一個git
接下來咱們就一塊兒來經歷一下這個技術選型的過程:github
排除:把數據以Json的形式存儲在文件中
以這種方式存儲一些用戶的配置信息是徹底沒問題的(用戶名、家庭住址、是否開啓免打擾模式等)sql
但要用這種方式存儲大量解構化的數據,就很是不科學了數據庫
主要緣由是:npm
用這種方案操做數據是須要把文件中的全部數據都加載到客戶端電腦的內存中去的緩存
因爲沒有索引機制,關聯查詢、條件查詢等操做效率不高,性能優化
更新了某項數據以後,要持久化更新操做,又要重寫整個文件。服務器
PS:app
若是你的應用操做的數據量很少,
你能夠選擇相似lowdb(https://github.com/typicode/lowdb)這樣的工具,
在必定程度上環節這些困難
排除:LocalStorage、SessionStorage、WebSql、Cookies
Cookies存儲容量過小,只能存4kb的內容,並且每次與服務端交互,同域下的Cookie還會被攜帶到服務端,也沒有關聯查詢、條件查詢的機制
LocalStorage存儲容量也很小,大概不會超過10M,它是以鍵值對形式保存數據的,一樣也沒有關聯查詢、條件查詢的機制
SessionStorage最大的問題是,每次關閉應用程序,它裏面的內容會被清空,想持久化存儲數據,就不用考慮它了
WebSql諸般特性都挺好,無奈這個技術已經被W3C委員會否決了,不知道哪天Electron也不支持了,到時就傻眼了
分析
如今可選的成熟方案几乎只剩下SQLite和IndexedDB了,
SQLite是一個輕型的、嵌入式的SQL 數據庫引擎,其特色是自給自足的、無服務器、零配置的、支持事務。它是在世界上最普遍部署的 SQL 數據庫引擎。
IndexedDB是Chromium內置的一個基於JavaScript的面向對象的數據庫,在Electron應用內它存儲的容量限制與用戶的磁盤容量有關,是用戶磁盤大小的1/3
市面上選這兩個方案的商業產品各都有不少
那麼到底哪一個好呢?
接下去咱們就作一個性能的對比
SQLite和IndexedDB性能對比
測試環境
CPU:I9 9900K 3.6GHZ
內存:32G
OS:Win10
環境搭建
SQLite環境
訪問SQLite數據使用的是knexjs操做庫,它是一個sql生成器,支持Promise API,鏈式操做很是好用,推薦使用
在Electron應用內安裝SQLite,比較特殊,須要使用以下安裝指令:
npm install sqlite3 --build-from-source --runtime=electron --target=9.0.0 --dist-url=https://atom.io/download/electron
注意:--target後面的內容與你使用的Electron的版本要一致
SQLite的數據庫表結構
CREATE TABLE [message]( [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, [msg_from] VARCHAR(80), [msg_to] VARCHAR(80), [msg] TEXT, [create_time] DATETIME);
這裏主要模擬了一個IM應用的消息表
SQLite的測試代碼以下
let { app } = require('electron'); let messages = require('./messages') let path = require('path'); let filename = path.join(app.getPath('userData'), 'db.db'); let db = require('knex')({ client: 'sqlite3', useNullAsDefault: true, connection: { filename }, timezone: 'UTC', dateStrings: true }); let start = async () => { let startTime = Date.now(); for (let i = 0; i < 10; i++) { let index = i % 2; await db('message').insert(messages[index]); } //let arr = await db('message').whereBetween('id',[1600,9600]); //await db('message').whereBetween('id',[0,10000]).del(); //await db('message').update({msg:`天接雲濤連曉霧。!!!`}).whereBetween('id',[2600,2800]); let endTime = Date.now(); console.log(endTime - startTime); } module.exports = { start }
其中用到了messages是兩個消息體的JSON對象,代碼以下:
let messages = [{ msg_from: '辛棄疾', msg_to: '劉曉倫', msg: `醉裏挑燈看劍,夢迴吹角連營。 八百里分麾下炙, 五十弦翻塞外聲, 沙場秋點兵。 馬做的盧飛快, 弓如霹靂弦驚。 了卻君王天下事, 贏得生前身後名。 可憐白髮生!`, create_time: new Date() }, { msg_from: '李清照', msg_to: '劉曉倫', msg: `天接雲濤連曉霧。 星河欲轉千帆舞。 彷彿夢魂歸帝所, 聞天語, 殷勤問我歸何處。 我報路長嗟日暮, 學詩謾有驚人句。 九萬里風鵬正舉。 風休住, 蓬舟吹取三山去!`, create_time: new Date(), }]; module.exports = messages
IndexedDB環境
IndexedDB的測試代碼是在渲染進程中執行的,代碼以下:
let Dexie = require('Dexie'); const db = new Dexie('db'); db.version(1).stores({ message: '++, message_from, message_to,msg,create_time' }); window.onload = async () => { let startTime = Date.now(); for (let i = 0; i < 10000; i++) { let index = i % 2; await db.message.add(messages[index]); } //let arr = await db.message.where("id").between(1000, 9000).delete(); let endTime = Date.now(); console.log(endTime - startTime); }
測試結果
插入
連續插入100行數據,執行8次
[ { name: 'SQLite', data: [526,551,536, 897, 530, 509, 534,538] }, { name: 'IndexedDB', data: [333,221,167, 169, 336, 313, 187,169] } ]
連續插入1000行數據,執行7次
[ { name: 'SQLite', data: [5669,7488,7443,7033,7231,7537,7563] }, { name: 'IndexedDB', data: [2140,2111,1755,1716,2126,1757,2006] } ]
連續插入10000行數據,執行4次
[ { name: 'SQLite', data: [202415,158451,144221,143993] }, { name: 'IndexedDB', data: [20028,18979,21013,18738] } ]
已存在10000行數據的前提下,再插入10行數據
[ { name: 'SQLite', data: [158,268,306,162,149,159] }, { name: 'IndexedDB', data: [56,99,47,49,53,52] } ]
檢索
在10000行數據中按主鍵檢索8000行數據
[ { name: 'SQLite', data: [47,55,56,60] }, { name: 'IndexedDB', data: [62,54,58,55] } ]
刪除
SQLite
已存在10000行數據的前提下,刪除200行數據(毫秒):1八、1六、18
已存在10000行數據的前提下,刪除8000行數據(毫秒):18
已存在10000行數據的前提下,刪除10000行數據(毫秒):18
IndexedDB
已存在10000行數據的前提下,刪除200行數據(毫秒):2一、十、10
已存在10000行數據的前提下,刪除8000行數據(毫秒):58
已存在10000行數據的前提下,刪除10000行數據(毫秒):30
更新
SQLite
已存在10000行數據的前提下,更新1行數據(毫秒):八、八、八、九、八、8
已存在10000行數據的前提下,更新100行數據(毫秒):30、30、2八、30、30
IndexedDB
已存在10000行數據的前提下,更新1行數據(毫秒):十一、八、七、七、八、8
已存在10000行數據的前提下,更新100行數據(毫秒):1五、1四、十二、十、13
結論分析
結論:插入數據兩個數據庫性能相差巨大,IndexedDB顯然優於SQLite,檢索,刪除,更新操做兩個數據庫性能相差無幾
分析:
SQLite有雙寫入機制,IndexedDB應該是有多級緩存寫入機制(待考),顯然多級緩存寫入機制更優秀
由於是Electron工程下完成此對比,因此Js經Electron轉到Node.js再轉到SQLite的Node module最後才轉到SQLite的C代碼,這個過程多是性能損耗的一大主要緣由
最後:
綜合對比下來,大型Electron應用更推薦使用IndexedDB來存儲業務數據
(因爲有Dexie的加持,IndexedDB操做也足夠簡單,全部中小型應用也是不錯的選擇)
若是你須要加密客戶端數據,SQLite還須要外套sqlcipher這樣的加密庫,因此性能上會有更多損耗,
然而IndexedDB自己就有一層加密邏輯(能夠說只能防君子,防不了小人),雖然簡單,但聊勝於無。
最後
歡迎你們購買個人新書《Electron實戰:入門、進階與性能優化》,
書裏還有更多有趣的內容,
你們感興趣能夠加QQ羣949674481交流。
噹噹:http://product.dangdang.com/28547952.html;
京東:https://item.jd.com/12867054.html