堪比Web SQL:開源項目讓IDB速度原地提昇數十倍

機器之心 2021-08-15 17:17:02 阅读数:120

本文一共[544]字,预计阅读时长:1分钟~
web sql idb 速度 原地
數據庫結構化 查詢語言 SQL,是絕大多數程序員都會經常用到的工具。不過這個東西入門容易精通難,有很多技巧和特性是一直被人忽略的。

最近,程序員 James Long 提出了 absurd-sql,它是 Web 上 SQLite 的持久化後端,不會將整個 數據庫加載到內存中,並且可以持續寫入。

James Long 撰寫了一篇博客來講解 Web 存儲 API(主要是 IndexedDB)的缺點,並介紹了 SQLite 在 absurd-sql 的加持下如何提供 10 倍的性能改進及其技巧。


項目地址:https://github.com/jlongster/absurd-sql

IndexedDB 並不是完美的

如果要編寫一個 web 應用程序,你可能會選擇使用 IndexedDB 來存儲數據,這是能在所有瀏覽器上運行 數據庫的唯一選項。

當試圖構建一個本地應用時,你會發現圍繞這個 數據庫構建整個應用並不理想,當然,其中的少量功能還是很有用的。但如果想構建更好的網絡應用程序,我們需要一種更强大的方式來處理數據。

IndexedDB 有一個缺點是速度緩慢。一般來說, 數據庫的簡單操作大約需要 10ms 的時間,profile SQLite 通常需要 0.01 ms 的時間,這些時間長短和具體編寫什麼應用程序有關。

在 IndexedDB 中 查詢數據,需要用戶手動操作。IndexedDB 唯一提供的函數是 count,其餘的 API 只返回一系列 item。用戶必須以特定的方式連接索引和構造數據,才能構建 查詢函數。

甚至添加一個新的對象存儲都很困難,如果用戶想打開 數據庫添加對象存儲,需要强制終止其他所有 tab 與 數據庫的連接。

IDB 不是完美甚至是有些低級的,因此作者提出了 absurd-sql。

absurd-sql

SQL 是構建應用程序的好方法。尤其是小型本地網絡應用程序。鍵 / 值存儲可能在大型分布式系統中占有一席之地,但是如果我們可以在 Web 上使用 SQLite 會不會很棒?

absurd-sql 使這成為了可能。absurd-sql 是 sql.js 的文件系統後端,它允許 SQLite 從 IndexedDB 讀 / 寫一小部分數據,就像磁盤一樣。

值得注意的是,absurd-sql 的項目作者受到了 SQLite 的啟發。

sql.js 是 SQLite 到 Webassembly 的一個端口,通過使用 Emscripten 編譯 SQLite C 代碼,它是 SQLite 在 Web 上應用的一個良好例子。它使用存儲在內存中的虛擬 數據庫文件,因此不會保留對 數據庫所做的更改。sql.js 將 SQLite 編譯為 WebAssembly,並允許讀取 數據庫和運行 查詢。這裏就存在一個重要的問題——用戶不能持續進行任何寫操作。它將整個 數據庫加載到內存中,並且只更改內存中的數據。刷新頁面後,所有更改都將丟失。

雖然內存 數據庫有其用途,但這會讓 SQLite 的用途變得很受限。開發者要使用它構建任何類型的應用程序,都需要具備數據持久化的能力。

absurd-sql 解决了這個問題,它通過攔截來自 SQLite 的讀 / 寫請求,將它們獲取並持久化到 IndexedDB(或任何其他持久後端)來工作。項目作者編寫了一個完整的文件系統層,該文件系統層知道 SQLite 如何讀取和寫入數據塊,並能够有效地正確執行操作。這意味著它永遠不會將 數據庫加載到內存中,而是只加載 SQLite 要求的任何內容,並且寫入始終保持不變。

項目作者錶示,這裏使用 sql.js 是因為它已經擁有一個龐大的社區,並且是迄今為止在 Web 上使用 SQL 的最常見方式。

安裝 absurd-sql 的代碼如下:

import initSqlJs from '@jlongster/sql.js';import { SQLiteFS } from 'absurd-sql';import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend';SQL = await initSqlJs();sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend());// This is temporary for nowSQL.register_for_idb(sqlFS);SQL.FS.mkdir('/sql');SQL.FS.mount(sqlFS, {}, '/sql');let db =new  SQL.Database('/sql/db.sqlite', { filename: true });

數據庫 db 的使用也與常規使用方法沒有什麼區別,並且還做到了有效地讀取和持久寫入 IndexedDB:

let stmt = db.prepare('INSERT INTO kv (key, value) VALUES (?, ?)');stmt.run(['item-id-00001', 35725.29]);let stmt = db.prepare('SELECT SUM(value) FROM kv');stmt.step();console.log(stmt.getAsObject());

這樣一操作,Web SQL 仿佛回來了!項目作者錶示:「雖然目前還不推薦將 absurd-sql 用於生產,但我移植了我的應用程序 Actual 來使用 absurd-sql,結果錶明還能够良好工作。」

速度提昇

IndexedDB 是當前唯一適用於所有瀏覽器的持久存儲,它將 數據庫和持久存儲兩種需求合二為一。

作者使用 sql.js + absurd-sql 抽象出存儲層。並討論了 absurd-sql 能讓 IndexedDB 變快多少。不過速度仍然比原生 SQLite 慢 50-100 倍,這是因為 IndexedDB 的寫入速度很慢,無法進行批量讀寫。

IndexedDB 只是 absurd-sql 的一個後端,作者還嘗試過使用 webkitFileSystem 做後端,結果發現其無法匹敵 IDB 後端的性能。 

實際上,SQLite 在各項性能指標上都能輕松擊敗 IndexedDB。該項目作者編寫了一個 基准測試應用程序來測試不同的場景,運行幾個不同的 查詢,並允許用戶以多種方式配置 SQLite 和基於原始 IndexedDB 實現運行測試。

測試結果:https://docs.google.com/spreadsheets/d/1Cpb9r3cZlbZgp1RoSTmh22wOPCRMqINzMUelUTIDo8Y/edit#gid=0
基准測試代碼:https://github.com/jlongster/absurd-sql/tree/master/src/examples/bench

以寫入為例,IDB 的速度比 SQLite 慢了數十倍:

批處理為 SQLite 提供了如此巨大的性能優勢,任何 CPU 處理都無法彌補差距。

而 absurd-sql 通過避免 IndexedDB 讀 / 寫節省了大量時間,以至於 CPU 命中的影響可以忽略不計。

實驗錶明,absurd-sql 能够很好地處理 1000000 item 的情况。從速度上看,absurd-sql 寫入需要 4-6s,讀取需要 2-3 秒;而 IndexedDB 完成這一過程,寫入大約是 2 或 3 分鐘,讀取大概在 1 分鐘左右。

參考內容:
https://jlongster.com/future-sql-web
版权声明:本文为[機器之心]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815171659548r.html