文件缓存工作原理:缓存切片、多队列、淘汰与预热机制详解
基本原理
缓存切片与预读机制
Doris 将目标文件按 1 MB 对齐进行切片,每片数据在完整下载后以独立的 Block 文件形式存储于本地文件系统。这种细粒度切片可避免缓存整个大文件造成空间浪费,同时使热点数据的命中与淘汰更加精准。
本地文件目录组织
缓存可分布在多块磁盘的多个目录中。Doris 根据目标文件路径计算哈希值,将该哈希值作为 Block 文件存放路径的最后一级目录;目录内每个 Block 文件以数据在目标文件中的偏移量命名,实现数据在多目录间的均匀分布。
示例: 目标文件路径为 /remote/data/datafile1,计算哈希值为 12345,则对应 Block 文件路径为:
/cache/123/12345/<offset>
其中 <offset> 表示该 Block 数据在原文件中的偏移位置。
多队列机制
Doris 将缓存数据按类型分入不同队列,避免缓存污染并提高命中率。各队列按优先级从高到低排列:
| 队列 | 存储内容 | 优先级 | 说明 |
|---|---|---|---|
| TTL 队列 | 设置了 TTL 属性的数据 | 最高 | TTL 期间内不被淘汰;TTL 为表级属性,例如设为 3600 表示导入完成后 1 小时内数据尽量保留在缓存中。适用于需要本地持久化的小规模常驻表。 |
| Index 队列 | 索引数据(不含反向索引) | 高 | 用于加速查询过滤;反向索引因数据量大,归入 Normal 队列处理。 |
| Normal 队列 | 普通数据(无 TTL 属性) | 中 | 大多数数据所在队列。 |
| Disposable 队列 | 临时数据(如 Compaction 读取的数据) | 最低 | 使用完毕后优先被淘汰。 |
淘汰机制
淘汰触发时机
| 触发方式 | 说明 |
|---|---|
| 空间不足被动淘汰 | 本地磁盘空间或 inode 不足时触发;或缓存用量达到预设容量上限时触发。 |
| 高水位主动淘汰 | 缓存用量达到高水位线时,Doris 提前异步清理旧缓存,避免同步淘汰阻塞写入。 |
| 垃圾回收淘汰 | 主动清理以下垃圾数据:Compaction 与 Schema Change 的原始数据、导入 commit 失败回滚的数据、DROP TABLE/PARTITION 后的残留数据。 |
| TTL 到期降级 | TTL 数据到期后降级进入 Normal 队列,参与正常的 LRU 淘汰流程。 |
淘汰目标选择策略
空间分配比例: 多个队列共享磁盘空间,各队列有独立的占比上限(例如 Normal 队列上限为总空间的 40%)。当其他队列未占满分配比例时,队列可使用所有剩余空间;随着各类型数据增加,占比逐渐趋近预设值。
淘汰顺序: 当某队列写入空间不足时,Doris 按以下顺序淘汰超出比例部分的数据(各队列内部按 LRU 选择淘汰目标):
Disposable → Normal → Index → TTL
若按序淘汰其他队列后仍空间不足,则触发自身队列的 LRU 淘汰。
避免热数据被淘汰的建议
- 预留足够磁盘空间: 由于缓存清理有一定滞后性,需保留余量。根据经验,文件缓存空间约为查询热数据量的 1.5 倍可保证较高命中率。
- 大查询隔离: 将大查询路由到独立集群,避免大查询占用缓存影响其他查询的命中率。
预热机制
缓存预热是指将数据提前加载到缓存,使后续查询直接命中本地,提升查询性能。Doris 提供以下三种预热方式:
手动预热
用户可对指定表、分区或参考另一集群的缓存内容发起预热。数据源始终为远程存储(而非其他 BE 节点)。预热流程如下:
- 用户执行预热指令,系统将目标转换为 tablet 集合,分发至对应 BE。
- BE 对 tablet 的所有数据文件执行顺序读,将数据写入本地文件缓存。
- 任务以最大 20 GB 为粒度分批执行,每批完成后保存存档点,支持中断恢复。
- 若 BE 宕机或用户手动取消,所有 BE 停止下载并结束本次预热。
用户可通过 SHOW WARM UP JOB 查看任务状态(FINISHED / CANCELLED / RUNNING)及整体进度。对相同表和分区重复预热时,Doris 会自动识别已有数据,仅增量更新,不重复下载。
数据均衡触发的预热
当 tablet 因负载均衡(扩缩容或节点宕机)迁移到新 BE 时,新 BE 会向旧 BE 发起 RPC 拉取缓存元信息,并基于元信息重新下载数据到本地文件缓存。旧节点对应的缓存数据在 tablet 信息清理时同步主动淘汰,释放空间。
注意: 迁移完成到缓存下载就绪之间存在时间窗口,此期间可能出现文件缓存未命中。
计算集群间自动预热(3.1+ 版本)
在存算分离场景下,多个计算集群的文件缓存可自动同步。Doris 提供两种同步方式:
| 方式 | 适用场景 | 说明 |
|---|---|---|
| 周期预热 | 对数据实时性要求不高 | 在 WARM UP SQL 中指定同步周期,任务周期性地将指定表和分区的数据增量同步到目标集群。 |
| 导入/Compaction 触发预热 | 对实时性要求高 | 导入进入 commit 阶段时,源集群 BE 通知目标集群对应 BE 下载刚上传的数据;Compaction 完成时同样触发类似通知路径。FE 负责维护目标集群的 tablet 分布信息。 |
场景分析
查询场景
查询时文件缓存的处理流程如下:
- Scanner 读取请求: 查询到达后,Scanner 组件准备读取所需数据文件。
- 查询本地缓存: Scanner 优先检查本地文件缓存,根据文件路径和偏移信息匹配缓存元数据。
- 缓存命中: 找到对应缓存数据后,返回 BlockFile 文件句柄集合,Scanner 直接读取本地数据,无需访问远程存储。
- 缓存未命中: 对于未命中范围,Scanner 从远程存储下载数据,写入文件缓存后返回,同时按淘汰策略腾出空间。
导入场景
数据导入时文件缓存的处理流程如下:
- 上传远程存储: 导入数据首先写入远程存储。
- 异步写入本地缓存: Doris 同步将数据异步写入本地文件缓存,使导入完成后紧接的查询可直接命中缓存。
- 缓存队列分配: 根据数据类型和 TTL 属性,数据写入对应队列(TTL 队列、Index 队列或 Normal 队列)。
Compaction 场景
Doris 的 Compaction 分为两类:
- Cumulative Compaction: 负责增量数据间的合并。
- Base Compaction: 负责基线数据版本(以 0 为起始版本)与增量数据版本的合并。
两类 Compaction 在文件缓存上的处理策略不同:
| Compaction 类型 | 缓存写入策略 |
|---|---|
| Cumulative Compaction | 输出数据上传远程存储的同时写入文件缓存,与导入流程一致,加速后续查询。 |
| Base Compaction | 默认仅在缓存空间充足时写入,避免大量冷数据造成缓存污染。可通过 BE 参数 enable_file_cache_keep_base_compaction_output = true 强制写入,但可能导致其他热数据被淘汰。 |
计划: Doris 后续版本将提供基于历史查询统计信息的自适应写入策略。
重启后缓存加载
3.1 版本之前,重启后 LRU 队列顺序无法恢复,导致热数据被淘汰,影响命中率。
3.1 版本引入 LRU 信息持久化,加载流程如下:
- 定期 dump: 将各 LRU 队列的顺序信息周期性持久化到磁盘。
- 重启加载: 节点重启时从磁盘读取 dump 数据,恢复缓存队列状态。
- 全盘扫描补全: 为修复 dump 时间窗口导致的元数据与磁盘文件不一致问题,重启后执行一次全盘扫描,确保数据完整性。
- 查询触发并发异步加载: 全盘扫描期间 BE 仍可提供查询服务;若查询数据尚未扫描到,系统提前加载该数据,减少查询延迟。
扩缩容场景
横向扩容
Doris 通过均衡操作将 tablet 迁移到新增 BE 节点。目标 BE 根据源 BE 缓存元信息重新下载数据到本地,保证新节点查询也能命中文件缓存。
横向缩容
tablet 均衡后,若集群整体文件缓存容量降至实际缓存数据量以下,将按淘汰机制自动淘汰超量数据。
纵向扩容
| 扩容方式 | 操作说明 |
|---|---|
| 增加单盘容量 | 执行以下命令通知 BE 空间变更:curl http://<BE_IP>:<WEB_PORT>/api/file_cache?op=reset&capacity=<新容量字节数> |
| 增加磁盘数量 | 不建议此方式。 Doris 目前未实现 rehash,不支持磁盘间均衡;缓存目录数量变动可能导致缓存查询故障。若确需增加磁盘,须清理缓存并按需重新预热。 |
纵向缩容
减少磁盘空间时同样需执行上述 reset 命令。缓存容量降至实际数据量以下时,将触发淘汰机制自动清理数据。
扩缩容后预热注意事项
横向扩缩容涉及 tablet 均衡操作,须等待迁移稳定后再执行预热,以确保预热效果。可通过监控 FE 的 doris_fe_tablet_num 指标判断迁移是否完成——曲线平稳无波动即表示迁移结束,可安全执行预热。
常见问题
Q:缓存命中率低,查询性能没有提升,怎么排查?
- 检查文件缓存磁盘剩余空间是否充足,建议缓存空间为热数据量的 1.5 倍以上。
- 确认是否存在大查询频繁淘汰热数据的情况,可考虑大查询隔离。
- 重启后短时间内命中率低属正常现象(LRU 恢复需要时间),可提前执行手动预热。
Q:扩容后查询缓存未命中,如何处理?
- 确认 tablet 均衡已完成(监控
doris_fe_tablet_num曲线平稳)。 - 执行手动预热命令,对目标表和分区进行预热。
- 横向扩容后新 BE 的数据下载需要一定时间,期间缓存未命中为预期行为。
Q:Base Compaction 是否会导致热数据被淘汰?
- 默认情况下,Base Compaction 仅在缓存空间充足时写入,不会主动淘汰热数据。
- 若开启
enable_file_cache_keep_base_compaction_output = true,Base Compaction 数据会强制写入缓存,可能导致热数据被淘汰,需根据实际场景权衡。
Q:TTL 到期后数据会立即被删除吗?
- 不会立即删除。TTL 到期后数据降级为 Normal 类型,进入 Normal 队列参与正常 LRU 淘汰,最终由淘汰机制决定何时清除。
Q:手动预热能否避免重复下载已有数据?
- 可以。Doris 在执行预热时会自动识别已缓存数据,仅对新增或变更的数据进行增量下载。