Canvas架构手记 02 存储配额与驱逐标准 | IndexedDB vs localStorage | 事务
localStorage 的优 / 缺点✅ 优点:API 简单、上手快;同步操作适合简单逻辑。❌ 缺点:容量小、阻塞主线程、无查询能力。IndexedDB 的优 / 缺点✅ 优点:容量大、异步非阻塞、支持复杂查询 / 事务。❌ 缺点:API 复杂、学习成本高;不适合小数据场景。
1 IndexedDB 浏览器存储限制和清理标准

1. 全局限制(浏览器总共能存多少)
- 浏览器能存的最大空间,是你电脑硬盘可用空间的 50%(比如 500G 硬盘可用,浏览器最多存 250G)。
- 这是 “软限制”:存满了会自动删一些网站的数据(整个网站全删,避免数据乱),腾出空间。
2. 组限制(单个网站 / 域名组能存多少)
- 每个 “主域名”(比如mozilla.org、firefox.com)算一个 “组”,组内的子域名(比如www.mozilla.org)共享空间。
- 每个组最多能存全局限制的 20%(比如全局是 250G,每个组最多存 50G),但最少不低于 10MB、最多不超过 2GB。
- 这是 “硬限制”:组存满了不会自动删数据,直接存不进去了。
举个例子
如果你的硬盘可用 500G,浏览器全局最多存 250G:
- mozilla.org、www.mozilla.org这些子域名,加起来最多能存 50G(250G 的 20%);
- firefox.com单独最多也能存 50G;
- 要是浏览器存到 250G 了,会自动删一些网站的全部数据,直到空间够。
2 浏览器存储配额与驱逐标准(基于 MDN 文档)
一、浏览器如何区分不同网站的数据?
浏览器会将不同网站的数据存储在名为 “存储桶”(buckets)的独立位置,以此降低用户跨网站被追踪的风险,核心遵循“按源(Origin)管理” 原则:
- “源” 的定义:由协议(如 HTTPS)、主机名(如example.com)和端口号共同决定。例如,
https://example.com与https://example.com/app/index.html属于同一源(协议、主机名、默认端口均相同);而http://example.com(协议不同)、https://blog.example.com(主机名不同)、https://example.com:8080(端口不同)均属于不同源。 - 配额与驱逐规则的适用范围:本文所述的存储限制和数据驱逐逻辑,对整个 “源” 生效。即便一个源下运行多个子站点(如
https://example.com/site1/和https://example.com/site2/),也共享同一套规则。 - 特殊情况:分区存储:部分场景下(如某源通过
<iframe>嵌入到多个第三方源中),浏览器可能会将该源的数据进一步分区存储,但为简化说明,本文默认数据 “按源存储”。
二、哪些技术可在浏览器中存储数据?
Web 开发者可通过以下 5 类核心技术在浏览器本地存储数据,各类技术的功能定位不同,具体如下表所示:
| 技术(Technology) | 描述(Description) |
|---|---|
| Cookies | 小型文本数据,由服务器与浏览器双向传递,用于记录跨页面导航的状态信息(如登录会话标识)。 |
| Web Storage | 仅存储字符串类型的键值对,包含localStorage(持久存储)和sessionStorage(会话级存储)两种形式,适合存储简单的客户端状态。 |
| IndexedDB | 用于在浏览器中存储大型数据结构(如复杂 JSON 对象),支持索引功能,可实现高性能的数据查询,适合存储离线应用数据、大量用户生成内容等。 |
| Cache API | 持久化存储 HTTP 请求与响应的配对数据,核心作用是缓存静态资源(如 JS、CSS、图片),加速网页二次加载。 |
| 源私有文件系统(OPFS) | 为当前源提供专属的文件系统,支持目录和文件的读写操作,适合需要按文件形式管理数据的场景(如编辑器的本地文件存储)。 |
此外,浏览器还会为源存储其他类型数据(如 WebAssembly 代码缓存),但上述 5 类是开发者可主动调用的核心存储技术。
三、浏览器存储的数据会持久存在吗?
一个源的数据在浏览器中分为“尽力而为存储(Best-effort)”和“持久存储(Persistent)”两种形式,持久性差异如下:
1. 尽力而为存储(默认形式)
- 持久性规则:数据仅在 “源未超出配额、设备有足够存储空间、用户未手动删除” 的前提下保留。若浏览器需释放空间,可能会主动删除这类数据。
- 用户感知:使用 IndexedDB、Cache API 等技术时,数据会自动以该形式存储,无需用户授权;浏览器删除数据时也不会打扰用户。
- 实际留存概率:Chrome 团队研究表明,此类数据被浏览器删除的概率极低 —— 若用户定期访问某网站,即便数据是 “尽力而为” 形式,也几乎不会被驱逐。
2. 持久存储(需主动开启)
- 持久性规则:仅当用户通过浏览器设置手动删除时,数据才会被清除,浏览器不会因空间不足主动驱逐。
- 开启方式:开发者需调用
navigator.storage.persist()方法申请,不同浏览器的授权逻辑不同:- Firefox:会弹出 UI 弹窗,征求用户同意;
- Safari、Chrome/Edge 等 Chromium 系浏览器:根据用户与网站的交互历史(如访问频率、是否添加书签)自动同意或拒绝,不显示弹窗。
特殊场景:隐私浏览模式
在隐私浏览模式(如 Chrome 的 “无痕模式”、Edge 的 “InPrivate 模式”)下,浏览器可能会施加特殊配额,且所有存储数据会在模式关闭后被彻底删除。
四、浏览器最多能存储多少数据?
不同存储技术的配额差异极大,核心分为 “Cookies”“Web Storage”“其他技术(IndexedDB/OPFS 等)” 三类,具体限制如下:
1. Cookies
- 不推荐用于数据存储:Cookies 会随每一次 HTTP 请求自动发送到服务器,若存储大量数据会增加请求体积、拖慢加载速度,仅适合传递少量状态信息(如会话 ID)。
- 配额说明:因不推荐作为存储工具,MDN 未详细列出各浏览器的 Cookies 限制(通常单个 Cookie 约 4KB,同一源下最多存储 20-50 个)。
2. Web Storage(localStorage + sessionStorage)
- 统一配额:所有浏览器均限制 “同一源下,
localStorage和sessionStorage各最多存储 5MB”,合计 10MB。 - 超出处理:若存储时超出配额,浏览器会抛出
QuotaExceededError异常,需开发者通过try...catch代码块捕获处理。
3. 其他技术(IndexedDB、Cache API、OPFS 等)
这类技术的配额由浏览器独立管理,不同浏览器的计算逻辑差异较大,具体如下:
| 浏览器(Browser) | 配额规则(按源计算) | 示例(以 500GiB 硬盘为例) |
|---|---|---|
| Firefox | 尽力而为模式:取 “用户配置文件所在磁盘总容量的 10%”“10GiB(同一 eTLD+1 域名组的上限)” 中的较小值;持久模式:最多为磁盘总容量的 50%(上限 8TiB),不受 eTLD+1 组限制。 | 尽力而为模式:最多存 10GiB(10GiB<500GiB×10%=50GiB);持久模式:最多存 250GiB(500GiB×50%)。 |
| Chrome/Edge(Chromium 系) | 尽力而为 / 持久模式统一配额:最多为磁盘总容量的 60%。 | 无论哪种模式,最多存 300GiB(500GiB×60%)。 |
|
Safari (WebKit 内核) |
分 “浏览器应用” 和 “嵌入式 WebView 应用” 两类: 1. 浏览器应用(如 Safari、可设为默认浏览器的第三方 WebKit 浏览器):同一源最多存磁盘总容量的 60%; 2. 嵌入式 WebView 应用(如 App 中的网页模块):同一源最多存 15%,但 “添加到桌面 / 程序坞的网站” 按浏览器应用配额(60%)计算; 3. 跨域框架:配额为父框架的 1/10; 4. 全局限制:所有源的总存储量,浏览器应用不超过磁盘的 80%,WebView 应用不超过 20%。 |
浏览器应用中:最多存 300GiB(500GiB×60%);WebView 应用中:最多存 75GiB(500GiB×15%)。 |
重要说明:配额计算的 “安全考量”
所有浏览器均基于 “磁盘总容量” 而非 “可用空间” 计算配额,目的是避免开发者通过 “检测可用空间变化” 追踪用户设备(即防止 “指纹识别”)。这意味着:即便源未超出配额,若设备实际可用空间不足,仍可能无法存储数据。
五、如何查看可用存储空间?
开发者可调用navigator.storage.estimate()方法,获取当前源的 “预估已用空间” 和 “预估可用空间”。需注意:
- 该方法返回的是估算值,非精确值;
- 若数据包含来自其他源的资源(如跨域图片缓存),浏览器会主动 “填充” 数据大小,避免泄露真实存储情况,进一步防范指纹识别。
六、当一个源的存储超出配额时,会发生什么?
若使用 IndexedDB、Cache API、OPFS 等技术存储数据时超出源的配额,浏览器会直接抛出QuotaExceededError异常。
开发者应对建议
- 用
try...catch包裹存储操作代码,捕获异常并提示用户(如 “本地空间不足,请清理浏览器缓存”); - 存储新数据前,主动删除无用旧数据(如过期的缓存资源、用户已删除的内容),释放空间。
七、数据何时会被驱逐?
浏览器仅会驱逐 “尽力而为存储” 的数据,不会触碰 “持久存储” 的数据,触发驱逐的场景分为三类:
1. 设备存储压力(最常见)
当设备可用空间过低(即 “存储压力”),且浏览器可用空间不足以容纳所有源的 “尽力而为数据” 时,浏览器会按LRU(最近最少使用)策略驱逐数据:先删除 “最近最少访问” 的源的全部数据,若空间仍不足,继续删除 “第二少访问” 的源,直至压力缓解。
2. 浏览器全局存储超限
部分浏览器会设定 “自身可使用的最大磁盘空间”(如 Chrome 当前限制为磁盘总容量的 80%)。若所有源的存储总量超出该限制,即便单个源未超配额,浏览器仍会按 LRU 策略驱逐 “尽力而为数据”。
3. Safari 主动驱逐(独有场景)
当 Safari 开启 “跨站跟踪防护” 时,若一个源在 “最近 7 天的浏览器使用中,无用户交互(如点击、触摸)”,其通过脚本创建的数据(如 IndexedDB 记录)会被主动删除;但服务器设置的 Cookies 不受此影响。
八、数据被驱逐时的规则是什么?
浏览器驱逐数据时,会遵循 “全量删除” 原则:
- 对某一源,会删除其所有 “尽力而为存储” 的数据(包括 IndexedDB、Cache API、OPFS 等所有技术存储的内容),不会只删部分数据 —— 避免因数据不完整导致应用逻辑异常(如仅删除缓存却保留索引,造成查询错误)。
3 IndexedDB vs localStorage
直接说结论:localStorage 是 “简单小数据” 的轻量存储,IndexedDB 是 “复杂 / 大量数据” 的高性能存储,两者定位完全不同。
一、核心定位
- localStorage是浏览器提供的键值对(Key-Value)轻量存储,仅支持字符串类型,适合存简单的客户端状态(如用户偏好、临时标识)。
- IndexedDB是浏览器内置的NoSQL 数据库,支持复杂数据结构(对象、数组)、索引查询,适合存大量 / 结构化数据(如离线应用数据、用户本地内容)。
二、关键差异对比
| 维度 | localStorage | IndexedDB |
|---|---|---|
| 数据类型 |
仅支持字符串 (需手动 JSON 序列化 / 反序列化) |
支持任意 JS 类型(对象、数组、Blob 等) |
| 存储容量 | 同一源下约 5MB(所有浏览器统一限制) | 通常为浏览器全局配额的 20%(如硬盘 500G 则约 50G) |
| 操作方式 | 同步操作(会阻塞主线程) | 异步操作(不阻塞页面渲染) |
| 查询能力 | 仅支持 “按键读取”,无查询 / 筛选功能 | 支持索引、游标,可按条件筛选 / 排序 |
| 事务支持 | 无事务,操作失败可能导致数据不一致 | 支持事务(原子性:要么全成功,要么全回滚) |
| 适用场景 | 简单小数据(如用户主题、登录状态) | 大量 / 复杂数据(如离线文章、本地表格) |
三、使用示例
1. localStorage(简单场景)
// 存数据(需手动转字符串)
localStorage.setItem('userTheme', JSON.stringify({mode: 'dark', size: 16}));
// 取数据(需手动转对象)
const theme = JSON.parse(localStorage.getItem('userTheme'));
// 删除数据
localStorage.removeItem('userTheme');
2. IndexedDB(复杂场景)
// 打开数据库
const request = indexedDB.open('MyDB', 1);
request.onupgradeneeded = (e) => {
const db = e.target.result;
// 创建存储对象(类似表)+ 索引
const store = db.createObjectStore('articles', {keyPath: 'id'});
store.createIndex('byCategory', 'category'); // 按分类建立索引
};
request.onsuccess = (e) => {
const db = e.target.result;
// 事务:添加数据
const tx = db.transaction('articles', 'readwrite');
const store = tx.objectStore('articles');
store.add({id: 1, title: 'Hello', category: 'note'});
// 事务:按索引查询
const index = store.index('byCategory');
index.getAll('note').onsuccess = (e) => {
console.log('分类为note的文章:', e.target.result);
};
};
四、优缺点总结
-
localStorage 的优 / 缺点
-
✅ 优点:API 简单、上手快;同步操作适合简单逻辑。
-
❌ 缺点:容量小、阻塞主线程、无查询能力。
-
IndexedDB 的优 / 缺点
-
✅ 优点:容量大、异步非阻塞、支持复杂查询 / 事务。
-
❌ 缺点:API 复杂、学习成本高;不适合小数据场景。
4 事务(Transaction)

