本文介绍在本地同样配置的PC中,使用同样体量的数据集,对比DuckDB与MySQL的对不同SQL语句场景地查询速度表现。结果是大多数业务需求场景下,DuckDB的查询速度基本都比MySQL快10倍左右。
PC配置:24核64GB内存
mysql版本:8.0.12
duckdb版本:1.5.1
python版本:3.12.10
数据集说明
** 数据集获取:https://tooli.fun/resource_detail?resource_id=330**
-
核心数据表:stock_daily
-
数据行数:14150202
-
导出CSV大小: 1.15GB
-
数据表结构:
CREATE TABLE
stock_daily(
ts_codevarchar(10) NOT NULL COMMENT '股票代码',
trade_datedatetime NOT NULL COMMENT '交易日期',
openfloat DEFAULT NULL COMMENT '开盘价',
highfloat DEFAULT NULL COMMENT '最高价',
lowfloat DEFAULT NULL COMMENT '最低价',
closefloat DEFAULT NULL COMMENT '收盘价',
pre_closefloat DEFAULT NULL COMMENT '昨收价',
changefloat DEFAULT NULL COMMENT '涨跌额',
pct_chgfloat DEFAULT NULL COMMENT '涨跌幅(未复权)',
volfloat DEFAULT NULL COMMENT '成交量(手)',
amountfloat DEFAULT NULL COMMENT '成交额(千元)',
adj_factorfloat DEFAULT NULL COMMENT '复权因子(前复权QFQ=当日收盘价×当日复权因子/最新复权因子,后复权HFQ=当日收盘价×当日复权因子)',
PRIMARY KEY (ts_code,trade_date),
KEYidx_trade_date(trade_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 -
维度表:stock_base
-
数据行数:5362
-
导出CSV大小: 1.12MB
-
数据表结构:
CREATE TABLE
stock_base(
ts_codevarchar(10) NOT NULL COMMENT 'TS代码',
symbolvarchar(10) DEFAULT NULL COMMENT '股票代码',
namevarchar(64) DEFAULT NULL COMMENT '股票名称',
areavarchar(64) DEFAULT NULL COMMENT '地域',
industryvarchar(64) DEFAULT NULL COMMENT '所属行业',
fullnamevarchar(255) DEFAULT NULL COMMENT '股票全称',
ennamevarchar(255) DEFAULT NULL COMMENT '英文全称',
cnspellvarchar(64) DEFAULT NULL COMMENT '拼音缩写',
marketvarchar(64) DEFAULT NULL COMMENT '市场类型(主板/创业板/科创板/CDR)',
exchangevarchar(64) DEFAULT NULL COMMENT '交易所代码',
curr_typevarchar(16) DEFAULT NULL COMMENT '交易货币',
list_statusvarchar(16) DEFAULT NULL COMMENT '上市状态 L上市 D退市 P暂停上市',
list_datedatetime DEFAULT NULL COMMENT '上市日期',
delist_datedatetime DEFAULT NULL COMMENT '退市日期',
is_hsvarchar(16) DEFAULT NULL COMMENT '是否沪深港通标的,N否 H沪股通 S深股通',
PRIMARY KEY (ts_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 -
维度表:stock_company
-
数据行数:5146
-
导出CSV大小: 9.9MB
-
数据表结构:
CREATE TABLE
stock_company(
ts_codevarchar(10) NOT NULL COMMENT '股票代码',
exchangevarchar(10) DEFAULT NULL COMMENT '交易所代码 ,SSE上交所 SZSE深交所',
chairmanvarchar(64) DEFAULT NULL COMMENT '法人代表',
managervarchar(64) DEFAULT NULL COMMENT '总经理',
secretaryvarchar(64) DEFAULT NULL COMMENT '董秘',
reg_capitalfloat DEFAULT NULL COMMENT '注册资本',
setup_datedatetime DEFAULT NULL COMMENT '注册日期',
provincevarchar(64) DEFAULT NULL COMMENT '所在省份',
cityvarchar(64) DEFAULT NULL COMMENT '所在城市',
introductiontext COMMENT '公司介绍',
websitevarchar(255) DEFAULT NULL COMMENT '公司主页',
emailvarchar(64) DEFAULT NULL COMMENT '电子邮件',
officevarchar(255) DEFAULT NULL COMMENT '办公室',
business_scopetext COMMENT '经营范围',
employeesint(11) DEFAULT NULL COMMENT '员工人数',
main_businesstext COMMENT '主要业务及产品',
PRIMARY KEY (ts_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
SQL运行对比
SQL:统计数据量
-- MySQL查询,耗时:8.038秒
select COUNT(*) from stock_daily;
-- DuckDB 查询CSV文件 耗时:0.452秒
select count(*) from 'e:/data/data_set/stock/stock_daily.csv';
SQL:按实际股票分组统计每只股票的交易天数
-- MySQL查询,耗时:2.627秒
SELECT ts_code,COUNT(*) FROM stock_daily GROUP BY ts_code;
-- DuckDB查询CSV文件, 耗时:0.478秒
SELECT ts_code,COUNT(*) FROM 'e:/data/data_set/stock/stock_daily.csv' GROUP BY ts_code;
SQL:按实际股票分组统计每只股票的交易天数,并按照天数排序
-- MySQL查询,耗时:13.16秒
SELECT ts_code,COUNT(*) AS days FROM stock_daily GROUP BY ts_code ORDER BY days;
-- DuckDB查询,耗时:0.495秒
SELECT ts_code,COUNT(*) AS days FROM 'e:/data/data_set/stock/stock_daily.csv' GROUP BY ts_code ORDER BY days;
SQL:查询2020年5月7号上涨的股票数量(收盘价>开盘价)
-- MySQL查询, 耗时:4.194秒
SELECT COUNT(*) AS up FROM stock_daily WHERE trade_date='2020-05-07 00:00:00' and `close` > `open`;
-- DuckDB查询, 耗时:0.511秒
result = conn.execute("""
SELECT COUNT(*) AS up FROM read_csv(
'e:/data/data_set/stock/stock_daily.csv',
auto_detect = true,
nullstr = ['\\N']
)
WHERE trade_date = DATE '2020-05-07' and close>open;
""").fetchall()
SQL:查询2020年5月7号上涨的股票数量,按照行业分组
特点:读取2个csv文件,left join 一次
-- MySQL查询, 耗时:4.705秒
SELECT b.industry,COUNT(*) AS up FROM stock_daily a LEFT JOIN stock_base b ON a.ts_code = b.ts_code WHERE a.trade_date='2020-05-07 00:00:00' AND a.`close` > a.`open` GROUP BY b.industry ORDER BY up DESC;
-- DuckDB查询, 耗时:0.596秒
SELECT b.industry,COUNT(*) AS up FROM read_csv(
'e:/data/data_set/stock/stock_daily.csv',
auto_detect =true,
nullstr = ['\\N']
) a leftjoin
read_csv(
'e:/data/data_set/stock/stock_base.csv',
auto_detect =true,
nullstr = ['\\N']
) b
ON a.ts_code = b.ts_code
WHERE a.trade_date =DATE'2020-05-07'AND a.close>a.open
GROUPBY b.industry ORDERBY up DESC;
SQL:查询2020年5月7号上涨的股票数量,按交易所和行业分组
特点:读取3个csv文件,left join 两次,
-- MySQL查询, 耗时:4.705秒
SELECT c.exchange,b.industry,COUNT(*) AS up FROM stock_daily a LEFT JOIN stock_base b ON a.ts_code = b.ts_code LEFT JOIN stock_company c ON a.ts_code=c.ts_code WHERE a.trade_date='2020-05-07 00:00:00' AND a.close>a.open GROUP BY c.exchange,b.industry ORDER BY up DESC;
--DuckDB查询, 耗时:0.883秒
SELECT c.exchange,b.industry,COUNT(*) AS up FROM read_csv(
'e:/data/data_set/stock/stock_daily.csv',
auto_detect = true,
nullstr = ['\\N']
) a
left join read_csv(
'e:/data/data_set/stock/stock_base.csv',
auto_detect = true,
nullstr = ['\\N']
) b ON a.ts_code = b.ts_code
left join read_csv(
'e:/data/data_set/stock/stock_company.csv',
auto_detect = true,
nullstr = ['\\N']
) c
ON a.ts_code = c.ts_code
WHERE a.trade_date = DATE '2020-05-07' AND a.close>a.open
GROUP BY c.exchange,b.industry ORDER BY up DESC;
结果说明
以上例子都是使用DuckDB直接查询CSV文件。同时也能直接查询parquet文件。读取Excel文件需要安装相应的扩展。
因为测试的机器是机械硬盘,所以效果很明显,如果是固态硬盘,那么在mysql能触发索引的情况下,两者的差距有所缩小。
如果将文件内容创建好物化视图,那么查询速度能更快一个量级。
例如:
-- 创建物化视图
create table view_stock_daily as select * from 'e:/data/data_set/stock/stock_daily.csv';
-- 查询物化视图
select * from view_stock_daily
当然,本文对比的是DuckDB和MySQL数据查询能力,正所谓术业有专攻,MySQL的OLTP能力一直是开源界的TOP队列里的。
DuckDB和MySQL比较,有点像战斗机和客机比较飞行速度。数据是真实的但也是局部的。
本文的对比逻辑仅仅是文章介绍DuckDB能力的一个抓点。让读者能在一个标准认知的场景里清晰地理解DuckDB的能力。