当前位置: 首页 > news >正文

DeepSeek总结的从 DuckDB 迁移到 chDB基准测试

来源: https://github.com/chdb-io/cookbook/tree/main/migration-from-duckdbBENCHMARK.md迁移基准测试 —— 深度探讨本文是从 DuckDB 迁移到 chDB指南的配套文档。指南的第 5 节将环境/场景/结果/摘要内联呈现本文件则包含不适合指南风格流程的部分摄取路径方法、并排 SQL 的案例研究、DataFrame 往返操作矩阵以及存储引擎权衡细节。有关可运行代码和复现步骤请参阅 README.md。规范结果位于benchmark/results_aligned.json。为什么这里有 18 个查询而指南只突出了 16 个我们运行了18 个查询以在 DuckDB 用户可能使用的每个维度上公平评估两个引擎类型化 JSON (Q1–Q3)、pandas 兼容 API (Q4)、AI 代理检索 (Q5)、漏斗/序列聚合 (Q6–Q7)、多百分位数 (Q8)、Parquet 上的基线分析 SQL (Q9–Q13)、参考查询 (Q14–Q15)、Parquet → DataFrame 导出 (Q16)以及两个存储引擎探测Q17 持久存储工作流Q18 主键范围扫描。Q17 和 Q18 在 chDB 不利的方向上产生了很大的标题差距例如DuckDB 在 Q17 上快约 14 倍。经检查这些差距反映了chDBMergeTree存储引擎的设计选择——MergeTree在写入时构建排序索引并维护主键记账这些成本在多个后续查询中摊销但在 5 个查询的工作流或仅触及几行的查询上会表现为原始开销。它们不是查询内核的性能差距并且这种架构选择一次写入排序多次廉价扫描对于 chDB 典型的遥测/分析工作负载是正确的尽管它输掉了这些特定的微基准测试。如果指南的第 5 节将 Q17/Q18 与内核查询并列那么标题数字会误导读者为典型的代理或笔记本工作负载做出引擎选择决策。因此指南突出了 16 个内核查询 (Q1–Q16)我们在本文件的末尾完整记录了 Q17/Q18存储引擎权衡——既是为了透明也是为了让任何工作负载确实是“一次性 ETL 少量后续查询”的人能够做出明智的选择。数据加载方式摄取路径方法JSON 工作负载 (Q1–Q3) 被平等地加载但设计上存储方式不同因为存储策略正是指南第 2.1 节比较的内容chDB:CREATE TABLE events (data JSON) ENGINE Memory和INSERT INTO events SELECT … FROM file(events.jsonl, JSONEachRow)——JSON类型在加载时提取类型化的子列因此data.user.tier.:String稍后读取的是一个压缩列。DuckDB:CREATE TABLE events AS SELECT … FROM read_json(events.jsonl)列类型化为JSONDuckDB v1.2 类型化JSON。路径访问在查询时针对编码值进行解析。两个引擎都摄取相同的 JSONL 文件并使用引擎提供的类型化JSON类型。Q1–Q3 查询数字仅为查询时间摄取时间不计入任何引擎的计时器。chDB 在摄取期间摊销了每个路径的提取成本而 DuckDB 没有但 DuckDB 随后在查询时为每行支付该成本。对于一次摄取多次查询的工作负载代理-事件模式这是我们想要衡量的架构权衡。对于一次摄取一次查询的工作负载这会使几百毫秒的差异偏向 DuckDB——如果这对您很重要请在您自己的数据上量化这一点。分析 SQL 工作负载 (Q9–Q15) 在两个引擎上读取相同的六个 Parquet 文件。案例研究——为什么 chDB 赢得每个工作负载案例 A — 类型化 JSON 子列路径访问编译为列读取Q1快 8.4 倍—— §2.1两个引擎都将列存储为JSON类型DuckDB v1.2 也提供类型化JSON类型而非旧的“存储为文本”表示并接受相同的路径访问语法。架构差异在于子列提取发生的时间——chDB 在加载时DuckDB 在查询时——成本立即显现。chDB— 带有类型化子列后缀的路径表示法SELECTdata.response.status.:StringASstatus,count(*)ASnFROMeventsGROUPBYstatusORDERBYnDESC.:String后缀指向 chDB 在加载时提取的类型化子列。读取是 O(1) 进入一个具有其自身最小/最大值和压缩的压缩列——无需每行 JSON 解析。DuckDB— 针对JSON类型列的相同路径语法SELECTdata.response.statusASstatus,count(*)ASnFROMeventsGROUPBYstatusORDERBYnDESCDuckDB 的类型化JSON在查询时针对编码值解析路径而不是读取预先提取的类型化子列。结果是正确的每行成本更高。在 100 万条合成代理-事件记录上DuckDB 35 毫秒 → chDB 4 毫秒8.4 倍。双路径和过滤加分组变体Q2Q3即使增加了算术运算也显示出 3.0–3.8 倍的胜率。对于累积 JSON 工具输出上下文并重复切片它的代理管道来说这是整个基准测试中最大的独立胜利。案例 B — DataStore一个 DuckDB 没有等价物的即插即用 pandas API—— §2.2DataStore 是使 chDB 成为已经用 pandas 编写的代理代码库的可行替代品的原因。相同的源代码只需更改一行导入语句importdatastoreaspd# 唯一更改的一行dfpd.read_parquet(yellow_tripdata_2024-01.parquet,columns[passenger_count,fare_amount,tip_amount,trip_distance])df.groupby(passenger_count).agg(avg_fare(fare_amount,mean),sum_tip(tip_amount,sum),avg_dist(trip_distance,mean),n(fare_amount,count),).to_pandas()# 物化惰性计划DataStore 通过 chDB 引擎将表达式编译为 ClickHouse SQL惰性的.to_pandas()调用物化结果。过滤器、分组、连接、时间分桶、窗口函数、字符串和日期时间访问器——每个常见的 pandas 习惯用法都保持相同的形状。覆盖率约 300 个 pandas 风格的方法209 个DataFrame 56 个.str 42 个.dt访问器外加 334 个 ClickHouse SQL 函数作为 DataStore 方法公开用于 pandas 没有本地名称的事物。对于已经使用 pandas 的代理和笔记本代码迁移只需更改一行import代码库的其余部分保持不变。DuckDB 没有等效的 pandas 方法表面——DuckDB 的 Python API 公开了 SQL 表面通过替换扫描您可以直接针对 DataFrame 执行duckdb.sql(SELECT … FROM pdf)无需register()但是以df.filter(...).groupby(...).agg(...)链式 pandas 调用编写的代码库必须重写为 SQL。使用 chDB链式语法保持不变。案例 C — 向量搜索集成优于原始扫描速度Q5—— §2.3生产代理的检索路径很少只是余弦相似度——它看起来像JSON 类型化元数据过滤器 → 向量 top-K 余弦 → SQL 连接会话历史 → 返回 DataFramechDB 将整个管道作为一个 SQL 语句在一个进程内引擎上运行向量在Array(Float32)中会话内存位于chdb.session.Session(path)JSON 元数据作为类型化子列案例 A以及顶部的分析 SQL。DuckDB 在内核中拥有原始距离函数array_cosine_distance因此线性扫描检索不需要扩展但周围的组件并未共置——没有原生的会话抽象外部 SQLite / Postgres / Redis并且 HNSW 索引需要INSTALL/LOAD vss。在 DuckDB 上检索管道仍然需要支付 3-8 倍的 JSON 元数据过滤差距案例 A。对于孤立的内核——SELECT id, cosineDistance(emb, [...]) ORDER BY d LIMIT 10在 10 万 × 384 维随机单位向量上无索引——DuckDB 35 毫秒 vs chDB 64 毫秒。这是一个真实但操作上很小的 30 毫秒差距两个引擎都远低于 100 毫秒的交互阈值。使用近似最近邻索引chDB 向量跳过索引DuckDBvss线性扫描的差异无关紧要。对于代理来说有趣的比较是工作流而不是内核。案例 D —windowFunnel一行代码进行漏斗分析Q6快 2.61 倍—— §2.8对于每个上车区找到在一小时窗口内匹配以下序列的最长前缀低票价$15→高票价$50→机场行程。chDB— 一个聚合函数SELECTPULocationID,windowFunnel(3600)(tpep_pickup_datetime,fare_amount15,fare_amount50,Airport_fee0)ASfunnel_levelFROMtripsGROUPBYPULocationIDDuckDB— 没有原生的漏斗函数所以需要一个 CTE 链在事件流上使用LAG(step,1) / LAG(ts,1) / LAG(step,2) / LAG(ts,2)再加上CASE WHEN step3 AND prev12 AND prev21 AND EPOCH(ts-prev2_ts) 3600 THEN 3 …。完整的 DuckDB 版本在workload_aligned_duckdb.pyQ6 中——大约三十行代码而 chDB 只有六行。在 1800 万行上274 毫秒 → 105 毫秒2.61 倍。DuckDB 版本峰值内存约为 2.0 GB RSS为LAG排序的中间结果chDB 峰值为 1.4 GB。案例 E —sequenceCount单个聚合中的模式匹配Q7快 4.54 倍—— §2.8计算每个上车区出现序列低票价 → 高票价 → 非常高票价的次数然后求和。chDBSELECTPULocationID,sequenceCount((?1)(?2)(?3))(tpep_pickup_datetime,fare_amount15,fare_amount50,fare_amount70)ASseq_countFROMtripsGROUPBYPULocationID(?1)(?2)(?3)模式是一个类似正则表达式的表达式作用于事件谓词——chDB 内置了匹配引擎。sequenceMatch布尔变体和每窗口变体紧随其后。DuckDB基本上需要与案例 D 中漏斗情况相同的 LAG-CTE 结构再加上一个GROUP BY来汇总计数。在 1800 万行上230 毫秒 → 51 毫秒4.54 倍并且 chDB 版本只有几行代码而 DuckDB 大约有三十行。案例 F —quantilesTDigest一个草图多个百分位数Q8快 2.35 倍—— §2.9票价金额的 P50、P95 和 P99忽略零金额行程——两个引擎都支持单草图多百分位数 API。chDBSELECTquantilesTDigest(0.5,0.95,0.99)(fare_amount)ASpctsFROMfile(yellow_tripdata_2024-*.parquet,Parquet)WHEREfare_amount0DuckDB— 列表形式的approx_quantile单次扫描单个 TDigest 草图——与 chDB 公平比较SELECTapprox_quantile(fare_amount,[0.5,0.95,0.99])ASpctsFROMread_parquet(...)WHEREfare_amount0两个查询都从数据上的单个 TDigest 草图生成一个数组[p50, p95, p99]。实现差异体现在原始吞吐量上在 1800 万行上64 毫秒 → 27 毫秒chDB 快 2.35 倍。百分位仪表板模式无处不在SLO 报告p99 延迟分析因此即使草图内核上有 2 倍的优势在集群规模上也会累积。chDB 系列也更广泛——quantilesExact、quantilesGK、quantilesBFloat16Weighted——当您需要与 TDigest 不同的精度/内存权衡时API 形状保持相同。案例 G — Parquet → DataFrame 导出零拷贝物化Q16冷启动快 1.61 倍 / 热启动快 2.99 倍加载一个 Parquet 文件并将完整结果作为 pandas DataFrame 返回——这是输出路径。这是 chDB 在零拷贝博客2026 年 1 月ClickBench 命中100 万行中发布的“DataFrame 导出比 DuckDB 快 24%”声明背后的操作。在我们 300 万行 × 19 列的 NYC TLC 文件上冷启动 392 毫秒 → 244 毫秒快 1.61 倍减少 38%热启动 325 毫秒 → 108 毫秒快 2.99 倍减少 67%。两者均达到或超过博客。机制chDB 的__arrow_c_stream__零拷贝 SIMD 路径直接将列物化到 NumPy 中无需中间的 Arrow → pandas 复制。重要——与Python(df)不同。Q16 是输出路径。Q13/Q15 的Python(df)表函数是输入路径现有的 pandas DataFrame → SQL → DataFrame经过不同的机制。那里的性能取决于操作——请参阅下面的“DataFrame 往返——输入取决于操作”。说明——§2.5 的连接优势不是基准测试行16 个内核查询衡量固定输入形状上的内核性能。它们不衡量 §2.5——约 80 种格式、12 种以上连接器、三个流式引擎的核心内建表面——这体现在部署形态上而不是毫秒级别没有INSTALL/LOAD链没有需要 pip 安装到代理运行时的 MongoDB / Redis 客户端没有单独的 Kafka / RabbitMQ / NATS 消费者进程没有在 SQL 之前对 Protobuf / Avro / MsgPack 输入进行 Python 端解码。对于数据已经是干净 Parquet 文件的代理来说这无关紧要对于数据是周围系统发出的数据洪流的代理来说这是最大的单一操作差异并且在任何单引擎查询计时中都是不可见的。DataFrame 往返——输入取决于操作Q16输出路径和 Q13/Q15输入路径经过不同的机制输入路径的结果取决于操作而不是任一引擎的全面胜利路径操作结果输出 — Parquet → DataFrame (Q16)完整文件导出chDB 冷启动快 1.61 倍热启动快 2.99 倍输入进程中热启动1000 万行COUNT(*)chDB 快 1.4 倍输入进程中热启动1000 万行过滤 COUNTchDB 快 1.1 倍输入进程中热启动1000 万行宽 (60 列) DF 上的COUNT(*)chDB 快 2.1 倍输入进程中热启动1000 万行GROUP BYDuckDB 快 1.0–1.3 倍输入子进程冷启动 (Q13 / Q15)GROUP BYDuckDB 快 1.6–2 倍主要是 chDB 引擎启动成本操作类型比引擎选择更重要——chDB 在轻量级聚合上明显胜出DuckDB 在GROUP BY上有微弱的优势。对于短暂的 Lambda 风格调用子进程冷启动的惩罚是真实存在的但不是稳定的Python(df)与register()的差异。运行benchmark/bench_input_path_scale.py和benchmark/bench_input_path_variants.py可在您自己的硬件上复现这些数字。存储引擎权衡Q17 / Q18这是指南未在其主要结果中包含的两个查询原因已预先说明它们衡量的是 chDBMergeTree存储引擎的设计选择而不是查询内核性能。以下是完整的数字和架构原理以便任何工作负载处于此角落的人都能做出明智的决定。Q17 — 持久存储工作流CREATE TABLE … AS SELECT 5 个后续查询DuckDB129 毫秒vs chDB1854 毫秒—— DuckDB 在此特定形态上快约 14 倍。发生了什么chDB 的MergeTree在写入时构建排序索引——整个行范围在主键上排序部分在后台合并存储布局支付前期成本以便后续查询可以使用稀疏主键索引、区域地图修剪和跳过索引来廉价读取。DuckDB 的持久存储是单个文件没有单独的排序步骤没有每列索引——写入更快读取使用 Parquet 风格的区域地图。这种权衡是真实的如果您的工作负载是一次性 ETL 5 个后续查询您将承担前期排序成本而无法摊销它并且 DuckDB 的单文件写入以 14 倍的因子获胜因为绝对时间由CREATE TABLE步骤主导。如果您的工作负载是持久化一次 针对同一表运行数百个查询chDB 为之设计的典型可观测性/多租户分析形态则前期成本会被摊销MergeTree的索引结构会领先。对于一次性 ETL 然后查询的工作流DuckDB 的单文件写入是正确的选择。对于具有许多后续读取的长期持久表chDB 的MergeTree是正确的选择。Q17 的标题数字反映了第一种形态。Q18 — 在排序的时间戳列上进行主键范围扫描DuckDB0.4 毫秒vs chDB2.9 毫秒—— 绝对差距2.5 毫秒。比率看起来令人担忧DuckDB 优势约 7 倍但按绝对值计算这是在仅触及少量行的查询上的几毫秒。在这个规模上chDB 方面的主要成本是MergeTree的主键记账稀疏索引查找标记范围解析——在较大行数下不可见的固定开销但在实际扫描工作低于毫秒时变得可见。DuckDB 在此形态上的查找基本上是仅元数据。随着范围扩大相对差距急剧缩小在匹配行数 10 万时chDB 达到或超过 DuckDB。Q18 的形态小范围排序列适合几个标记确实是 DuckDB 的领域但仅限于毫秒级预算而 chDB 根本就不是您会首先考虑的引擎。综合解读标题比率DuckDB 优势 14 倍和 7 倍在数学上是正确的。它们对于典型的 chDB 工作负载不是选择决定性的——它们衡量的是存储设计的前期成本该成本仅在跨许多针对相同数据的查询中才能得到回报以及为数十亿行表设计的索引结构的常数时间开销。为代理或笔记本工作负载在 chDB 和 DuckDB 之间进行选择的读者应将其视为边界信息“如果您的工作负载完全像这样请留在 DuckDB 上”而不是内核性能的结论。这也是指南突出 16 个内核查询的原因将 Q17/Q18 与 Q1–Q16 一起展示会使读者锚定在最大的标题差距上而这在此处是引擎最不相关的维度。
http://www.zskr.cn/news/1361291.html

