alitrack

DuckDB实战:ICU 每秒都在产生数据,4.3 亿条记录怎么查

「ICU 数据量大」是个抽象说法。到底多大?

我们把自己的 MIMIC-IV 副本(从 PhysioNet 申请下载)转成 Parquet 后,跑了一遍统计。数字比想象的夸张。

● ● ●

一张表,4.3 亿行

4.3 亿行监护数据:Parquet 压缩 + 毫秒级查询

4.3 亿行监护数据:Parquet 压缩 + 毫秒级查询

MIMIC-IV 里最大的一张表叫 chartevents,存的是监护仪数据:心率、血压、血氧、体温……每个 ICU 患者每秒都在产生记录。

4.33 亿行。

1.58 亿行的 labevents(化验结果)排第二,1000 万行的 datetimeevents(时间事件)排第三。光这三张表加起来,6 亿行。

整个 MIMIC-IV 的 Parquet 副本 9.2 GB,覆盖 hosp(住院)、icu(ICU)、ed(急诊)、derived(派生)、note(病历文本)、cxr(影像报告)六个模块。

● ● ●

4.3 亿行查询只要 0.03 秒

数据大是一回事,查得快是另一回事。

我们在 MacBook 上直接跑(无服务器、无索引、单机):

查询
结果
耗时
count(*)
 全表扫描 4.33 亿行
432,997,491
0.03s
WHERE itemid=220045
(心率)
8,752,069 行
0.28s
count(DISTINCT subject_id)
65,366
0.07s
心率 min/max/avg
1 / 296 / 86.2
0.51s

这不是优化过的数据库,就是 DuckDB 直接读 Parquet 文件。为什么这么快?

第一,Parquet 是列式存储。 数据按列存,查询只读需要的列。count(*) 甚至只读文件的元数据(行数直接写在文件头),不需要扫数据。

第二,谓词下推。WHERE itemid=220045 时,DuckDB 在读取阶段就把不匹配的行块跳过了,只解压命中的部分。

第三,压缩。 4.33 亿行、每行十几个字段,原始 CSV 估计要 30-50 GB;Parquet 压缩后 1.7 GB。读 1.7 GB 比读 50 GB 快一个数量级,磁盘 IO 就是性能。

● ● ●

这些数据里藏着什么

4.3 亿行不是冷冰冰的数字,随便看几个就有意思:

  • 心率平均值 86.2
    (正常静息 60-100),ICU 患者的平均水平——因为重症患者很多在用药、在发烧、在应激
  • 心率最值 1 到 296
    ——1 大概率是传感器脱落或记录错误,296 是极端心动过速。真实数据就是这样,脏值、缺失、异常并存
  • 65,366 个不同患者
    在 chartevents 里有记录(整个库 299,712 个患者)。只有进过 ICU 的患者才有监护数据

● ● ●

对我们做 AI 查询的意义

数据量大直接影响了我们怎么做 AI 问答(本系列第二篇的模板路线)。

第一,全表扫描不可怕,但没必要。 4.3 亿行全扫只要 0.03s,是因为 count(*) 读的是元数据。真做分析(JOIN、GROUP BY)还是要靠谓词下推把扫描范围缩小。我们的模板全部带 WHERE 条件(时间窗口、患者 ID),就是这个原因。

第二,模板必须预过滤。 「查患者 73713 的最近 5 条心率」,模板 SQL 是:

SELECT charttime, valuenum
FROM chartevents
WHERE subject_id = 73713 AND itemid = 220045
ORDER BY charttime DESC
LIMIT 5

subject_id 过滤在读取阶段就生效,4.3 亿行瞬间缩到几千行。LLM 直接写 SQL 容易漏掉 WHERE 条件,全表扫 4.3 亿行再排序,慢且容易超时。模板把「必须先过滤」写死在 SQL 里。

第三,大数据是评测的隐形维度。 我们评测 AI 答对没(本系列第八篇),31 题里的监控类查询(fluid_balance、ventilation)都是基于 chartevents 这种大表。小数据集上能跑的 SQL,在大表上可能超时、可能内存爆。评测题必须用真实规模的数据。

● ● ●

给做临床数据分析的人

  1. 01Parquet 是你的朋友
    。4.3 亿行压到 1.7 GB,查询快一个数量级。MIMIC 官方给的是 CSV,转 Parquet 是本地化的第一步(本系列第一篇)
  2. 02先看元数据再看数据
    。count(*) 在 Parquet 上是 0.03s 不是因为它快,是因为行数写在文件头。别拿它当「查询性能」的证据
  3. 03谓词下推是性能第一来源
    。WHERE 条件写在查询里,比事后过滤快几个数量级。AI 生成的 SQL 必须强制带过滤条件
  4. 04脏值要有心理准备
    。心率 1 到 296,真实临床数据就是这样。统计前先做合法性过滤(我们用的是 valuenum 大于 0 且小于 300)

● ● ●

参考来源

  1. 01
    MIMIC-IV 官方文档(chartevents 表说明):https://mimic.mit.edu/docs/iv/modules/icu/chartevents/
  2. 02
    DuckDB 官方文档(Parquet 扫描与谓词下推):https://duckdb.org/docs/data/parquet/overview.html
  3. 03
    Apache Parquet 格式规范(列式存储):https://parquet.apache.org/docs/file-format/
  4. 04
    本文统计基于本地 MIMIC-IV Parquet 副本(PhysioNet 申请),查询脚本:本系列实验数据