DuckDB 存图片和视频?官方说不推荐,但开发者们已经疯了
把一部 2GB 的电影存成 200GB 的 SQL 表、用 SQL 查"狗在画面的哪一帧"、一条 SELECT 搞定图文混合搜索——DuckDB 在你的文件系统上做的事,比你想的野多了。
DuckDB 被叫做"分析型 SQLite"——嵌入式的、零配置的、跑在你笔记本上的分析引擎。正常情况下它处理 CSV、Parquet、JSON 这些结构化数据。
但你有没有想过:图片和视频能不能也丢进去?
答案有点拧巴:官方文档写"不推荐",但生态已经给你铺好了三条路。 有一条甚至成了 DuckDB 的核心扩展。
● ● ●
官方的态度:能存,但别存大文件
DuckDB 有个 BLOB(Binary Large Object)类型,最大支持 4GB 的二进制数据。用 read_blob() 函数从磁盘读文件,用 md5() / sha256() 算哈希,用 octet_length() 看文件大小。
但官方文档的原话是:
While blobs can hold up to 4 GB, storing very large objects directly in the database is generally discouraged.
翻译:能存,但别这么干。 大文件塞 BLOB 会让库文件膨胀、查询变慢。官方建议文件留磁盘,库只存路径。
那 DuckDB 就不能存图片视频了?不是——它只是说别只靠 BLOB,还有更好的方式。
● ● ●
三条路线,三套解法
路线 1:元数据入库,文件留盘(90% 场景的最佳选择)
最务实的做法:只把图片/视频的元数据和路径存进 DuckDB,原始文件留在文件系统或对象存储。
CREATE TABLE images (
id BIGINT PRIMARY KEY,
file_path VARCHAR,
width INTEGER, height INTEGER,
file_size BIGINT, format VARCHAR,
md5_hash VARCHAR,
-- 从 EXIF 提取的拍摄参数
camera_model VARCHAR, iso INTEGER,
aperture FLOAT, shutter_speed FLOAT,
gps_lat FLOAT, gps_lon FLOAT,
taken_at TIMESTAMP
);
GitHub 上有个项目 JohnApps/exif 就是这么干的:解析图片 EXIF 元数据存 DuckDB,再用 ResNet50 做图像内容分类,一条 SQL 就能查"上个月我用 Sony 相机在 f/1.4 下拍了多少张日出"。
这适合你想管理图片和视频,而不是分析像素。个人图库、企业素材库、安全监控录像索引,都是这个路线的菜。
路线 2:Lance 格式——"一行存一切"(最先进的方案)
如果你的需求超越了元数据查询——比如"找到与这张图片相似的所有产品"或"搜索包含特定场景的视频"——那就要上 Lance 了。
Lance 是一个专门为多模态 AI 设计的开放湖仓格式,从 2025 年起被 DuckDB收编为核心扩展。不再是第三方插件,是官方生态的一部分。
装完就能用:
INSTALL lance; LOAD lance;
Lance 的设计核心:一行存一切。
┌──────────────────────────────────────────────┐ │ 一个 Lance 表行 │ ├─────────────┬────────────────────────────────┤ │ 结构化字段 │ item_id, title, brand, color │ │ 全文索引 │ description │ │ 图片原始字节 │ image_bytes (JPEG BLOB) │ │ 跨模态向量 │ multimodal_vec (CLIP 512d) │ │ 文本语义向量 │ text_vec (E5 768d) │ └─────────────┴────────────────────────────────┘
关键特性:
- 惰性加载:不
SELECT image_bytes时,那一列完全不读取,零开销 - 向量搜索是 SQL 函数:
lance_vector_search()返回带距离排序的结果 - 图文混合搜索:一条 SQL 同时做向量检索 + 全文搜索 + 结构化过滤
- 100 倍随机访问:比 Parquet 快 100 倍,不牺牲扫描性能
- Git 式版本控制:自动版本管理、Time Travel、ACID 事务
实战例子——"买了带花卉图案米色鞋子的用户还有多少?"
这在一个传统架构里需要:向量数据库搜相似图片 → 拿到 ID 列表 → SQL 仓库查用户 → Join → 统计。三套系统。
在 Lance + DuckDB 里,一条 SQL:
WITH similar_products AS (
SELECT item_id, _distance
FROM lance_vector_search(
'catalog/products.lance',
'multimodal_vec',
(SELECT multimodal_vec FROM query_image LIMIT 1),
k = 100
)
WHERE _distance < 0.3
)
SELECT s.customer_id, COUNT(*) AS purchase_count
FROM sales s
JOIN similar_products sp ON s.item_id = sp.item_id
GROUP BY s.customer_id
ORDER BY purchase_count DESC;
向量搜索、元数据过滤、SQL Join 和聚合——全在一个 SQL 会话里完成,不用拷数据,不用写胶水代码。
LanceDB 的工程师在博客里写:"The Lance core extension for DuckDB collapses all three — image bytes live as a BLOB column alongside embeddings and structured fields in a single table."
三套系统塌成一列。这就是 Lance 给 DuckDB 带来的变化。
路线 3:像素级存储——"不是该不该,是能不能"(实验极限)
DuckDB 官方博客有一篇叫 Relational Charades: Turning Movies into Tables 的文章,开头引了《侏罗纪公园》Ian Malcolm 的名言:
"Your scientists were so preoccupied with whether they could, they didn't stop to think if they should."
然后他们就把 1963 年的电影 Charade(奥黛丽·赫本主演),一个像素一行地存进了 DuckDB:
CREATE TABLE movie (
i BIGINT, -- 帧号
y USMALLINT, -- 第几行
x USMALLINT, -- 第几列
r UTINYINT, -- R 通道
g UTINYINT, -- G 通道
b UTINYINT -- B 通道
);
一部电影:169,563 帧 × 720 像素 × 392 像素 = 479 亿行。
DuckDB 文件大小:200 GB。 原始 MP4 视频:仅 2 GB。
这不实用——用 200GB 存 2GB 的电影,显然是一个"能 vs 该"的玩笑。但这个实验证明了 DuckDB 引擎的强度:
SUMMARIZE movie;——全表扫描 479 亿行,MacBook 上 20 分钟跑完- 统计电影中出现了多少种不同颜色——2 分钟
- 每 1000 帧计算平均画面——正常工作
2 分钟算完 479 亿行的聚合。对一个跑在笔记本上的嵌入式数据库,这不是疯,是能力。
● ● ●
视频:Lance 的 BLOB v2 和 HuggingFace 集成
Lance 的视频支持更进一步。HuggingFace Hub 在 2026 年 2 月宣布了原生 Lance 格式支持,OpenVid-1M 数据集(高质量带字幕视频)以 video_blob 列存储:
-- 浏览元数据,不加载视频 SELECT id, caption FROM 'hf://datasets/OpenVid-1M/data.lance' LIMIT 10; -- 按需取出特定视频的原始字节 SELECT video_blob FROM 'hf://datasets/OpenVid-1M/data.lance' WHERE id = 'video_001';
Lance 的 Blob v2 API 和 torchcodec 兼容,可以直接将视频 BLOB 解码为 PyTorch 张量用于训练。从存储到训练,数据格式不变。
● ● ●
MotherDuck 那条路
你提到的 MotherDuck 文章,应该是关于 Unstructured.io + MotherDuck 集成那篇。它的路线不同:
不是直接分析像素,而是把图片/PDF/文档 OCR 成文本 → 存入 MotherDuck → 用内置的 embedding() 函数生成向量 → 做 RAG 语义搜索。
-- MotherDuck 原生 AI 函数
SELECT embedding(text_column, 'text-embedding-3-small') FROM documents;
SELECT prompt('classify the sentiment', review_text) FROM reviews;
这是 RAG 路线,适合文档问答,但不适合直接的像素/帧分析。
● ● ●
怎么选
决策流程图
| 场景 | 推荐方案 |
|---|---|
| 管理图库元数据,按设备/时间/地点检索 | DuckDB + EXIF 解析 |
| 电商商品图搜图、视频内容查找 | Lance + DuckDB Lance 扩展 |
| 文档 OCR → 语义搜索(RAG) | MotherDuck + Unstructured.io |
| "我就是想试试 SQL 查电影像素" | 像素级存储(玩玩可以) |
一句话总结:DuckDB 可以存图片和分析图片,但存的方式决定了它的价值。
把大文件直接塞 BLOB 是最傻的做法——DuckDB 自己都劝你别干。元数据入库 + 文件留盘的混合模式覆盖 90% 的需求。如果真需要跨模态检索——Lance 扩展已经被官方收编,是 DuckDB 生态里的正解。
那个 479 亿行的电影实验不会跑到生产环境里,但它说明了一件事:DuckDB 这个跑在笔记本上的嵌入式数据库,能把一部电影的每个像素当行处理。这可不是随便一个数据库做得出来的。
调研来源:DuckDB 官方文档、LanceDB 官方博客、MotherDuck 博客、GitHub 社区项目。