IndexedDB数据库使用
创始人
2025-05-29 15:59:41
0次

最近业务需求中需要使用到数据库,在翻看vscode源码过程中,发现vscode一共用了两种数据库存储数据,SQLite和IndexedDB,本文主要对IndexedDB做下讲解

简介

2008 年左右,网站 、 论坛、社交网络开始高速发展,传统的关系型数据库在存储及处理数据的时候受到了很大的挑战 ,其中主要体现在以下几点:

  • 难以应付每秒上万次的高并发数据写入 。
  • 查询上亿量级数据的速度极其缓慢 。
  • 分库、分表形成的子库到达一定规模后难以进一步扩展 。
  • 分库、分表 的规则可能会因为需求变更而发生变更。
  • 修改表结构困难 。

在很多 互联网应用场景下 , 对数据联表的查询需求不是那么强烈 ,也并不需要在数据写入后立刻读取,但对数据的读取和并发写入速度有非常高的要求 。 在这样的情况下 ,非关系型数据库得到高速的发展 。

关系型数据库:

  • Oracle
  • Mysql
  • …

非关系型数据库:

  • MongoDB
  • Redis
  • indexedDB
  • …

背景

随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。

  • Cookies存储容量太小,只能存4kb的内容,而且每次与服务端交互,同域下的Cookie还会被携带到服务端,也没有关联查询、条件查询的机制。
  • LocalStorage存储容量也很小,大概不会超过10M,它是以键值对形式保存数据的,同样也没有关联查询、条件查询的机制。
  • SessionStorage最大的问题是,每次关闭应用程序,它里面的内容会被清空,想持久化存储数据,就不用考虑它了。
  • WebSql诸般特性都挺好,无奈这个技术已经被W3C委员会否决了,不知道哪天Electron也不支持了,到时就傻眼了。

所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。

通俗地说,IndexedDB 就是浏览器提供的本地数据库,一个基于 JavaScript 的面向对象数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。

特点

IndexedDB 具有以下特点。

