最近在数据仓库项目中遇到一个典型问题:当单表数据量突破5亿条记录后,传统关系型数据库的查询性能开始急剧下降。团队需要评估在OLAP场景下,轻量级分析型数据库DuckDB与传统MySQL的性能差异。这个测试源于真实业务需求——我们每天需要处理超过2TB的日志数据,并支持业务部门的实时分析查询。
测试环境采用AWS r5.2xlarge实例(8 vCPU/64GB内存),数据集使用纽约出租车行程记录的公开数据集,包含:
MySQL 8.0.32 配置要点:
ini复制innodb_buffer_pool_size=48G
innodb_io_capacity=2000
innodb_flush_neighbors=0
query_cache_type=0
建立复合索引:(pickup_datetime, payment_type)
DuckDB 0.8.1 配置:
sql复制SET threads TO 8;
SET memory_limit='60GB';
数据以Parquet格式存储,DuckDB直接读取文件系统
设计四类典型分析查询:
时间范围聚合(按月统计行程量)
sql复制SELECT DATE_TRUNC('month', pickup_datetime) AS month,
COUNT(*) AS trips
FROM trips
WHERE pickup_datetime BETWEEN '2015-01-01' AND '2019-12-31'
GROUP BY 1;
多表关联分析(车型与收入的关联)
sql复制SELECT v.vehicle_type,
AVG(t.total_amount) AS avg_amount
FROM trips t JOIN vehicles v ON t.vehicle_id = v.id
GROUP BY 1;
窗口函数计算(按司机ID统计移动平均)
sql复制SELECT driver_id, pickup_datetime,
AVG(total_amount) OVER (
PARTITION BY driver_id
ORDER BY pickup_datetime
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
) AS moving_avg
FROM trips;
复杂条件过滤(空间+时间复合查询)
sql复制SELECT COUNT(*)
FROM trips
WHERE ST_Distance(pickup_location, ST_Point(-73.98, 40.75)) < 1000
AND pickup_datetime BETWEEN '2019-01-01' AND '2019-01-31';
| 查询类型 | MySQL | DuckDB | 差异倍数 |
|---|---|---|---|
| 时间范围聚合 | 28.4 | 3.2 | 8.9x |
| 多表关联分析 | 112.7 | 18.5 | 6.1x |
| 窗口函数计算 | 263.8 | 41.2 | 6.4x |
| 复杂条件过滤 | 89.3 | 6.7 | 13.3x |
注意:所有测试均执行3次取平均值,清空OS缓存后测试
内存峰值:
磁盘IO:
随着数据量增长(1亿→5.8亿记录),观察到:
DuckDB采用向量化执行引擎,其核心优化包括:
cpp复制// DuckDB向量化过滤的简化伪代码
void Filter(Vector &input, SelectionVector &sel) {
for(idx_t i = 0; i < input.size; i += VECTOR_SIZE) {
// 一次处理1024个值
VectorData vdata;
input.Orrify(i, vdata);
for(idx_t j = 0; j < VECTOR_SIZE; j++) {
if (CheckCondition(vdata, j)) {
sel.Append(vdata.sel->get_index(j));
}
}
}
}
测试中发现MySQL主要耗时在:
对于必须使用MySQL的场景:
sql复制ALTER TABLE trips PARTITION BY RANGE (YEAR(pickup_datetime)) (
PARTITION p2015 VALUES LESS THAN (2016),
PARTITION p2016 VALUES LESS THAN (2017),
...
);
内存不足错误:
bash复制# 错误示例:Error: Out of Memory
解决方案:
sql复制SET memory_limit='60GB'; -- 不超过物理内存的90%
SET temp_directory='/mnt/tmp'; -- 指定临时文件目录
并行度设置不当:
| 数据量 | MySQL聚合查询 | DuckDB聚合查询 |
|---|---|---|
| 1亿 | 5.2s | 0.8s |
| 3亿 | 16.1s | 2.4s |
| 5.8亿 | 28.4s | 3.2s |
测试Parquet vs CSV加载:
从测试数据来看,在5亿级数据量下:
实际项目中,我们最终采用混合架构:
这种架构使我们的月报生成时间从原来的6小时缩短到23分钟,同时硬件成本降低60%。对于需要频繁全表扫描的分析场景,列式存储引擎的优势是决定性的。