存儲引擎及如何選擇合適的存儲 API

原文請查閱這裏,略有刪減,本文采用知識共享署名 4.0 國際許可協議共享,BY Trolandjavascript

本系列持續更新中,Github 地址請查閱這裏java

這是 JavaScript 工做原理的第十六章。git

概述

每當設計網頁程序的時候,爲本地設備選擇合適的存儲機制尤其重要。一個好的存儲引擎能夠幫助開發人員有效地存儲數據,減小傳輸帶寬及提升程序的響應速度。正確的存儲緩存策略是構建移動端離線網頁體驗的核心組成部分,愈來愈多的用戶想固然覺得能夠離線使用移動端網頁程序。github

本章,咱們將討論各類可用的存儲 API 和服務,並提供一些在構建網頁程序時如何正確地選擇存儲引擎的建議。web

數據模型

數據存儲模型決定了其內部是如何組織存儲數據的。這會影響到整個網頁程序的設計,並計算出爲獲取高性能網頁程序及解決其所遇到的問題所須要的代價。沒有所謂更好的技術和一刀切的解決方案,由於全部的問題都是工程學相關的問題。那麼,讓咱們來瞧瞧可供選擇的數據模型吧:數據庫

  • 結構型:以預約義字段將數據存儲於表中,因其爲典型的基於 SQL 的數據庫管理系統,因此能夠很好地適應靈活和動態的數據查詢。IndexedDB 即瀏覽器端結構型數據庫的一個典型例子。
  • 鍵/值型:鍵/值數據存儲及關係型 NoSQL 數據庫,容許開發者經過惟一鍵索引來存儲和獲取非結構型數據(即非預約數據類型的字段的數據)。鍵/值數據存儲就像哈希表存儲,意及其容許在必定時間內訪問索引的不定數據類型的數據。鍵/值數據型存儲的很好的例子有瀏覽器端的 Cache API 和 服務器端 Apache Cassandra。
  • 字節流型:這一簡單的模型把數據存儲爲定長,混淆字符串的字節變量,讓應用層來控制其內部數據組織。該模型尤爲適合於文件存儲和其它層次型組織的 blob 數據。字節流存儲的典型例子包括文件系統和雲存儲設備。

持久性

網頁程序的數據存儲方法能夠以數據的存儲時長來進行分析:api

  • 會話持久性:僅當活動的單個網頁會話或者瀏覽器選項卡時數據有效,關閉即失效。會話持久性數據存儲一個例子即 Session Storage API
  • 設備持久性:該類數據存儲於指定設備的跨會話和瀏覽器選項卡/窗口有效。設備持久性存儲機制的一個例子即 Cache API
  • 全局持久性:該類數據跨會話和設備存儲。所以,它是兼容性最好的數據持久性方案。它不會存儲於設備上,這意味着須要從服務端存儲中得到數據。由於這裏只討論針對設備的數據存儲,因此這裏只是稍微提下服務端數據存儲。

客戶端數據持久性

現現在,有至關多的瀏覽器 API 可供選擇用於存儲數據。這裏將詳細討論這些方法,而後對其進行比較以便讓開發者輕鬆地選擇正確的數據存儲方案。跨域

然而,首先在選擇如何存儲數據以前,開發者須要考慮幾件事情 。固然了,第一件事即必須想清楚打算如何使用網頁程序及以後的維護和性能優化。即便成竹在胸,可代選擇的方案可能只有幾個。如下爲開發者須要考慮的問題:瀏覽器

  • 瀏覽器支持-優先考慮標準化和組織良好的 API,由於這些 API 不會輕易變更且兼容性好。這些 API 一樣有很是豐富的文檔和活躍的開發者社區。
  • 事務-有時候,事務對於相關的數據存儲操做集原子化成功或失敗相當重要。傳統數據庫使用事務模型來實現該功能,在事務模型中以把相關數據更新劃分爲任意的單元。
  • 同步/異步-少數存儲 API 是同步的意即存儲或者檢索數據請求會阻塞當前活躍線程直到數據請求結束。使用同步數據存儲 API 會阻塞主線程且會讓程序界面假死。儘可能使用異步存儲 API。

對比

這裏,讓咱們瀏覽一下網頁開發者當前可用的 API 並使用上述的幾個維度來進行比較。緩存

文件系統 API

0_9kpehy4mub8f-hsp

有了 FileSystem API,網頁程序就能夠在用戶本地文件系統的沙箱中進行新建,讀取,操做和寫文件。