事务(Transaction) 是数据库中 “原子性的一组数据操作集合”,是与数据库交互的核心方式 —— 简单说,所有数据库的读 / 写操作都必须在事务中执行,核心目的是保证数据一致性和操作可靠性。
一、事务的核心特性
- 原子性(核心):事务中的所有操作要么 “全部成功执行”,要么 “全部失败回滚”,不会出现 “部分成功” 的中间状态。比如向数据库添加 3 条数据,若第 2 条失败,前 1 条也会被撤销,数据库回到初始状态。
- 范围固定:创建事务时会定义 “作用域”,即该事务能操作的数据库对象(如 IndexedDB 的 “对象存储 / Object Store”),且整个事务生命周期内作用域不能修改。
- 短生命周期:事务设计为 “快速完成”,若执行时间过长,浏览器会主动终止它 —— 避免长期锁定存储资源,影响其他操作。
- 可主动中止:开发者可手动调用接口中止事务,此时事务中已做的所有数据修改都会被回滚,数据库恢复到事务开始前的状态。
二、事务的关键规则(结合 IndexedDB 场景)
- 并发限制:一个数据库连接可同时存在多个活跃事务,但有约束:
- 写事务(修改数据)的作用域不能重叠(比如不能同时有两个写事务操作同一个对象存储);
- 读事务(仅查询数据)无重叠限制,可同时存在多个。
- 操作依赖:事务是所有数据操作的 “容器”—— 不管是读取数据、修改数据,还是创建 / 删除数据库对象(如 IndexedDB 的对象存储、索引),都必须在事务中进行,不能直接操作数据库。
三、IndexedDB 中的事务模式(3 种)
事务的模式决定了其能执行的操作权限,IndexedDB 中核心分为 3 类:
- readonly(只读模式):仅能查询数据,不能修改(如读取对象存储中的记录),多个只读事务可同时操作同一作用域。
- readwrite(读写模式):可查询、添加、修改、删除数据,同一作用域同一时间只能有一个读写事务。
- versionchange(版本变更模式):唯一能创建 / 删除对象存储、索引的模式,用于数据库结构升级(如新增表、修改索引),执行时会锁定整个数据库,阻止其他事务并行。
四、事务的核心作用(为什么重要?)
对 IndexedDB 而言,事务是保证数据可靠性的关键:
- 避免数据不一致:比如转账场景(A 账户减钱、B 账户加钱),若中途出错,事务回滚可防止 “只扣钱没加钱” 的 bug;
- 控制资源竞争:通过锁定机制,防止多个操作同时修改同一数据(如两个写事务同时更新同一条记录);
- 简化错误处理:无需单独处理每个操作的失败,只需判断事务整体成功 / 失败,统一回滚或提交。
简单总结:事务就像数据库操作的 “打包工具”,把一组相关操作绑在一起,要么全成、要么全退,同时通过规则控制并发和资源占用,是 IndexedDB 中最核心的概念之一。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)