alitrack

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 社区项目。