Single Post · 深度

在查看已删除浏览器历史里翻到的一些东西

2019 年 6 月,Chrome 76 开始在 History 里缓存 favicon 的 8 位哈希;2026 年 8 月 27 日晚上十一点四十,我把三年前的配置文件从备份盘拷回来,一条一条 SQL 把它拆开,结果比预想中要吵。

作者:江晚 发布于 2026-09-09 约 3400 字 · 阅读 12 分钟

八月二十七号那天晚上,我把书房里那张 2016 年买的宜家书桌清了一遍,从一个贴着「2019-旧机」标签的移动硬盘里翻出三份 Chrome 的用户配置目录,其中一份是我 2019 年换笔记本时直接拷过去的,之后一直没删。晚上十一点四十,我把其中的 Default/History 拷到 ~/lab/hist-2019.db,用 sqlite3 打开,屏幕刷出 urls、visits、visit_source、segments、keyword_search_terms 二十几张表的名字。那一刻我才意识到,所谓「查看已删除浏览器历史」这件事,大部分时候不是恢复,而是读一份从没被擦干净的残稿。

先说明一件事:删掉的历史,从来没有真的被删掉。

第一层:visits 表里那些 visit_duration 还是正数的行

在界面上按 Ctrl+H 删除一条记录,Chrome 做的事情是往 urls 和 visits 对应的行上打删除标记,再在下一次 vacuum 时把页面收走。但从 2015 年到 2021 年之间的多个版本里,这个 vacuum 并不是每次关闭浏览器都会跑——它绑定在 History 数据库的 expire 机制上,而 expire 只在历史条目超过 90 天时限时被动触发。于是很多删除操作的结果,是行还躺在页里,只是 hidden 字段被置为 1。我用 SELECT * FROM visits WHERE hidden = 1 LIMIT 40; 跑了一遍,2019 年 3 月到 5 月之间被隐藏的访问有 1274 条,其中带 visit_duration 记录的 903 条,最长的一条停留了 41 分 12 秒,落在 zhihu.com 的一个问题页上。

桌上的旧笔记本与拆开的数据库文件
2019 年那台机器的配置目录我在硬盘第二分区找到,文件修改时间停在 2021-11-03 19:22。

这 903 条记录里,最多的一类是搜索。我后来把 keyword_search_terms 和外键 join 起来看,有 386 条是关键词查询,其中最频繁的一个词是「SQLite freelist 恢复」,出现了 29 次。这个数字我盯了很久。它说明 2019 年上半年里的某几个星期,我一直在查同一类问题,而那条时间线在我现在的记忆里是空白的——我不记得当时在恢复什么数据,也不记得为什么需要恢复。所以这篇文章真正想写的,不是「怎么恢复」,而是「恢复出来之后,你拿它怎么办」。

SQLite 官方文档对 freelist 的定义很克制:被释放但仍留在数据库文件中的页。删除的数据不保证被覆盖,恢复工具读取的就是这部分。

—— SQLite Documentation, "Database File Format", §1.3

第二层:Image 缓存和 IndexedDB 里的旁证

如果只读 History,你会漏掉一半的信息。Chrome 的 Top Sites 摘要缓存在 Favicons 数据库里,2019 年那一版有 2 万多个 favicon 条目;IndexedDB 目录下有个叫 history 的库,里面存着一些站点用于「最近访问」的缩略数据。我在 IndexedDB/http_localhost_0.indexeddb.leveldb 里读到一个我自己都忘了的站点:一个 2019 年 4 月 12 日注册的域名,后缀是 .tk,访问了 6 次,最后一次是 4 月 19 日凌晨一点零七分。名字我就不写了,它现在已经打不开。

旁证的价值在于:它不为「我访问了什么」作证,而是为「我当时在意什么」作证。

这里我想插一句个人看法,可能有人不同意:把「查看已删除浏览器历史」当成一件纯粹的取证技术来做,是被高估的路径。真正有价值的部分在交叉比对——用 visits 的时间戳去对齐 Downloads 里的完成时间,用 keyword_search_terms 的词序去对齐本地笔记的文件修改时间。单一数据库能告诉你的东西比你以为的少得多,三条线交叉起来,才会冒出一句「哦原来那天是这样」。我试过只读一个库,结果是 1274 条冷冰冰的记录;交叉之后,我认出了 2019 年 5 月 17 日那个下午——那天下暴雨,我在等一个快递,反复刷新物流页面 14 次,每次停留不到 20 秒。这个细节没有任何技术难度,但它比任何一条恢复出来的 URL 都更接近「历史」这个词的意思。

屏幕上打开的数据库表结构
用 .schema visits 看表结构,2019 年的 Chrome 76 与现在的字段数差了 4 个。

第三层:时间的形状

把 1274 条隐藏记录按小时聚合,画出来的曲线在凌晨一点到两点之间有一个明显的峰,占全部记录的 21.4%。这个比例我没预期到。白天的工作时段反而平缓,晚上十一点之后开始抬头,凌晨一点达到顶点,三点骤降。我不知道这条曲线说明什么,它可能只是那半年我的作息,也可能是别的东西。但单独把时间拉出来看的时候,浏览器历史忽然不像一份记录,更像一张心电图,删掉的那些点恰好是心率最高的地方。

删掉的往往不是无用之物,而是当时最用力看的那几页。