該接口包含以下幾個部分:

  • 讀取和操做文件:File/BlobFileListFileReader
  • 新建和寫文件:Blob()FileWriter
  • 訪問目錄和文件系統:DirectoryReaderFileEntry/DirectoryEntryLocalFileSystem

文件系統 API 並非標準的 API.因其兼容性不太好,因此切記不要在生產環境中使用。各類瀏覽器廠商的實現會有很大的不一樣且該 API 之後可能會變動。

文件和目錄條目 API 接口文件系統用來表示一個文件系統。可從任意文件系統條目的 filesystem 屬性中獲取這些 對象。少數瀏覽器提供了額外的 API 來建立和操做文件系統。

該接口不會容許開發者訪問用戶的文件系統。相反,開發者會在瀏覽器沙箱內得到一個虛擬磁盤。若想要訪問用戶的文件系統,能夠採起安裝 Chrome 插件的方法。

得到文件系統

網頁程序可調用 window.requestFileSystem() 來訪問沙箱文件系統。:

// 注意: 該文件系統以 Google Chrome 12 爲前綴The file system has been prefixed as of Google Chrome 12:
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(type, size, successCallback, opt_errorCallback)
複製代碼

第一次調用 requestFileSystem() 方法的時候會新建一個本地存儲。須要注意的是該文件系統是沙箱型的,意即網頁程序不能夠訪問其它程序的文件。

在得到訪問文件系統的權限後,開發者能夠對文件和目錄進行大部分常規文件系統操做。

和其它存儲策略相比,FileSystem 大有不一樣,它旨在知足數據庫不能很好地解決客戶端存儲的狀況。通常來講,程序用來處理大型二進制 blobs 文件或者和瀏覽器上下文以外的程序分享數據。

如下爲使用 FileSystem 的好範例:

  • 斷點續傳工具-當選擇文件或者文件目錄來上傳的時候,會把文件複製進沙箱後一次上傳一個分片。
  • 視頻遊戲,音樂或者其它含有大量媒體文件的程序
  • 爲了更好的性能提供離線訪問或者本地緩存功能的音頻/圖片編輯器-數據 blobs 通常會很是龐大且須要可讀寫
  • 離線視頻播放器-其須要下載大型文件以備以後觀看或者快速的尋軌-緩衝
  • 離線網頁郵件客戶端-下載附件且進行本地存儲

如下爲該 API 的當前瀏覽器支持狀況:

0_ndu4n8xqf6qeqmsy

Local storage

0_asohzlowolitnuel

localStorage API 容許開發者訪問 文檔 源的 Storage 對象。存儲的數據在多個瀏覽器會話之間仍然有效。localStorage 和 sessionStorage 相似,只不過存儲在 localStorage 中的數據沒有過時時間,而存儲在 sessionStorage 中的數據會在頁面會話結束時丟失-意即當關閉頁面時即丟失。

不管是 localStorage 仍是 sessionStorage 其數據只存儲在特定的頁面源中,所謂頁面源包含協議,主機名和端口。

如下爲該 API 的當前瀏覽器支持狀況:

0_hxc_nupnycubhj-l

Session storage

0_-imsnws_l1g0syla

sessionStorage 屬性容許開發者訪問當前源的會話 Storage 對象。前面簡述過,sessionStorage 和 localStorage 相似。惟一的區別即,存儲在 localStorage 中的數據沒有過時時間而 sessionStorage 中的數據會在頁面會話結束時丟失。頁面會話的時效爲瀏覽器打開時且在頁面重載和恢復時。在新的選項卡中打開新頁面或者窗口會致使從新初始化一個新的會話,這與會話 cookies 的工做機制是不同的。

請注意不管數據存儲於 sessionStorage 仍是 localStorage 都僅只在指定的頁面源中有效。

如下爲該 API 的當前瀏覽器支持狀況:

0_hxc_nupnycubhj-l

Cookies

0_vkqiniyfu2o7d7bh

所謂 cookie(網頁 cookie,瀏覽器 cookie) 指的是由用戶的服務器發送到客戶端的一小段數據。瀏覽器將其存儲下來而後在下一次請求的時候捎帶上它發往服務器。典型地,它被用來告知兩個請求是否來自於同一個客戶端-好比保持用戶登陸狀態。它爲 無狀態 HTTP 協議記錄有狀態信息。

