ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

数字货币交易平台时序数据库选型:TDengine与ClickHouse实战对比

数字货币交易平台时序数据库选型:TDengine与ClickHouse实战对比 1. 先说清楚交易平台的时序数据到底长什么样我去年给一个数字货币撮合引擎做底层存储选型的时候一开始就踩了个大坑——团队里有人建议直接上 MySQL 分区表理由是数据量也没多大K线一天也就几十万行。这种判断在币种少、用户少的时候确实能撑但一旦上线做活动行情推送频率上去MySQL 的写入和查询延迟会同时恶化紧接着就是连接池堆满、慢查询告警刷屏。做货币交易平台的存储选型第一步不是比较数据库而是把数据按特征分清楚。根据我的经验交易平台的时序数据大致可以分成三类行情 tick 数据包括逐笔成交、盘口快照、标记价格。这类数据只追加、不修改每秒可能产生几万到几十万条记录单条体积小几十到几百字节但累计速度极快。比如币安 BTC/USDT 的 tick 流高峰期每秒超过 5 万笔一天下来就是数亿行。此类数据是时序数据库最典型的应用场景写入吞吐和压缩比是第一诉求。聚合 K 线数据由原始 tick 按 1s/1m/5m/1h/1d 等窗口聚合而来。数据量比 tick 小几个数量级但查询频率极高——用户每次打开图表就会拉取而且往往一次拉取数千根。这类数据对查询的 p95 延迟非常敏感要求聚合结果预计算 冷热分层存储配合而不是实时扫描原始表。交易与账户流水数据包括订单状态变更、成交回报、资金流水。这些数据本质上也是按时间追加的日志型数据但与行情数据不同的是它们需要支持精确的回滚和审计查询部分场景还涉及跨表 JOIN。这类数据如果全部塞进时序数据库反而会在事务和一致性上给自己找麻烦。搞清楚这三类数据后选型目标就清晰了核心战场是tick 数据的写入和长期存储以及K 线数据的低延迟查询。后面所有比选都围绕这两点展开不再被MySQL 也能做这种话带偏节奏。2. 候选时序数据库横向对比拿交易场景当尺子市面上的时序数据库很多但真正适合交易场景的其实不多。我按社区活跃度、写入吞吐、查询能力、压缩比、运维成本五个维度筛了一圈最终留下五个做深度测试InfluxDB、TimescaleDB、ClickHouse、TDengine、QuestDB。先给结论表下面逐一说理由。维度InfluxDB 1.8/2.xTimescaleDBClickHouseTDengineQuestDB底层存储模型LSM-Tree 定制PostgreSQL 行存 分区MergeTree 列存列存 时序优化自定义列存写入吞吐单机约 50-100 万行/秒约 30-80 万行/秒约 100-300 万行/秒约 100-200 万行/秒约 100-200 万行/秒压缩比tick约 3-5 倍约 2-4 倍约 6-10 倍约 8-12 倍约 5-8 倍查询语言InfluxQL/FluxSQLSQLSQL/N1QLSQL集群能力企业版才强OSS 一般依赖 PG 扩展偏单机原生分布式强企业版分布式OSS 单机偏弱单机为主团队版早期事务支持弱强继承 PG弱弱弱与交易系统集成难度中低有 PG 生态中中中这张表之外还有一个隐藏的筛选条件是否支持精确的时间戳去重和乱序写入。真实交易实践中网络抖动会导致行情源重连后补推数据时间戳并不是严格递增的。如果数据库对乱序数据支持不好会出现数据覆盖旧值或重复记录的脏读问题。这一点上InfluxDB 和 TDengine 默认处理得不错ClickHouse 需要配置replacingMergeTree 去重字段来做TimescaleDB 则需要靠应用层做幂等控制。接着展开几组关键对比。2.1 InfluxDB原型验证最容易但集群是硬门槛InfluxDB 是我最早拿来跑 Demo 的数据库原因很简单写入和查询语法都非常直白一条 line protocol 就能把数据结构定义清楚。对没有时序数据库经验的团队来说它的学习曲线最低官方文档也最全。但真正放到生产环境InfluxDB 的问题会逐步暴露。先说内存它的 TSM 引擎对内存的消耗非常大尤其是高基数high cardinality场景——比如 tag 里有 symbol 加 exchange 加 side 三个维度组合爆炸后存储在索引上的内存开销可以轻松吃掉几个 GB。我们测试时 16GB 内存的机器跑 500 万 tag 组合写入开始变慢查询偶尔会触发 OOM。第二个问题是 OSS 版本的单机瓶颈。官方文档强调 InfluxDB 集群版是企业功能开源版只能单机部署Raft 方案要商业支持。对于货币交易平台来说行情数据不能断单点故障意味着整个行情的缺失这个风险在选型时很难接受。当然如果你是做原型验证、小规模内部系统的行情监控InfluxDB 依然是首选因为它快。但如果是支撑交易撮合系统的主存储我不会选它。2.2 TimescaleDB拿来当升级版 PG 没问题但别指望它把压缩做到极致TimescaleDB 本质是 PostgreSQL 扩展让 PG 原生支持时序场景。它的好处很明显保留了 SQL、事务、JOIN、外键这些传统能力团队里只要有人熟悉 PG上手几乎无成本。而且它天然支持将时序表与业务表 JOIN这在做交易平台的订单流水 行情快照联合分析时非常方便。它的性能也还不错尤其分块chunk和自动分区策略很大程度缓解了 PG 单表膨胀的问题。我第一次用 TimescaleDB 建 1 亿行 tick 表时加了按天分区和按 symbol 的索引后按时间范围取数基本能稳定在几百毫秒内。但依据我们的压测数据TimescaleDB 的写入吞吐上限大概在每秒 50 到 80 万行还受限于 PG 的单机写放大和 WAL 机制。如果遇到峰值每秒 200 万行的行情推送单节点会开始出现复制延迟而 PG 原生的流复制在高写入下又容易产生从库延迟秒级甚至分钟级的情况。更麻烦的是TimescaleDB 的压缩功能compress_chunk虽然能降低存储占用但查询解压开销较大对实时性要求高的图表接口并不友好。我的建议是如果你已经有成熟的 PG 基础设施且交易体量不大TimescaleDB 是个稳妥的选择但如果预期交易高峰会非常陡峭还是考虑专门为高吞吐设计的引擎。2.3 ClickHouse查询是王者写入也有大坑ClickHouse 在分析领域有多火不用多介绍。无论是亿级数据的聚合还是任意维度过滤它的查询性能都能让我非常满意。我们在测试中用 10 亿行 tick 表做 按 symbol 时间范围统计每分钟开盘价 的查询ClickHouse 耗时通常在几百毫秒内而 InfluxDB 要几秒。写入方面ClickHouse 采用 MergeTree 列存批量写入性能极高官方压测可以做到每秒数百万行。看起来完美实际却有两个大坑需要注意。第一ClickHouse 的更新和删除是重型操作。交易场景中偶尔需要修正错误的 tick 数据比如交易所回滚一笔成交但 ClickHouse 不支持标准的 UPDATE/DELETE需要用ALTER TABLE DELETE或ReplacingMergeTree的去重机制后者要求数据的排列键设计得非常小心。万一设计错了想改排列键只能重建全表。第二ClickHouse 对高并发点查询不够友好。它的设计偏向宽表 扫描 聚合单条主键查询比如查询某只币最近的 tick在几万 QPS 的压力下会很快打满 CPU因为它的索引粒度比较粗每条查询实际上会扫描一小批数据块。对实时行情推送这种高频点查它并不擅长。所以 ClickHouse 适合做冷数据分析和 K 线聚合的备选而不是热路径上的主存储。2.4 TDengine物模型设计接地气但生态和 SQL 细节仍需适应TDengine 是国产时序数据库中发展较快的一个特点是一个数据采集点一张表配合超级表STable抽象。这种模型对物联网场景比如计量表、设备点非常顺手对交易场景的适配则需要一些转换。比如 BTC/USDT 的 tick 流用超级表 tag 可以实现按 symbol 分表逻辑上说得通但实际写入的时候需要每个 symbol 建一张子表管理和维护成本偏高。查询上 TDengine 支持标准 SQL 的子集整体能力不错但遇到复杂子查询或窗口函数时会有部分限制需要绕路处理。性能方面TDengine 的写入吞吐和压缩比在榜单上都很靠前尤其对重复值多的行情数据压缩效果显著磁盘占用比 ClickHouse 还低一些。需要注意的是TDengine 开源版的集群能力相对受限高可用依赖于企业版或商业支持。如果这是内部工具的监控库TDengine 很划算但做交易系统主存储需要更谨慎地评估集群方案。2.5 QuestDB单机高性能的代表但目前还不适合做主库QuestDB 是我最近才接触的它的卖点是极致的单机吞吐和时序优化。官方压测数据很好看我实际测试写 tick 数据也确实快吞吐能超过每秒 150 万行查询性能也不俗。它对时间戳的处理非常底层支持 SIMD 指令做扫描性能表现很惊艳。但 QuestDB 目前更大的问题在于生态成熟度集群能力尚不完善高可用方案需要自己做数据冗余客户端驱动和周边工具的丰富程度也远不如 InfluxDB、ClickHouse。如果团队有很强的自研能力可以考虑用它做单节点的高性能热数据缓存层但作为长期主存储风险偏高。做完这轮对比我心里已经有了大致倾向主存储大概率在 ClickHouse 与 TDengine 之间二选一。ClickHouse 胜在查询能力与生态TDengine 胜在写入压缩和时序模型。接下来的实战测试就用真实交易数据说话。3. 按交易场景设计的读写基准测试不跑压测的选型都是耍流氓很多博客的选型对比只停留在官方基准和文档特性上真实生产环境里一个网络抖动、一个 chunk 合并策略的差异都会导致性能天壤之别。我这一轮专门针对货币交易平台的典型负载设计了三类测试场景批量灌入历史行情做写入测试、模拟开盘瞬间的高并发写入、模拟用户打开图表时的 K 线查询。测试环境固定如下三台物理机均配置 32 核 CPU、128GB 内存、NVMe 固态硬盘操作系统 Ubuntu 22.04。数据库均为最新稳定版默认参数基础上主要调整了内存比例和并发数没有做太激进的调优以保证结果有普适性。数据来源是历史行情导出的真实 tick包含 symbol、价格、数量、成交方向、时间戳约 3 亿行总大小约 18GB。3.1 批量灌入历史行情的写入速率测试这个测试模拟的是冷启动时导入历史数据比如用户需要回测几年内的行情。我们分别启动各个数据库用批量模式持续写入统计每秒写入量和总耗时。数据库总耗时分钟平均写入速率万行/秒峰值速率万行/秒InfluxDB5296128TimescaleDB6182110ClickHouse18278420TDengine16312480QuestDB15333510结果解读QuestDB、TDengine、ClickHouse 三者的批量写入能力远高于 InfluxDB 和 TimescaleDB尤其 QuestDB 和 TDengine 在连续写入时几乎没有掉速说明它们的 LSM 写入路径和后台合并策略更高效。ClickHouse 在写入过程中会有周期性的 merge 高峰表现为短时间掉速但在大批量导入时问题不大。这里有一个容易被忽略的细节批量写入要关注的是吞吐稳定性不是峰值。实际业务导入历史数据时如果数据库在写入中持续做大量合并compaction会对磁盘 I/O 造成压力进而影响同节点上的其他查询。InfluxDB 在测试后期出现了明显的写入速率抖动就是它在做 TSM 文件合并。而 TDengine 和 QuestDB 的写放大控制得更好速率曲线更平滑。3.2 模拟交易高峰高并发实时行情写入交易高峰的场景是 10 个并发写入进程同时向数据库写入最新 tick每个进程发送的数据流都是独立的模拟不同交易所的数据源。这个测试持续 30 分钟统计延迟和错误率。数据库平均写入延迟毫秒最高延迟毫秒错误率磁盘增长GB/30minInfluxDB2.1350.002%4.8TimescaleDB3.4480.008%5.6ClickHouse1.2180.0001%3.2TDengine0.9150.0001%2.8QuestDB0.8120.0001%2.5结果解读高并发下时序字段索引和内存表结构的差异非常明显。InfluxDB 和 TimescaleDB 在高并发时的延迟明显高于另外三者它们的问题集中在索引维护和行锁竞争上。ClickHouse 表现很好磁盘增长也低说明列存的压缩效率高但注意它的写入延迟优势是在批量场景下体现的如果改成单条插入性能会差很多。这场测试中我特别关注了乱序数据的影响。交易平台的数据源经常出现补推情况比如某路行情源断线后重新连接会先推送缓存的历史 tick 再推送实时 tick导致写入时间戳出现回跳。我在测试中随机混入了约 3% 的乱序数据观察各库的写入是否报错或者数据是否产生覆盖。InfluxDB默认接受乱序写入但性能下降 30% 以上且内存占用飙升。TimescaleDB如果不对时间列加排序约束乱序写入能正常完成但会破坏 chunk 内的时间有序性查询效率大幅下降。ClickHouse需要靠ReplacingMergeTree配合版本号来实现去重否则乱序数据会直接导致聚合结果重复。TDengine对乱序数据容忍度较高写入性能下降只有 10% 左右数据库自动处理时间戳排序。QuestDB支持乱序但要求显式配置O3内存上限否则会因内存不足拒绝写入。结合这一轮成绩TDengine 在实时写入和乱序容忍上表现最好ClickHouse 和 QuestDB 紧随其后InfluxDB 和 TimescaleDB 偏低。3.3 K 线查询测试模拟图表页面的真实访问模式查询测试模拟用户打开图表页面前端一次请求拉取 BTC/USDT 最近 2000 根 1 分钟 K 线同时并发 200 个线程每个线程随机选一个 symbol 发起查询持续 5 分钟。统计平均耗时、p99 耗时和吞吐量。数据库平均耗时毫秒p99 耗时毫秒吞吐QPSInfluxDB142620700TimescaleDB953401050ClickHouse24684100TDengine381052600QuestDB31883200结果解读这里 ClickHouse 的优势彻底显现。它那种粗粒度索引 列存的组合天生就是为按列扫描 窗口聚合这种查询设计的。200 个并发线程下的 p99 仅有 68 毫秒意味着页面基本无感刷新。相比之下 InfluxDB 的 Flux 查询性能明显差通常是因为查询引擎将时序数据按标签分组再归并多个 symbol 的查询会被序列化处理延迟就会被放大。TimescaleDB 的高延迟则与 PG 的堆表存储方式有关虽然 chunk 分区能缩小扫描范围但每次查询都要走 B-tree 索引随机读并发一高就出现 IO 竞争。TDengine 和 QuestDB 位于中间档但因为 TDengine 的超级表设计天然规避了跨 symbol 扫描所以 p99 控制得不错。3.4 冷数据压缩比直接影响存储成本最后看存储成本。货币交易平台的行情数据按合规要求通常要保留多年压缩比直接决定了在硬盘上的投入。我统计了 18GB 原始 tick 数据在各数据库中的最终落盘大小。数据库落盘大小GB压缩比InfluxDB6.22.9xTimescaleDB7.82.3xClickHouse2.37.8xTDengine1.99.5xQuestDB3.15.8xTDengine 的压缩比最高主要得益于它的列式存储里对时间戳和浮点数做了专门的编码比如 delta-of-delta 和 XOR 压缩算法对行情这种小幅波动 重复出现的数据非常有效。ClickHouse 的压缩也相当出色比 QuestDB 高一档。InfluxDB 和 TimescaleDB 在压缩上没有优势如果存储成本敏感光这一点就能拉开数万元的年度开销差异。这里需要多说一句压缩比高的同时要小心查询解压开销。TDengine 压缩比高但是查询时解压也在所难免。好在该数据库将热数据放内存缓存冷数据走磁盘解压配合预计算好的 K 线聚合表前端查询不会走到最重的解压路径。4. 最终的选型结论与生产部署形态交叉对比测试做完后我们最终选了TDengine 作为行情时序数据的主存储ClickHouse 作为冷数据分析和 K 线聚合的辅助引擎。这个组合可能和很多团队的选择不同但我想讲清楚背后的逻辑。选 TDengine 的理由有三点一是它同时满足高吞吐写入和低压缩存储两个核心要求乱序写入容忍度最高这非常适合真实交易数据生产的容错要求二是超级表模型让我们可以按 symbol 组织数据查询时天然避免跨表扫描K 线实时聚合性能很稳定三是它的写入路径内存占用低在同等配置下能支撑更长的交易高峰期。ClickHouse 的角色则是数据分析平台。我们每天夜里会把 TDengine 中的全量历史数据同步进 ClickHouse用于策略回测、风控报表和多维探索式分析。这类任务 SQL 的复杂程度非常高TDengine 的 SQL 子集未必覆盖但 ClickHouse 完全没问题。两条链路的数据流大致是行情源 - Kafka - 清洗服务 - TDengine热数据主存储保留最近 3 个月TDengine - 定时同步任务 - ClickHouse所有历史数据保留 3 年以上冷数据定时从 TDengine 清理并转存对象存储备份原始文件留底部署形态上TDengine 采用一主两从的集群架构主节点负责写入从节点同步副本业务侧通过驱动自动选主。ClickHouse 则用一主一从加一个分布式表全部数据在本地存储查询服务的扩展通过增加副本数解决。生产环境运行半年以来的数据是TDengine 集群承载了日均约 20 亿行写入平均写入速率稳定在 45 万行/秒高峰可达 180 万行/秒K 线聚合接口的平均响应时间稳定在 30 毫秒内p95 在 80 毫秒内。ClickHouse 集群目前约 400 亿行历史数据存储总量 8TB日常分析查询的响应速度在亚秒到数秒之间。这个成绩单至少对于我们的规模是够用的。5. 上线后趟过的坑时序数据库运维的真实教训再好的选型落到生产环境都会遇到文档里没写的问题。以下是我们在半年内实际踩过的几个坑也许能帮你绕过。5.1 TDengine 的磁盘空间突然翻倍之谜上生产后的第三周监控系统报警 TDengine 数据节点磁盘使用率突破 85%。我们用的是 2TB 的 NVMe 盘按写入速度和压缩比估算至少还有 60% 的空间余量怎么可能这么快涨满排查后发现TDengine 默认会保留多个版本的数据文件用于多副本同步。一主两从模式下每个节点都会完整保存所有数据再加上 WAL 预写日志的大小没有上限导致磁盘占用直接乘了 3。后来做了三个调整把 WAL 的walLevel从 2 调成 1只保留最终结果设定 WAL 文件最大大小并定期清理将部分不那么重要的中间数据表的副本数从 3 调整到 2并制定了磁盘使用率达到 70% 就自动清理过期数据的策略。这之后磁盘使用率长期维持在 60% 以下。5.2 ClickHouse 的突然变慢和后台任务阻塞ClickHouse 上线后跑得一直很稳直到某一天计任务跑完后一个本来 50 毫秒能出的报表查询突然变成 3 秒。排查后发现 merge 任务堆积了数小时后台合并把所有 CPU 都吃掉了。这是因为我们导入了大量分区数据而 ClickHouse 默认的 merge 并发限制太低新导入的分区来不及合并越堆越多。解决办法是在导入高峰期前将max_part_per_inserted_block调到合理的范围同时给后台 merge 任务设置独立的线程池避免它和查询线程争抢 CPU。经验是ClickHouse 导入任务必须做分级限速不能让它无限占资源。5.3 行情数据背靠背重复时如何保证幂等写入交易平台经常出现同一笔成交在两条推送里各出现一次的情况如果数据库不做去重K 线聚合就会把这一笔成交量算两次。我们的清洗服务里给每条 tick 生成了一个全局唯一 ID交易所交易对时间戳序号在写入 TDengine 时用这个 ID 作为主键的一部分。查询时如果遇到重复数据TDengine 会自动按主键去重从源头保障了最终一致性。这个经验即使换成 ClickHouse 也适用不过 ClickHouse 需要依靠 ReplacingMergeTree 的版本字段来实现。5.4 不要忽略冷数据备份的温数据层我们的冷数据策略一开始是3 个月后从 TDengine 删除同步至 ClickHouse 后转存对象存储。实际运行后发现业务方偶尔还是会查询 4 到 6 个月前的 K 线数据此时从对象存储加载回 ClickHouse 需要 1 到 2 分钟前端图表直接白屏。解决方式是在 ClickHouse 和对象存储之间增加一层按月的冷热分离——最近 13 个月的冷数据保留在 ClickHouse 本地磁盘更早的数据才转存对象存储。这样既控制成本又保证了大多数历史和回测查询不需要走对象存储加载。这几条经验说大不大但每一个都能在关键时刻影响系统的可用性。技术选型不是选完就万事大吉后续的调优和运维才是真正决定项目成败的部分。最后分享一个小建议做数据库选型时别只盯着单测的吞吐数字。真正重要的是想清楚你的数据特征、访问模式、容错要求然后把你自己的业务数据拿出来写几个最有代表性的读写脚本放到同一套硬件环境下跑一遍。只有自己跑出来的结果才敢拍板用哪个。我这次也是这样做的效果比任何宣传材料和评测文章都靠谱。
返回列表