DataStore 提供两种兼容模式,用于控制输出是按 pandas 兼容方式组织,还是针对原生 SQL 性能进行优化。
概览
| 模式 | compat_mode 值 |
描述 |
|---|---|---|
| Pandas (默认) | "pandas" |
完整兼容 pandas 的行为。保留行顺序,支持 MultiIndex、set_index、dtype 修正、稳定排序的并列值判定,以及 -If/isNaN 包装器。 |
| Performance | "performance" |
采用 SQL 优先执行。移除所有 pandas 兼容性开销。吞吐量最高,但结果结构可能与 pandas 不一致。 |
性能模式会禁用哪些功能
| 开销 | Pandas 模式行为 | 性能模式行为 |
|---|---|---|
| 保留行顺序 | 注入 _row_id、rowNumberInAllBlocks() 和 __orig_row_num__ 子查询 |
已禁用——不保证行顺序 |
| 稳定排序的并列值决胜规则 | 在 ORDER BY 后追加 rowNumberInAllBlocks() ASC |
已禁用——并列值的顺序可能是任意的 |
| Parquet preserve_order | input_format_parquet_preserve_order=1 |
已禁用——允许并行读取 Parquet |
| GroupBy 自动 ORDER BY | 添加 ORDER BY group_key (pandas 默认为 sort=True) |
已禁用——返回的分组顺序可能是任意的 |
| GroupBy dropna WHERE | 添加 WHERE key IS NOT NULL (pandas 默认为 dropna=True) |
已禁用——包含 NULL 分组 |
| GroupBy set_index | 将分组键设为索引 | 已禁用——分组键保留为列 |
| MultiIndex 列 | agg({'col': ['sum','mean']}) 返回 MultiIndex 列 |
已禁用——使用扁平列名 (col_sum、col_mean) |
-If/isNaN wrapper |
为 skipna 使用 sumIf(col, NOT isNaN(col)) |
已禁用——直接使用 sum(col) (ClickHouse 原生会跳过 NULL) |
对 count 使用 toInt64 |
使用 toInt64(count()) 以匹配 pandas int64 |
已禁用——返回原生 SQL Dtype |
对全为 NaN 的求和使用 fillna(0) |
全为 NaN 时,求和结果返回 0 (pandas 行为) | 已禁用——返回 NULL |
| Dtype 修正 | abs() unsigned→signed 等 |
已禁用——原生 SQL Dtype |
| 索引保留 | SQL 执行后恢复原始索引 | 已禁用 |
first()/last() |
argMin/argMax(col, rowNumberInAllBlocks()) |
any(col) / anyLast(col)——更快,但具有非确定性 |
| 单 SQL 聚合 | ColumnExpr groupby 会物化中间 DataFrame | 将 LazyGroupByAgg 注入 lazy 操作链——生成单条 SQL 查询 |
启用性能模式
使用配置对象
from chdb.datastore.config import config
# Enable performance mode
config.use_performance_mode()
# Back to pandas compatibility
config.use_pandas_compat()
# Check current mode
print(config.compat_mode) # 'pandas' or 'performance'使用模块级函数
from chdb.datastore.config import set_compat_mode, CompatMode, is_performance_mode
# Enable performance mode
set_compat_mode(CompatMode.PERFORMANCE)
# Check
print(is_performance_mode()) # True
# Back to default
set_compat_mode(CompatMode.PANDAS)使用便捷导入方式
from chdb import use_performance_mode, use_pandas_compat
use_performance_mode()
# ... high-performance operations ...
use_pandas_compat()何时使用性能模式
在以下情况下使用性能模式:
- 处理大型数据集 (数十万到数百万行)
- 运行以聚合为主的工作负载 (groupby、sum、mean、count)
- 行顺序并不重要 (例如聚合结果、报表、仪表盘)
- 你希望获得最高的 SQL 吞吐量和尽可能低的额外开销
- 你关心内存占用 (并行读取 Parquet,无中间 DataFrames)
在以下情况下应继续使用 pandas 模式:
- 你需要完全一致的 pandas 行为 (行顺序、MultiIndex、dtypes)
- 你依赖
first()/last()返回真正的第一行/最后一行 - 你使用依赖行顺序的
shift()、diff()、cumsum() - 你在编写测试,将 DataStore 的输出与 pandas 进行比较
行为差异
行顺序
在性能模式下,任何操作的行顺序都无法保证。这包括:
- 过滤器结果
- GroupBy 聚合结果
- 未显式调用
sort_values()时的head()/tail() first()/last()聚合
如果需要有序结果,请显式添加 sort_values():
config.use_performance_mode()
ds = pd.read_csv("data.csv")
# Unordered (fast)
result = ds.groupby("region")["revenue"].sum()
# Ordered (still fast, just adds ORDER BY)
result = ds.groupby("region")["revenue"].sum().sort_values()GroupBy 结果
| 方面 | Pandas 模式 | 性能模式 |
|---|---|---|
| 分组键所在位置 | 索引 (通过 set_index) |
普通列 |
| 分组顺序 | 默认按键排序 | 任意顺序 |
| NULL 分组 | 排除 (默认 dropna=True) |
包含 |
| 列名格式 | 多重聚合时使用 MultiIndex | 扁平名称 (col_func) |
first()/last() |
确定性 (按行顺序) | 非确定性 (any()/anyLast()) |
聚合
config.use_performance_mode()
# Sum of all-NaN group returns NULL (not 0)
# Count returns native uint64 (not forced int64)
# No -If wrappers: sum() instead of sumIf()
result = ds.groupby("cat")["val"].sum()单条 SQL 执行
在性能模式下,ColumnExpr 的 groupby 聚合 (例如 ds[condition].groupby('col')['val'].sum()) 会作为单条 SQL 查询执行,而不是像 pandas 模式那样分两步执行:
config.use_performance_mode()
# Pandas mode: two SQL queries (filter → materialize → groupby)
# Performance mode: one SQL query (WHERE + GROUP BY in same query)
result = ds[ds["rating"] > 3.5].groupby("category")["revenue"].sum()
# Generated SQL (single query):
# SELECT category, sum(revenue) FROM data WHERE rating > 3.5 GROUP BY category这避免了中间 DataFrame 的物化过程,并可显著减少内存占用和执行时间。
与执行引擎的对比
性能模式 (compat_mode) 和执行引擎 (execution_engine) 是相互独立的配置维度:
| 配置项 | 控制内容 | 取值 |
|---|---|---|
execution_engine |
由哪个引擎执行计算 | auto, chdb, pandas |
compat_mode |
是否为实现 Pandas 兼容性而调整输出形态 | pandas, performance |
将 compat_mode='performance' 设为性能模式时,execution_engine 也会自动设为 chdb,因为性能模式是专为 SQL 执行设计的。
from chdb.datastore.config import config
# These are independent
config.use_chdb() # Force chDB engine, keep pandas compat
config.use_performance_mode() # Force chDB + remove pandas overhead在性能模式下测试
为性能模式编写测试时,结果的行顺序和结构格式可能与 pandas 不同。请使用以下策略:
排序后比较 (聚合、过滤)
# Sort both sides by the same columns before comparing
ds_result = ds.groupby("cat")["val"].sum()
pd_result = pd_df.groupby("cat")["val"].sum()
ds_sorted = ds_result.sort_index()
pd_sorted = pd_result.sort_index()
np.testing.assert_array_equal(ds_sorted.values, pd_sorted.values)值域检查 (首项/末项)
# first() with any() returns an arbitrary element from the group
result = ds.groupby("cat")["val"].first()
for group_key in groups:
assert result.loc[group_key] in group_values[group_key]schema 与计数 (不带 ORDER BY 的 LIMIT)
# head() without sort_values: row set is non-deterministic
result = ds.head(5)
assert len(result) == 5
assert set(result.columns) == expected_columns最佳实践
1. 尽早在脚本中启用
from chdb.datastore.config import config
config.use_performance_mode()
# All subsequent operations benefit
ds = pd.read_parquet("data.parquet")
result = ds[ds["amount"] > 100].groupby("region")["amount"].sum()2. 顺序很重要时,请显式排序
# For display or downstream processing that expects order
result = (ds
.groupby("region")["revenue"].sum()
.sort_values(ascending=False)
)3. 适用于批处理/ETL 工作负载
config.use_performance_mode()
# ETL pipeline — order doesn't matter, throughput does
summary = (ds
.filter(ds["date"] >= "2024-01-01")
.groupby(["region", "product"])
.agg({"revenue": "sum", "quantity": "sum", "rating": "mean"})
)
summary.to_df().to_parquet("summary.parquet")4. 在会话内切换模式
# Performance mode for heavy computation
config.use_performance_mode()
aggregated = ds.groupby("cat")["val"].sum()
# Back to pandas mode for exact-match comparison
config.use_pandas_compat()
detailed = ds[ds["val"] > 100].head(10)- 执行引擎 — 引擎选择 (auto/chdb/pandas)
- 性能指南 — 通用优化建议
- 与 pandas 的关键区别 — 行为差异