我用 DuckDB 分析了整台手机,顺便搞懂了数据湖是不是必需的
起因:一个被误解的问题
我一直以为用 DuckDB 得先有个「数据湖」——那种企业级的、堆在云上的海量数据仓库。听起来就很重,得搭一套平台。
结果完全想错了。DuckDB 的定位恰恰相反,它是「数据分析界的 SQLite」——单个文件、零服务、import 就能用。它能查数据湖,但完全不需要数据湖。
验证这个最好的办法,是拿它分析点真实的东西。我手头正好有一批数据:这台旧安卓手机。它同时是我的家庭服务器,也是这个博客的宿主。于是我把它当成一个数据源,做了一次「全面体检」。
方法始终是同一个:用脚本把某类数据抽成 CSV,再用 DuckDB 直接查那个文件。 没有数据库服务器,没有数据湖,全程在手机本地,几分钟出结果。
四个维度,四张侧写。
一、四千张照片的拍摄习惯
先从照片开始。一个 Python 脚本用 PIL 把每张照片的 EXIF(拍摄时间、相机型号、分辨率)抽成 CSV,四千多张,一分钟跑完。然后:
SELECT model, COUNT(*) FROM 'photos.csv'
WHERE model != '' GROUP BY model ORDER BY 2 DESC;
结果拼出了一张我自己都没意识到的画像。我有好几台相机:
realme GT Neo5 SE 2309 张 手机,绝对主力
ZV-E10 313 张 索尼微单
NIKON Z 5 121 张 尼康全画幅
Canon EOS 5D Mark IV 20 张
松下 DC-S5M2 17 张
一路往前翻,还能看到设备的变迁史:2011 年的索尼摄像机、2016 年的卡西欧卡片机、2020 年的旧手机……一部个人的「影像器材编年史」,全藏在 EXIF 里。
而拍摄量在爆发式增长:
2022 180 张
2023 875 张
2024 1744 张 ← 高峰
2025 618 张
2022 年入手第一台微单之后,逐年翻倍,2024 年到顶。
但最有意思的一条,是把相机分成「手机」和「专业相机」两类,分别算平均拍摄时刻:
手机 平均 14.1 点
专业相机 平均 16.0 点
手机是白天随手记录,专业相机明显偏晚——那是「我决定今天要出门拍照」的有意识创作,更常发生在下午光线变好的时候。一条 GROUP BY 把两种完全不同的行为区分了出来,而我自己从没这样意识到过。
二、640GB 里到底存了什么
接着扫全部文件的元数据(路径、大小、类型、时间),25283 个文件,12 秒完成:
SELECT topdir, ROUND(SUM(size)/1073741824.0, 2) AS GB
FROM 'files.csv' GROUP BY topdir ORDER BY GB DESC;
一目了然:
DCIM 94 GB ← 照片+视频,占了 84%
Pictures 3 GB
微信缓存 2 GB
音乐 1.6 GB
这台手机 84% 的空间是影像。 它本质上是个相机,附带打电话功能——和照片分析的结论互相印证。
而最有用的一条发现是意外的:文件管理器的回收站里躺着 8GB 没清空的垃圾——几个删掉的游戏安装包,删是删了,回收站却没清。查一条 SQL 就看见了,清空立省 8GB。数据分析的第一个实际价值,往往就是「发现你以为删掉了、其实还占着地方的东西」。
三、视频:和照片同步的创作曲线
902 个视频,33.7GB。按年份看:
2023 321 个 / 10.6 GB
2024 363 个 / 15.7 GB ← 高峰
2025 200 个 / 7.1 GB
和照片的年份分布完全吻合——2024 是影像创作的巅峰年。数据在不同维度上互相印证:照片多的那年,视频也多。这种一致性本身就说明数据是可信的。
四、最「元」的一个:手机给自己做体检
这台手机跑着 Prometheus,一直在记录自己的温度、内存、电量。我把这些时序数据也导成 CSV,用 DuckDB 查——用数据分析这台机器自己的健康史。
内存最紧张的几个时刻:
00:26 可用 0.22 GB ← 我把内存压测推到极限那一刻
00:02 可用 0.89 GB ← 一次进程压力测试
14:46 可用 1.38 GB ← 跑了个大模型
每一个内存低谷,都精确对应我那天做过的一次实验。 温度峰值 80.3°C 也一样,几个时间点全是压测时段。这台机器把我对它做的每件事,都默默记了下来。
还有一个值得说的细节:监控数据的「在线率」显示 100%,但这不代表它没崩过——它崩的时候,连记录自己崩了的程序也一起崩了,那些时段是纯粹的数据空洞。空洞本身就是事故的痕迹。 有时候「没有数据」比「数据异常」更能说明问题。
一点技术上的体会
四个分析,DuckDB 咬了我两次,都是同一个原因:它会自动推断 CSV 的列类型。我按字符串去处理一个它已经认成数字的列,它就报类型错误。
一开始觉得烦,后来觉得这恰恰是它聪明的地方——你不用定义 schema(像 SQLite 那样随便存),但它又坚持类型正确(像 PostgreSQL 那样严谨)。这个「零配置 + 强类型」的平衡,是它比 SQLite 更适合做分析的关键。
尾声
回到最开始那个问题:DuckDB 需要数据湖吗?
四次体检给了最彻底的答案:不需要。它需要的只是一个文件。 照片、文件清单、视频列表、监控历史——每一个都只是磁盘上一个几 MB 的 CSV,DuckDB 对着它直接跑 SQL,就把一台用了几年的手机,拆解成了一张清晰的数字自画像。
工具轻到这个程度,「分析自己的数据」这件事,门槛就消失了。而一旦数据能被查询,它总会讲出一些你不知道的故事。
全部分析跑在一台旧安卓手机上,数据没有离开过它。25000 个文件、4000 张照片、900 个视频、900 个监控点——加起来处理时间不到十分钟。