相关文章:

  • OpenSSH PKCS#11双重释放漏洞深度解析与实战防护
  • SQL报错注入实战:MySQL/PostgreSQL/Oracle三库绕过与数据提取
  • CVE-2025-68493深度解析:OGNL沙箱坍塌与Java Web内网横向移动
  • 案发现场时空回溯:UWB无法全域留痕,无感定位全链路可复盘
  • 无授权不感知、无穿戴可溯源:无感定位重构公安新型治安底座
  • 讲讲libevent底层机制
  • 宁夏买家电推荐去哪里 - 资讯纵览
  • AI智能体运行时正走向操作系统化:从血泪工程到基础设施
  • BepInEx插件开发全解析:Unity游戏Mod生态基建指南
  • 大模型规模信仰的科学反思:数据、架构与训练策略的结构性失衡
  • Unity八叉树优化碰撞检测:高性能空间索引实战
  • 智能体的人格化设计:如何平衡一致性、多样性与用户偏好?
  • 2021 AI落地三大支点:模型压缩、MLOps闭环与小样本学习实战
  • FairyGUI GLoader动效动态接管与运行时替换实战
  • GPT-4稀疏激活机制解析:1.8万亿参数为何仅用2%
  • 潜变量扩散模型原理解析:从宝可梦生成看LDM工程落地
  • 神经网络初始化三大问题:梯度爆炸、激活塌缩与对称性破缺
  • 机器学习工程师实战书单:9本通过代码验证的黄金工具书
  • 如何深度破解百度网盘macOS版:SVIP解锁与下载速度优化完全指南
  • 广州离婚律师哪家服务好 - 资讯纵览
  • 弱监督学习实战:用规则和模型快速生成高质量训练标签
  • Unity中大型项目性能瓶颈与架构设计缺陷深度解析
  • Unity开发者首选VSCode配置指南:高效替代Visual Studio
  • FlashAttention的OOM排查:为什么显存够了还是报内存不足?
  • 鸿蒙签名验证报错UNABLE_TO_VERIFY_LEAF_SIGNATURE根因解析
  • DVWA中SVG文件上传触发XSS漏洞实战解析
  • Mythos能力跃迁:大模型因果建模与可信度感知技术解析
  • JMeter分布式压测实战:从单机瓶颈到三节点集群搭建
  • Mythos模型:通用大模型在网络安全领域的范式跃迁
  • 好用的深圳谷歌SEO服务商推荐 - 资讯快报