数据归档和在线查询的冲突,核心解法在分层
交易系统的数据归档与在线查询想同时做好,关键是把存储分成热、温、冷三层,让数据按访问频率自动流转,而非把所有历史数据都堆在同一张表里硬扛。
很多团队遇到的问题是:行情数据、订单流水越攒越多,在线查询越来越慢,但业务方又要求保留全部历史数据随时可查,如果只做归档,历史查询变慢;如果只做优化,在线性能被拖垮,这本质上是存储成本、查询速度和数据完整性三者之间的博弈。
行情数据归档与查询性能优化,先从存储分层开始
业内专家指出,交易系统的数据访问呈现出明显的”温度分布”,最近一周的数据被高频访问,最近三个月的数据偶尔被查询,超过一年的数据几乎无人问津,只有在审计、对账或策略回测时才会被捞出来。
针对这种分布,合理的设计是:
- 热数据:保留最近7-15天,存放在SSD或内存数据库中,响应时间控制在毫秒级
- 温数据:保留最近1-3个月,存放在普通SATA盘或云盘,响应时间可接受秒级
- 冷数据:超过3个月甚至1年的数据,归档到对象存储或廉价存储,查询时间允许10秒以上甚至分钟级
判断冷热数据的标准
不要凭感觉分类,用实际访问日志说话,统计每个表、每个分区在过去30天的查询次数和平均查询时间,按访问频率排序,前20%的数据是热数据,后50%以上基本算冷数据,这个比例会随业务变化,建议每个季度重新统计一次。
交易系统数据归档方案怎么选?先看成本和访问频率
归档不是简单地把旧数据删掉或者备份到磁带,而是要让归档后的数据仍然”可查”,只是允许更长的响应时间,目前主流的方案有几种,各有适用场景。
| 方案 | 存储介质 | 查询速度 | 成本 | 适用场景 |
|---|---|---|---|---|
| 分区表+DETACH | 同一台机器不同磁盘 | 快 | 低 | 单机数据库,数据量千万级 |
| 归档到对象存储 | 云OSS/S3 | 慢(需下载后导入) | 极低 | 超大数据量,低频查询 |
| 冷热双集群 | 两套数据库实例 | 中 | 中 | 需要跨冷热数据联合查询 |
| 数据湖(Iceberg/Hudi) | 分布式文件系统 | 中 | 中 | 需要跑分析类查询的场景 |
对于大多数中小型交易系统,分区表冷热分离是最容易落地且见效最快的方案,它不需要引入新的技术栈,不需要改业务代码,只需要在建表时设计好分区键,然后写个定时任务把过期分区迁移到冷存储。
在线交易历史数据查询优化,先解决索引失效问题
很多系统查询慢不是因为数据量太大,而是索引设计不合理,交易数据最典型的查询模式有两种:按时间范围查一笔订单,或者按交易账号查全部历史记录。
一个常见的坑是:把时间字段和账号字段分开建单列索引,但查询条件里同时带了两个字段,导致数据库只能用到其中一个索引。
正确的做法是建立复合索引,把查询频率最高的字段放在前面,如果业务方最常按”账号+时间”查询,就应该建立(account_id, trade_time)的联合索引,而不是单独建两个索引。
一个实操案例:订单表从10秒到200毫秒
某团队维护的订单表有8000万行数据,归档前查询一笔三个月前的订单需要10秒以上,排查后发现,查询语句走了全表扫描,优化步骤是:
- 按
trade_time做RANGE分区,每月一个分区 - 建立
(account_id, trade_time)复合索引 - 将半年前的旧分区
DETACH到冷存储目录,但仍挂在原表下 - 查询时数据库自动路由到正确分区,不再扫全表
优化后,在线查询平均耗时200毫秒,历史数据查询虽然在冷盘上但也能在3秒内返回,整个改造只花了一个下午。
历史数据查询越来越慢,问题出在归档粒度太粗
很多系统的归档策略是”半年后整体迁移”,这种粗粒度方式会导致两个问题:一是迁移瞬间IO峰值过高,影响在线业务;二是如果业务方突然需要查某一天的旧数据,还是得从整个归档文件中捞。
更合理的做法是按”天”或者”周”为粒度分批归档,并且归档过程中保持数据可查询。
用分区继承实现平滑归档
以PostgreSQL为例,可以用分区表加继承的方式实现平滑归档:
- 在线表保留最近90天的分区
- 每过一天,创建新的当天分区,同时把91天前的分区
DETACH并移动到归档存储 - 归档存储使用相同的表结构,通过视图将在线表和归档表union起来,对外暴露统一的查询接口
这样做的核心优势在于,归档动作是连续的、低负载的,不会出现某个凌晨突然拷贝几百GB数据的极端情况,业务方通过同一个视图查询所有数据,无需关心数据具体在哪个存储上,而查询优化器会根据查询条件自动跳过无关分区。
查询网关:让在线和归档数据”无缝”拼接
如果在线库和归档库用的是完全不同的存储引擎,例如在线用MySQL、归档用ClickHouse,就需要在中间加一层查询代理,查询代理负责:
- 解析SQL,判断查询范围是否涉及归档数据
- 如果只查近期数据,直接走MySQL,不碰归档
- 如果查询范围跨越冷热边界,分别查询MySQL和ClickHouse,再在内存中合并结果
这个代理不能有状态,最好是无状态的API服务,这样便于水平扩展,实际项目中可以用ShardingSphere或自研的中间层实现,行业共识认为,查询网关的引入会让系统架构变复杂,但它是兼顾冷热查询的兜底方案。
交易系统数据归档时,别忘了校验数据完整性
归档过程中最怕的是数据丢失,文件拷贝过去后发现少了几个文件,或者某几行数据损坏了,审计时候才发现问题,那就麻烦了。
归档前后的校验步骤
每次归档任务执行完成后,按以下步骤做校验:
- 行数对比:归档前后分别
COUNT(),行数不一致立即告警 - 校验和对比:对归档文件做MD5或CRC32校验,确保文件未损坏
- 抽样回查:随机取归档库中0.1%的数据与在线库备份对比字段值,发现差异重跑该批任务
- 双写机制:归档写入完成前,不删除在线库的分区,等校验通过后再物理删除
这套流程需要固化到自动化脚本里,即使是日常归档任务,也不建议跳过校验,因为磁盘静默损坏的概率虽然低,但一旦发生就是重大事故。
归档数据的恢复演练要定期做
不要等审计要求时才去验证归档数据能不能查,每季度抽一个冷分区做恢复测试,把归档存储中的数据拉回到临时实例,运行几条典型查询语句,确认能返回正确结果,整个过程文档化,作为运维审计记录的一部分,这样即使存储供应商出了问题(如云服务商的对象存储性能波动),也能发现备份数据是否可用。
冷数据查询慢,怎么满足业务方的”在线查”需求
业务方的诉求很直接:历史数据我也要在页面上查,不想等太久,但实际上,很多查询场景是可以接受”非实时”的,关键在于引导预期。
主动把查询分类:实时查询和异步查询
- 实时查询:只查热数据,响应时间要求在1秒内,用于日常交易复核
- 异步查询:查询范围跨越冷数据,提交任务后轮询结果,响应时间允许10秒到几分钟,用于审计、对账、策略回测
在界面上明确区分这两种模式,异步查询任务走消息队列,由后台worker去处理,避免大查询把数据库连接池耗尽。
预计算聚合表解决”统计报表查得慢”
很多历史查询其实不是查明细,而是查统计结果,上个月每天的交易量是多少”,这类查询即使数据在热库里也需要全表扫描,非常重。
解决思路是:归档时顺便生成按天聚合的预计算表,归档任务每天跑完后,在目标库中更新当天的daily_summary表,包含交易量、交易笔数、盈亏分布等维度的汇总,业务方查询统计报表时直接读预计算表,速度从分钟级降到秒级。
Q&A:交易系统数据归档与查询的常见困惑
线上交易系统的数据保留多久比较合适?
取决于监管要求和业务需求,国内证券期货类系统的订单和成交记录一般要求保存不少于20年,但并不是说所有数据都要放在在线库里,合理做法是:在线库保留3-6个月的活跃数据,其余数据归档到冷存储,但保证在审计时需要时能够恢复查询。
归档后,历史数据查询结果的正确性如何保证?
通过三层机制保证,第一层是归档前的行数和校验和对比,确保数据完整迁移;第二层是归档后的抽样回查,随机抽取归档数据与源数据比对;第三层是定期恢复演练,模拟真实查询场景验证数据可读性,三层都通过,才允许物理删除在线库中的原始分区。
数据量增长太快,归档任务跟不上怎么办?
把归档任务的调度频率从每日一次调整为按小时增量执行,每个小时只归档当前时间往前推N天的数据,并设置独立的归档队列和资源限制,避免归档任务与在线业务的IO竞争,如果单机归档能力仍然不足,将归档目标改为分布式对象存储,利用并行上传提升吞吐量。
归根结底,交易系统的数据管理没有”银弹”,数据归档和在线查询的性能平衡核心是让数据按访问规律流动,该快的快,该省的省,设计分层存储结构,建立清晰的归档与校验流程,再给不同的查询场景匹配不同的响应路径,这套组合下来,数据量的增长就不会再成为业务发展的瓶颈。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/758649.html