Cookies 有如下三個主要用途:

  • 會話管理-登陸,購物車,遊戲積分或者其它須要在服務端存儲的數據。
  • 個性化-用戶參數,皮膚和其它設置
  • 監控-記錄和分析用戶行爲

Cookies 曾經一統客戶端存儲方案。當它是客戶端存儲的惟一方案的時候,這是不二選擇,現現在推薦選擇使用現代存儲 API 來存儲客戶端數據。每次發送請求都會捎帶上 Cookies,因此會影響性能(特別是當在一個移動端請求數據的時候)。

有兩種類型的 cookies:

  • 會話 cookie-當用戶關閉瀏覽器時失效。網頁瀏覽器可使用恢復會話技術來固化大多數會話 cookie,就好像不曾關閉瀏覽器同樣。
  • 永久性 cookie-和客戶端關閉即過時相反,永久性 cookie 會在指定的過時時間過時或者在一個指定的時間(Max-age)後過時。

請注意不要在 cookie 中存儲憑據或者敏感信息,因其固有的不安全缺陷機制。

然而,不肖說,cookie 是兼容性最好的方案。

Cache

0_xz2u-ztabhwjosky

Cache 接口是緩存請求/響應對象的存儲機制。請注意和 workers 同樣可在窗口做用域內使用 Cache 接口。雖然 Cache 是在服務工做線程規範中定義的,但這並不表示必定要和服務工做線程一塊兒使用。

一個源能夠擁有多個命名的緩存對象。開發者只須要在腳本(好比在服務工做線程中)中實現如何處理更新緩存便可。除非顯示請求不然不會更新緩存中的對象,只能經過刪除緩存對象,不然不會過時。使用 CacheStorage.open() 來打開指定命名的緩存對象,而後調用任意的緩存方法來維護緩存。

開發者須要定時清除緩存條目。每一個源在瀏覽器端都有限額的緩存數據。使用 StorageEstimate 來估算使緩存配額使用率。瀏覽器盡力管理硬盤空間,但它有可能會刪除指定源的緩存數據。瀏覽器可能會刪除指定源的全部數據抑或不會。切記使用名稱來對腳本進行版本控制且只操做能夠安全操做的腳本版本。查看 Deleting old caches 以獲取更多信息。

CacheStorage 接口表示 Cache 對象存儲。

接口:

  • 提供一個能夠爲 ServiceWorker,其它類型工做線程或者 window 做用域可訪問的全部命名的緩存的主目錄(雖然是在 服務工做線程 中定義的緩存,可是並不意味着只能將其和工做線程配合使用)
  • 維護一份字符名稱和 Cache 對象的映射

使用 CacheStorage.open() 來建立 Cache 實例。

使用 CacheStorage.match() 來檢查指定的 Request 是不是 CacheStorage 對象中的 Cache 對象的鍵。

使用經過全局 caches 屬性來訪問 CacheStorage。

IndexedDB

0_hp66xm7oe9u8ofk1

IndexedDB 是一種客戶端持久性數據存儲方案。因其容許開發者建立擁有富查詢能力的網頁程序而不用關心網絡狀況,這些網頁程序能夠線上或者離線運行。

IndexedDB 適用於大量的數據存儲(好比,商業圖書館DVD 目錄)和不須要保持網絡連通的網頁程序(好比,郵件客戶端,待辦事項及便箋)。

因你們都比較熟悉其它存儲 API ,本文將對 IndexedDB 多嘮會嗑。另外,現在隨着網頁程序愈來愈複雜 IndexedDB 也正變得愈來愈流行。

IndexedDB 原理

IndexedDB 容許開發者使用鍵來存儲和獲取一個對象。全部對數據庫的操做均發生於事務之中。和其它網頁存儲方案同樣,IndexedDB 遵循同源策略。所以,不可以跨域訪問數據,只能訪問同一個域名下的存儲數據。

能夠在包括 網頁服務線程 的大多數上下文上使用該異步 API。IndexedDB 曾經也有 synchronous 版本,應用於網頁線程中,可是因爲社區對此並不感冒因此被從規範中刪除了。

IndexedDB 曾經也有一個被稱爲 WebSQL 數據庫的競品規範,可是已經被 W3C 所棄用。雖然 IndexedDB 和 WebSQL 均是存儲方案,可是它們功能並不同。WebSQL 數據庫是一個關係型數據庫訪問系統,而 IndexedDB 只是一個索引表系統。