(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

(4)同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

(5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。

(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

使用

  • 数据库:IDBDatabase 对象
  • 对象仓库:IDBObjectStore 对象
  • 索引: IDBIndex 对象
  • 事务: IDBTransaction 对象
  • 操作请求:IDBRequest 对象
  • 指针: IDBCursor 对象
  • 主键集合:IDBKeyRange 对象

我们以类的形式对它封装:

export class IndexedDB {static async create(name: string, version: number | undefined, stores: string[]): Promise {const database = await IndexedDB.openDatabase(name, version, stores);return new IndexedDB(database, name);}static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise {mark(`code/willOpenDatabase/${name}`);try {return await IndexedDB.doOpenDatabase(name, version, stores);} catch (err) {if (err instanceof MissingStoresError) {console.info(`Attempting to recreate the IndexedDB once.`, name);try {// Try to delete the dbawait IndexedDB.deleteDatabase(err.db);} catch (error) {console.error(`Error while deleting the IndexedDB`, getErrorMessage(error));throw error;}return await IndexedDB.doOpenDatabase(name, version, stores);}throw err;} finally {mark(`code/didOpenDatabase/${name}`);}}private static doOpenDatabase(name: string, version: number | undefined, stores: string[]): Promise {return new Promise((c, e) => {const request = window.indexedDB.open(name, version);request.onerror = () => e(request.error);request.onsuccess = () => {const db = request.result;for (const store of stores) {if (!db.objectStoreNames.contains(store)) {console.error(`Error while opening IndexedDB. Could not find '${store}'' object store`);e(new MissingStoresError(db));return;}}c(db);};request.onupgradeneeded = () => {const db = request.result;for (const store of stores) {if (!db.objectStoreNames.contains(store)) {db.createObjectStore(store);}}};});}private static deleteDatabase(indexedDB: IDBDatabase): Promise {return new Promise((c, e) => {// Close any opened connectionsindexedDB.close();// Delete the dbconst deleteRequest = window.indexedDB.deleteDatabase(indexedDB.name);deleteRequest.onerror = (err) => e(deleteRequest.error);deleteRequest.onsuccess = () => c();});}private database: IDBDatabase | null = null;private readonly pendingTransactions: IDBTransaction[] = [];constructor(database: IDBDatabase, private readonly name: string) {this.database = database;}hasPendingTransactions(): boolean {return this.pendingTransactions.length > 0;}close(): void {if (this.pendingTransactions.length) {this.pendingTransactions.splice(0, this.pendingTransactions.length).forEach(transaction => transaction.abort());}if (this.database) {this.database.close();}this.database = null;}runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest[]): Promise;runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest): Promise;async runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest | IDBRequest[]): Promise {if (!this.database) {throw new Error(`IndexedDB database '${this.name}' is not opened.`);}const transaction = this.database.transaction(store, transactionMode);this.pendingTransactions.push(transaction);return new Promise((c, e) => {transaction.oncomplete = () => {if (isArray(request)) {c(request.map(r => r.result));} else {c(request.result);}};transaction.onerror = () => e(transaction.error);const request = dbRequestFn(transaction.objectStore(store));}).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1));}async getKeyValues(store: string, isValid: (value: unknown) => value is V): Promise> {if (!this.database) {throw new Error(`IndexedDB database '${this.name}' is not opened.`);}const transaction = this.database.transaction(store, 'readonly');this.pendingTransactions.push(transaction);return new Promise>(resolve => {const items = new Map();const objectStore = transaction.objectStore(store);// Open a IndexedDB Cursor to iterate over key/valuesconst cursor = objectStore.openCursor();if (!cursor) {return resolve(items); // this means the `ItemTable` was empty}// Iterate over rows of `ItemTable` until the endcursor.onsuccess = () => {if (cursor.result) {// Keep cursor key/value in our mapif (isValid(cursor.result.value)) {items.set(cursor.result.key.toString(), cursor.result.value);}// Advance cursor to next rowcursor.result.continue();} else {resolve(items); // reached end of table}};// Error handlersconst onError = (error: Error | null) => {console.error(`IndexedDB getKeyValues(): ${toErrorMessage(error, true)}`);resolve(items);};cursor.onerror = () => onError(cursor.error);transaction.onerror = () => onError(transaction.error);}).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1));}
}

在服务里面使用

import { IndexedDB } from 'vs/base/browser/indexedDB'; //引入
private readonly whenConnected!: Promise; //定义私有变量存储数据库

初始化 constructor

this.whenConnected = this.connect(); //链接数据库
private async connect(): Promise {try {return await IndexedDB.create('indexedDB-test', undefined, ['test-store1']);} catch (error) {throw error;}
}

在方法里面使用

let indexedDB = await this.whenConnected;// 增
try {await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.add({ 'test': '222' }, 'key3'));
} catch (e) {console.log('存储数据出错')
}// 删
try {await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.delete('key3'));
} catch (e) {console.log('删除数据出错');
}// 改
try {await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.put({ 'lichangwei': '123' }, 'key3'));
} catch (e) {console.log('删除数据出错');
}// 查
const value = await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.getAll());

有问题欢迎留言

参考阮一峰日志

相关内容

热门资讯

在大吉订购APP订购交易亏损被...   近期,不少投资者反映在“大吉订购App”上投资白银、铂金等商品遭遇严重亏损。该平台宣称提供现货订...
铂银壹号APP商品订购交易亏损...   许多中小投资者误以为网上的“商品现货订购”是合规合法的理财渠道,殊不知这背后隐藏着巨大的合规风险...
兴鑫淘金APP贵金属订购交易亏...   白银、贵金属或其他商品类投资,除国家批准的正规期货交易场所及持牌期货公司外,市面上大量第三方交易...
铂银掘金APP无资质的违规平台...   铂银掘金等平台的投资者,往往是被“注册送现金”“充值返现券”“首单免亏”等广告话术吸引,进而下载...
八方淘金APP订购交易不合规,...   “八方淘金APP”“铂银掘金APP”“铂银壹号APP”等软件被指以“现货订购”为名,实际采用高杠...
在指尖订购APP订购商品亏损被...   指尖订购APP这类平台,表面是“订购交易”,实则是披着电商外衣的非法期货陷阱。绝大多数投资者被蒙...
在犀贝易购APP做商品订购交易...   市面上打着“现货投资”“贵金属交易”旗号的APP并不少,但真正合规、受监管的平台并不多。犀贝易购...
易达淘金APP虚假商品订购交易...   易达淘金APP存在严重的虚假宣传与违规交易行为。该平台打着“商品订购”的旗号,实则通过不合规的交...
大浪淘金APP白银订购亏损被骗...   大浪淘金APP最隐蔽的套路,在于其完全封闭的内盘交易系统。表面上,它宣称与国际贵金属市场接轨;实...
一品好购APP上订购白银出现亏...   一品好购APP原名“一品订购”,运营方为上海宝智未来信息技术有限公司。近期不少投资者反映在该平台...
九州订购APP贵金属订购投资骗...   九州订购APP正通过抖.音、快手、今日头条等平台大量投放投资广告,用“5元起投”“充值返现”“0...
坚锅淘金APP交易软件并不正规...   当前,市面上大量所谓的“现货商城订购”交易软件,早已偏离了正常的实物商品买卖,演变为高杠杆、高手...
斗金订购APP贵金属期货交易骗...   斗金订购APP的投资者往往被铺天盖地的广告宣传所诱导——注册即送现金、充值返现券等看似诱人的福利...
警惕!云启购APP违规经营,披...   "5元起投、充值返现、稳赚不赔"——这些诱人的广告语背后,可能是一个精心设计的金融陷阱。  近期...
量子淘金APP贵金属投资不合规...   "4元起投""充值返券""月入过万"——这些极具诱惑力的广告语在短视频平台、社交软件上铺天盖地。...
鑫汇智投APP贵金属商品订购骗...   鑫汇智投APP非法期货投资骗局,受害者在投资过程中根本不懂交易,也不去研究和验证这个平台是否正规...
矅光智策APP平台违规经营非法...   矅光智策APP对外宣称是“商城订购”“现货交易”,但查询其经营范围,并未显示具备开展现货期货交易...
黄金珠宝怎么选?内侧刻字都代表... 潮新闻客户端 记者 马焱 通讯员 任倩影 9月8日下午,“云游杭州 乐购金秋”——“浙城·浙放心”活...
原创 冰... 最近全球国债市场正在上演魔幻一幕: 美债、日债、欧债遭遇猛烈抛售,收益率冲到几十年新高,美国财政部都...
日韩股市开盘表现分化 上证报中国证券网讯 9月9日,日韩股市开盘表现分化,韩国综合指数开盘上涨0.26%,报6972.87...