几百万行 CSV 不用起集群:用 DuckDB 在本地查明白

举报
茉莉风铃 发表于 2026/09/21 10:32:49 2026/09/21
【摘要】 pandas 读个 500 万行的 CSV 内存就吃满,为偶尔一次分析搭套大数据环境又不值当。这篇讲 DuckDB:一个 pip 装上就能用的嵌入式分析型数据库,直接在 CSV/Parquet 上跑 SQL,不用先导入,还是列式加向量化执行所以快。含和 pandas 配合、为什么快、以及它的边界在哪。

有次运营丢给我一个 800 万行的订单 CSV,要按城市统计个客单价。我兴冲冲 pd.read_csv,内存直接干到十几个 G,风扇狂转,最后还是 OOM 了。为了这一次的活去搭 Spark?不至于。装个数据库再把 CSV 导进去?光导数据就得等半天。

后来我用了 DuckDB,一行 pip install,直接对着 CSV 文件写 SQL,十几秒出结果,内存占用还很低。这篇讲讲它是什么、怎么用、以及边界在哪。

image.png

一、先说清楚它是个什么

DuckDB 是一个"嵌入式"的分析型数据库。 拆开讲:

  • 嵌入式:它不是一个要你先启动、再连过去的数据库服务,而是一个库,一个文件。Python 里 import duckdb 就直接能用,没有服务、没有端口、没有配置文件。这一点跟 SQLite 很像。
  • 分析型:这里要说个词——OLAP(在线分析处理)。它适合的是"扫几百万行、按某列分组聚合"这种分析活儿,不适合"高并发地查单条记录、改单条记录"这种业务库活儿。SQLite 是嵌入式的事务型(OLTP)数据库,DuckDB 是嵌入式的分析型(OLAP)数据库,正好是两个方向。

它还有两个词常一起出现,顺手解释下:

  • 列式存储:传统数据库按"行"存(一条记录的所有字段挨在一起),DuckDB 按"列"存(所有记录的同一个字段挨在一起)。你只查 3 个字段时,它就只读那 3 列的数据,其余的碰都不碰——这是它快的第一个原因。
  • 向量化执行:CPU 处理数据不是一条一条来,而是一批一批(比如一次 2048 个值)批量算。这样能充分利用 CPU 缓存和 SIMD 指令,是它快的第二个原因。

二、装上就能用

pip install duckdb

然后直接对着文件写 SQL:

import duckdb

duckdb.sql("SELECT * FROM 'data.csv' LIMIT 5").show()

注意到了吗——没有建表、没有导入。FROM 'data.csv' 它自己会去读这个文件、自动识别表头和字段类型。

干点正经的活:

duckdb.sql("""
  SELECT city,
         count(*)   AS cnt,
         avg(amount) AS avg_amount
  FROM 'orders_*.csv'                    -- 支持通配符,多文件一起读
  WHERE created_at >= '2026-01-01'
  GROUP BY city
  ORDER BY cnt DESC
""").show()

orders_*.csv 这种写法特别省事,按月拆分的十几个文件一口气就查完了。

三、和 pandas 配合

已经有个 DataFrame 了,直接查:

import pandas as pd
import duckdb

df = pd.read_csv('small.csv')
result = duckdb.sql("SELECT city, sum(amount) AS total FROM df GROUP BY city").df()

df 这个变量名在 Python 里,DuckDB 能直接认出来当表用,不用注册。结尾的 .df() 是把结果转回 pandas DataFrame。

反过来,一个大 CSV 你想先用 SQL 过滤聚合、再交给 pandas 画图表,这么写:

df = duckdb.sql("""
  SELECT * FROM 'orders.csv' WHERE amount > 100
""").df()   # 结果小了,pandas 才扛得住

这是很实用的组合:让 DuckDB 干重活,让 pandas 干它擅长的数据整理和画图。

四、先把 CSV 转成 Parquet

Parquet 是一种列式的文件格式(可以理解为"为分析而生的 CSV")。同样的文件,Parquet 通常只有 CSV 的三分之一大小,查询还快好几倍,因为列式格式跟 DuckDB 的存储方式天然契合。

一次转换,之后反复用:

duckdb.sql("COPY (SELECT * FROM 'orders.csv') TO 'orders.parquet' (FORMAT PARQUET)")

之后就一直查 parquet 文件。如果你手上的 CSV 是常驻数据集,这一步的收益非常明显。

五、到底快在哪,边界在哪

跟几种常见选择对比一下:

场景 pandas DuckDB Spark
数据量 内存装得下才舒服 能处理远大于内存的数据 分布式,上 TB 级
上手成本 低 低,一个 pip 高,要搭集群
表达方式 Python 链式调用 SQL SQL / DataFrame
部署 无 无,一个文件 要集群

它的边界要说清楚:DuckDB 是单机的。它能把单机性能压榨到极致,但你不能指望它横向扩展到几十台机器。数据量真到了 TB 级且持续增长,那还是得上分布式引擎。但对绝大多数"几百万到几亿行、在一台机器上"的分析任务,它比 Spark 轻太多了。

几个容易卡的坑

  • 别把它当业务数据库用。它是分析场景设计的,并发写入支持很弱,事务也不是它的强项。别想着拿它替代 MySQL 存订单。
  • 类型推断偶尔猜不准。CSV 没有类型信息,它靠采样猜。日期列可能被猜成字符串。发现不对就在查询里显式转换,或者先 CREATE TABLE 指定类型再把数据导进去。
  • 结果别全量转 pandas。.df() 会把所有结果读进内存。聚合完再转是常识,可真到写的时候经常有人 SELECT * 然后 .df(),直接把内存打满,等于白用 DuckDB。
  • 性能对比要公平。第一次查询慢是正常的,后面有缓存。另外它读 CSV 本身要花解析时间,转 Parquet 之后差异会大很多。
  • 版本匹配。CLI 工具和 Python 包的版本要对齐,不然用 CLI 建的库文件 Python 可能读不了。
  • 注意磁盘空间。它虽然能处理大于内存的数据,但中间结果会往磁盘落临时文件,磁盘满了照样失败。

小结

DuckDB 填的是一个很尴尬的空档:数据量大到 pandas 吃力,又没大到需要 Spark。装一个 pip 包,直接对着 CSV/Parquet 写 SQL,不用起服务、不用导入数据,几秒钟就能开始分析。我现在的习惯是:数据先转 Parquet,日常查询用 SQL,最后用 .df() 把聚合后的小结果交给 pandas 画图。整套流程都在一台笔记本上完成,不用再为了一次临时分析去申请资源。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。