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());

有问题欢迎留言

参考阮一峰日志

相关内容

热门资讯

企业IP打造指南:小公司低成本... 小公司做企业IP,不是为了装门面,而是让客户在没见到你之前,就能通过内容知道你是谁、你解决什么问题、...
官方:赵心童入选世界斯诺克名人... 北京时间5月8日消息,世界斯诺克巡回赛(WST)今日正式公布了2025/26赛季年终奖项及名人堂更新...
小灰熊AI学员王锋:希望能跟上... 35了,老程序员了。 从进入互联网行业到现在,其实已经做了很多年移动端开发。最早那几年,安卓行业发展...
原创 2... 2026年全国两会把稳定房地产市场列为重点工作,政府工作报告明确提出因城施策控增量、去库存、优供给。...
一年翻倍,六年未归——徽商银行... 文:向善财经 今年的港股市场,与A股市场出现了明显的分化。 A股这边,科技板块在AI浪潮中热闹非凡;...
古井贡酒2025:在行业深度调... 以“稳”为底、以“新”为翼。 文/每日财报 杜康 在行业库存高企、价格倒挂的背景下,当多数酒企在为...
好上好8408万收购鼎瑞芯加码... 5月7日晚,好上好(001298.SZ)抛出一份收购公告,拟以8408万元现金收购深圳市鼎瑞芯科技有...
全面大撤离!李嘉诚英国“套现”... 突发,李嘉诚又卖了。 这次,套现了455亿。 金额不少,但更值得关注的是透露着不同寻常的信号。 因为...
油气价格上涨加剧法国一季度贸易... 据新华社,法国海关7日发布的数据显示,受中东局势推高国际油气价格影响,法国今年第一季度贸易逆差扩大至...
昆仑芯启动科创板IPO上市辅导... 5月8日,据证监会官网显示,昆仑芯(北京)科技股份有限公司于2026年5月7日正式启动科创板上市辅导...
贵州茅台酒股份有限公司关于回购... 来源:上海证券报 证券代码:600519 证券简称:贵州茅台 公告编号:临2026-016 贵州茅...
百度昆仑芯启动科创板上市辅导,... 5月8日,证监会官网显示,昆仑芯(北京)科技股份有限公司 (下称“昆仑芯”)于2026年5月7日正式...
滕州信华的承压时刻:罚单、失信... 2026年4月末,滕州信华美元债单日跌近2%,关联方被列“老赖”。半年前,这家AA+城投曾因非市场化...
002808,或被终止上市! 【导读】因触及财务类退市指标,*ST恒久或被终止上市 中国基金报记者 李智 又一A股或被终止上市。 ...
院士团队掌舵,溧阳这家企业已完... 近日,溧阳天目先导电池材料科技有限公司(下称“天目先导”)官宣完成B轮融资,投资方包括知卓创新资本、...
工商银行全新推出“工盈研选”品... 深圳商报·读创客户端记者 詹钰叶 近日,工商银行重磅推出「工盈研选」基金销售服务品牌,以客户盈利为核...
和讯信息胡云龙:逼空走势,周五... 今天市场出现逼空走势,场内投资者因持有筹码而尤为受益。五一前布局的投资者当前收获颇丰。然而,随着上证...
今晚,油价上调! 4月21日国内成品油价格下调以来,国际市场原油价格剧烈震荡,前期大幅上涨后近日有所回落,本次调价的前...
南方东英旗下两倍做多海力士,成... 【导读】南方东英旗下两倍做多海力士,成为全球最大的个股杠杆及反向产品 中国基金报记者 伊万 人工智能...
原创 金... 黄金,这东西从古至今就没离开过中国人的生活。从老辈人压箱底的小黄鱼,到如今年轻人结婚绕不开的“三金”...