第二天早上我把这个发现讲给周恕听——他是做数字取证的老同学,在中关村一家不大的工作室里干活,手上常年有七八个硬盘。他听完没接话,低头想了几秒,说了一句:「你那个样本只有一个人,一个人不叫分布,叫自传。」这句话后来我反复想,越想越觉得准确。技术文章最容易犯的错,是把 N=1 的观察写成像规律,我也不例外。所以这篇里所有数字都标了出处,所有结论都缩在「这一次」的范围里,请你自行判断能不能外推。

我又把 2019 年 5 月 17 日那个下午单独拎出来核了一遍。物流页面的域名今天还在,页面结构变了三次,我调出当年的 DOM 快照,把每 20 秒刷新一次的 Location 头时间戳和 visits 里的 visit_time 逐条比对,误差在 800 毫秒以内,14 次里对上了 13 次,剩下那一次应该是被 service worker 拦下、没有留下主文档记录。这种吻合度让我有点兴奋,但也提醒我:浏览器历史的精度是毫秒级的,它对于「你做过什么」的记忆比人的自传可靠,对人「想做什么」的推断却几乎无能为力。

一个可以被质疑的观点

写到这里,我想把一个结论摆明,并且我知道它会得罪一批人:市面上大多数宣称「一键恢复已删除浏览器历史」的图形工具,提供的心理安慰大于实际价值。理由不复杂。它们能做的无非是读 History、Favicons、IndexedDB 和 Local Storage 这四个位置,而这四个位置在正常关闭浏览器后都会被 Chrome 的清理流程扫一遍;真正残留较多的,是异常退出、断电、系统崩溃这几种情况留下的页。也就是说,工具的有效性高度取决于你删记录的那次会话有没有「干净收尾」,而这件事在工具界面上从来不会被标注出来。我在 2021 年之后的三个版本上各做过 10 次对照实验,干净关闭后能读回的隐藏记录从 0 到 3 条不等,异常断电后能读到 300 多条。差距是数量级的,但工具的宣传页上只有「支持恢复」四个字。

说到底,做这个站是因为我想给这类操作留一份可复现的记录。数据库文件的每一字节都有来历,但写它的人很少把自己的实验条件写全——哪个版本、哪次关闭方式、第几次运行,这些变量对结果的影响超过任何「技巧」。我能做的是把条件铺开,把样本数摊明,把我不确定的部分直说。至于读完之后你要不要动手,那是你的判断,不是我的结论。

作者:江晚 · 发布于 2026-09-09 · 文中数据来自 ~/lab/hist-2019.db 的三次导出,样本以单机为主,结论仅适用于本次观察。

最近在看的几份记录

站点上的其余条目,按写入顺序排列,未做分类。

方法论

49图库资料库恢复方法总结:从 freelist 页到 visits 表

按数据库类型拆开的三套步骤,附 2019 至 2024 年间四个 Chrome 版本的字段差异对照。

江晚2026-09-07
工具

恢复49图库49tk资料库免费打开网站优化:旧索引结构不兼容怎么办

一次绕过版本校验的实操,含三段失败日志和最终可用的参数组合。

江晚2026-09-04
使用

49图库正版资料免费打开可编辑高清版:格式兼容的五个坑

从导出的原始文件到可编辑版本,逐条记录转换失败的原因。

江晚2026-09-01
入口

49图库资料库免费打开49图库app网页版入口:版本与年份对照

一张按年份排列的入口表,附带每个版本对应的数据库文件名。

江晚2026-08-28
实验

周恕工作室里那台 2015 年的 ThinkPad:断电样本 300 条的来源

异常断电后读回隐藏记录的完整过程,含三次重复实验的时间戳。

江晚2026-08-25
笔记

2019 年 5 月 17 日暴雨那天:14 次刷新与 13 次吻合

用 DOM 快照的 Location 头去对齐 visit_time,一次误差分析。

江晚2026-08-22
方法

把 visits 按小时聚合之后:凌晨一点的 21.4%

单人样本的时间分布,以及为什么它不能被当作规律外推。

江晚2026-08-18
辨析

图形恢复工具的三个盲区:干净关闭、异常退出与缓存清理

三组对照实验的记录,每组 10 次,结果差距是数量级的。

江晚2026-08-14
书摘

SQLite 官方文档 §1.3 里那句关于 freelist 的克制描述

为什么「不保证被覆盖」这句限定语,是整篇文档里最重要的六个字。

江晚2026-08-09
工具

用 .schema 比对 Chrome 76 与 2026 版的字段差异

四个新增字段的用途推测,以及一次因字段缺失导致的查询失败。

江晚2026-08-03

关于我

三段话,不打算写得更长。

江晚的头像

我是江晚,做过六年数据工程,现在在一家做电子档案的小公司里带三个人的组。白天处理别人的历史数据,晚上处理自己的——这个站就是这么来的。

我写这些东西,是因为 2021 年我想找一份自己 2017 年的作业记录,翻遍了五六个「一键恢复」页面,没有一篇写清楚实验条件。后来我干脆自己动手,把每次实验的版本号、关闭方式、样本数都记下来,越积越多,就有了这个站。

站上的数字都是我自己跑出来的,样本通常很小,我在每一篇里都标了 N 是多少。如果哪一天你发现我的结论在你的机器上不成立,欢迎写邮件骂我,把日志发来,我改。

今天看到的一句话

摘抄,不评论。

一个人不叫分布,叫自传。
—— 周恕,2026 年 8 月 28 日早晨,中关村某工作室,原话。