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

最近业务需求中需要使用到数据库,在翻看vscode源码过程中,发现vscode一共用了两种数据库存储数据,SQLiteIndexedDB,本文主要对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());

有问题欢迎留言

参考阮一峰日志

相关内容

热门资讯

山西太钢不锈钢股份有限公司 2... 来源:证券日报 证券代码:000825 证券简称:太钢不锈 公告编号:2026-001 本公司及董...
把自己的银行贷款出借给别人,有... 新京报讯(记者张静姝 通讯员邸越洋)因贷款出借后未被归还,原告牛女士将被告杨甲、杨乙诉至法院,要求二...
金价暴跌,刚买的金饰能退吗?有... 黄金价格大跌,多品牌设置退货手续费。 在过去两三天,现货黄金价格经历了“过山车”般的行情,受金价下跌...
预计赚超2500万!“豆腐大王... 图片来源:图虫创意 在经历了一年亏损后,“豆腐大王”祖名股份(003030.SZ)成功实现扭亏为盈。...
特朗普提名“自己人”沃什执掌美... 据新华社报道,当地时间1月30日,美国总统特朗普通过社交媒体宣布,提名美国联邦储备委员会前理事凯文·...
爱芯元智将上市:连年大额亏损,... 撰稿|多客 来源|贝多商业&贝多财经 1月30日,爱芯元智半导体股份有限公司(下称“爱芯元智”,HK...
一夜之间,10只A股拉响警报:... 【导读】深康佳A等10家公司昨夜拉响退市警报 中国基金报记者 夏天 1月30日晚间,A股市场迎来一波...
谁在操控淳厚基金?左季庆为谁趟... 2026年1月6日,证监会一纸批复核准上海长宁国有资产经营投资有限公司(下称“长宁国资”)成为淳厚基...
工商银行党委副书记、行长刘珺会... 人民财讯1月31日电,1月29日,工商银行党委副书记、行长刘珺会见来访的上海电气集团党委书记、董事长...
布米普特拉北京投资基金管理有限... 从亚马逊到联合包裹,一场席卷美国企业的“瘦身”行动正在持续。多家企业近期承认,近年来的扩张步伐迈得过...
酒价内参1月31日价格发布 飞... 来源:酒业内参 新浪财经“酒价内参”过去24小时收集的数据显示,中国白酒市场十大单品的终端零售均价在...
筹码集中的绩优滞涨热门赛道股出... 2025年以来,在受多重因素的刺激下,科技、航天、基础化工等热门赛道中走出轮番上涨的结构性行情,其中...
2026年A股上市公司退市潮开... 来源:界面新闻 界面新闻记者 赵阳戈 随着2026年序幕拉开,A股市场新一轮“出清”即将上演。...
雷军官宣新直播:走进小米汽车工... 【太平洋科技快讯】1 月 31 日消息,小米创办人、董事长兼 CEO 雷军在社交媒体发文宣布,将于 ...
现货黄金直线跳水,跌破5200... 新闻荐读 1月29日晚,现货黄金白银快速走低,回吐盘中全部涨幅。23:15左右,现货黄金跌破5300...
加拿大拟与多国联合设立国防银行 新华社北京1月31日电 加拿大财政部长商鹏飞1月30日说,加拿大将在未来数月与国际伙伴密切合作,推进...
马斯克大消息!SpaceX申请... 据券商中国,美东时间1月30日,路透社报道,据两位知情人士透露,马斯克旗下SpaceX公司2025年...
澳网:雷巴金娜2-1萨巴伦卡女... 北京时间1月31日,2026赛季网球大满贯澳大利亚公开赛继续进行,在女单决赛中,5号种子雷巴金娜6-...
春节前白酒促销热:“扫码抽黄金... 春节临近,白酒市场再现价格异动。 近日,飞天茅台批价拉升,有酒商直言“年前要冲2000元关口”,引发...