不要以其它類型數據庫爲藍本,想固然地使用 IndexedDB。相反,須要仔細閱讀文檔。如下爲開發者所須要瞭解的核心概念:

  • IndexedDB 數據庫存儲鍵-值對-值能夠是複雜的結構型對象而鍵能夠是這些對象的屬性。開發者可使用對象的任意屬性來建立索引進行快速搜索,好比枚舉排序。鍵也能夠是二進制型對象。
  • IndexedDB 是創建在事務型數據模型之上的-IndexedDB 中的全部操做都發生於事務上下文之中。所以,開發者不能夠在事務以外執行命令或者打開遊標。一樣地,事務只能自動而不能夠手動提交。
  • **大多數 IndexedDB 都是異步的 **-API 不會經過返回值地形式來返回數據。相反,須要傳入回調函數來處理返回值。意即,開發者不是同步把值存儲進數據庫或者直接從數據庫中取回值。相反,發起 request 請求即表示一次數據庫操做。當數據處理結束會通知開發者,開發者所監聽的事件類型會通知數據操做是否成功。這和 XMLHttpRequest (或者其它這麼多 JavaScript 相關的東西) 的工做原理大同小異。
  • IndexedDB 使用大量的請求-請求是對象用來接收以前提到的成功或者失敗事件。它們包含 onsuccess 和 onerror 屬性,和 readyState,result,errorCode 等用來告知請求狀態的屬性同樣。
  • IndexedDB 是面向對象的-IndexedDB 並非一個含有表示行列集合的表關係型數據庫。這一巨大的差別影響開發者設計和構建網頁程序。
  • IndexedDB 不使用結構型查詢語言(SQL)-它在索引上使用查詢後會建立一個遊標,可使用該遊標來遍歷結果集。若不熟悉 NoSQL 系統,能夠閱讀 維基百科關於 NoSQL 的文章
  • IndexedDB 也應用了同源策略-一個源即包含域名,應用程序層協議及 URL 端口地址的文檔,腳本即在源中執行。每一個源都擁有其關聯的數據庫集。每一個數據庫在源中都有惟一的標識。

IndexedDB 侷限性

IndexedDB 被設計用來知足大多數的客戶端存儲狀況的。然而,它並無被設計用來處理以下狀況:

  • 國際化排序-並非全部的語言以一樣的方式排列字符串,所以國際化排序是不支持的。雖然數據庫並不可以以指定的國際化順序來存儲數據,開發者能夠讀取數據庫中數據而後自行排列數據。
  • 同步- API 並非用來和服務端數據進行同步的。開發者必須本身寫代碼來把客戶端 indexedDB 數據庫和服務端數據庫進行同步。
  • 全文檢索-該 API 中沒有和 SQL 中的 LIKE 相似的操做符。

另外,須要注意的是瀏覽器會在如下狀況清除數據庫:

  • 用戶發起清除操做的請求-許多瀏覽器都容許用戶清除指定網站的數據,包括 cookie, 書籤,存儲的密碼以及 IndexedDB 數據。
  • 瀏覽器在隱私模式下-一些瀏覽器含有『隱私瀏覽』(Firefox) 和 『無痕瀏覽』(Chrome)模式。會話結束會清除數據庫。
  • 超出了磁盤容量或者磁盤限額。
  • 數據損壞。。。

雖然現實狀況和瀏覽器能力突飛猛進,可是瀏覽器產商都朝着盡一切可能保存數據的方向努力。

0_kgdqye70_z58d7na

選擇合適的存儲 API

正如以前所說的那樣,最好儘量採用兼容性好的 API 且提供異步調用模型來最大限度地提高 UI 響應速度。這些標準天然而然會產生以下技術選擇:

  • 使用 Cache API 來操做離線存儲。該 API 在建立離線應用所必須的支持 服務工做線程 功能的瀏覽器中可用。Cache API 很是適用於排列已知 URL 的關聯資源。
  • 使用 IndexedDB 來存儲程序狀態和用戶生成的內容。和只支持 Cache API 的瀏覽器相比,這使得用戶能夠在更多的瀏覽器中離線使用程序。

參考

招賢納士

今日頭條招人啦!發送簡歷到 likun.liyuk@bytedance.com ,便可走快速內推通道,長期有效!國際化PGC部門的JD以下:c.xiumi.us/board/v5/2H…,也可內推其餘部門!

本系列持續更新中,Github 地址請查閱這裏

相關文章
相關標籤/搜索