
TDengine 数据缓存机制全解析写缓存、读缓存、元数据缓存与文件系统缓存【免费下载链接】tdengineTDengine is an open source, high-performance, cloud native time-series database optimized for Internet of Things (IoT), Connected Cars, Industrial IoT and DevOps.项目地址: https://gitcode.com/taosdata/tdengineTDengine 为 IoT / IIoT 高并发场景设计了一整套分层缓存体系包括写缓存Write Cache、读缓存Read Cache、元数据缓存Metadata Cache与文件系统缓存File System Cache。本文基于 Data Caching 文档展开结合仓库中的参数定义、架构说明与源码实现如 tsdbCache.c、tsdbMemTable.c系统讲解每种缓存的工作原理、关键配置参数、适用场景与调优策略。读完本文你将能够根据业务特征读写比例、实时性要求、可靠性要求通过CREATE DATABASE/ALTER DATABASE精确配置VGROUPS、BUFFER、CACHEMODEL、CACHESIZE、PAGES、PAGESIZE、WAL_LEVEL与WAL_FSYNC_PERIOD在性能与成本之间找到最佳平衡点。缓存类型总览TDengine 的缓存机制并非单一组件而是由四类缓存协同构成覆盖从数据写入、实时读取到元数据访问与持久化保障的完整链路类型作用关键参数典型场景延伸阅读写缓存优先把新写入的数据放在内存缓存中达到阈值后批量落盘最早数据BUFFER、VGROUPS最近数据读写、写入吞吐BUFFER下文读缓存缓存每个子表的最新数据以加速当前值查询CACHEMODEL、CACHESIZELAST、LAST_ROWRead Cache下文元数据缓存缓存 vnode 之前访问过的元数据PAGES、PAGESIZE元数据访问PAGES下文文件系统缓存WAL 顺序追加依赖文件系统缓存fsync决定何时强制落盘WAL_LEVEL、WAL_FSYNC_PERIOD写性能与数据可靠性的权衡WAL_LEVEL下文下文分别深入剖析每一类缓存并在相应小节补充仓库内的参数定义与源码级实现依据。写缓存Write Cache时间驱动写驱动的缓存管理策略TDengine 采用一种创新的时间驱动缓存管理策略也称写驱动缓存管理机制。这与传统读驱动缓存模型截然不同其核心思路是优先把新写入的数据保存在缓存中当缓存容量达到预设阈值时系统将最早写入的数据批量落盘从而实现缓存与磁盘之间的动态平衡。在 IoT 数据应用中用户通常最关注最近生成的数据即设备的当前状态。TDengine 充分利用了这一业务特征——把最新到达的当前状态数据优先存放在缓存中使用户能够快速访问所需信息。从架构文档的表述看TDengine 直接将新到达的数据存入缓存以快速响应查询与分析需求从这一角度出发合理设置数据库参数后TDengine 本身就可以充当数据缓存层无需再部署 Redis 等额外缓存系统从而简化系统架构、降低维护成本。需要特别注意的是TDengine 重启后缓存数据会被清空全部批量落盘与专业 KV 缓存系统重启后自动回填缓存的行为不同。vnode / vgroup分布式缓存的基本单元为实现数据的分布式存储与高可用TDengine 引入了虚拟节点vnode概念。每个 vnode 最多可有 3 个副本多个副本共同构成一个 vnode 组vgroup。创建数据库时用户需要为每个 vnode 确定写缓存大小以保证数据分布的合理性与存储的高效性。从源码结构看每个 vnode 拥有独立的内存空间被划分为多个固定大小的内存块不同 vnode 之间的内存完全隔离相关实现在 tsdbMemTable.c 与 tsdb.h 中。写入过程采用类似日志的顺序追加方式每个 vnode 同时维护自己的 SkipList 结构用于快速检索。当超过 1/3 的内存块被写满时系统即启动数据落盘flush流程并将新的写操作引导至新的内存块——这样 vnode 中始终保留约 1/3 的内存块给最新数据既实现了缓存目的又保证了查询效率。关键参数VGROUPS 与 BUFFER创建数据库时的两个关键参数决定了数据库由多少 vgroup 承载数据、每个 vnode 分配多少写缓存VGROUPS数据库的初始 vgroup 数量见 VGROUPS。BUFFER单个 vnode 写入内存池的大小单位 MB默认 256最小值 3最大值 16384见 BUFFER。该参数正是架构文档中所描述的 vnode 内存空间大小配置入口。以下 SQL 创建一个包含 10 个 vgroup、每个 vnode 使用 256MB 内存的数据库CREATE DATABASE POWER VGROUPS 10 BUFFER 256 CACHEMODEL NONE PAGES 128 PAGESIZE 16;调优要点缓存并非越大越好。缓存越大虽然写入性能越好但超过一定阈值后继续增大缓存对写入性能的提升将不再明显。应根据数据量、机器内存与写入模型综合确定具体参数含义可参考 VGROUPS 与 BUFFER。读缓存Read Cache面向当前值查询的专用缓存读缓存机制专为高频率实时查询场景设计尤其适合需要实时掌握设备状态的 IoT / IIoT 业务——这类场景中用户最关心的往往是最新数据例如设备的当前读数或状态。读缓存将每个子表的最新数据缓存在内存中在缓存命中时LAST/LAST_ROW查询无需再从磁盘读取历史数据从而显著降低查询响应延迟并缓解存储系统 I/O 压力。CACHEMODEL四种缓存模式通过cachemodel参数用户可以灵活选择缓存模式。下表完整列出四种模式及其语义CACHEMODEL缓存内容主要加速对象none不缓存默认值—last_row每个子表最近一行数据LAST_ROWlast_value每列最近的非 NULL 值不受WHERE、ORDER BY、GROUP BY、INTERVAL等影响的LASTboth同时缓存最近一行与最近各列值上述条件下的LAST_ROW与LAST注意频繁切换CACHEMODEL可能导致LAST/LAST_ROW结果短暂不准确请谨慎操作建议保持开启状态来源CACHEMODEL 的注意事项。此外带过滤、排序、分组或窗口的LAST查询往往无法充分利用last_value缓存启用读缓存会在写路径上维护缓存可能影响写入性能高吞吐场景可将both调整为last_row或last_value参见 Ingesting Data Efficiently。CACHESIZE缓存容量CACHESIZE设置每个 vnode 用于缓存子表最新数据的内存大小默认 1范围 [1, 65536]单位 MB见 CACHESIZE。应按机器内存与表规模合理设置容量是否足够的判定方法可参考 Modify CACHESIZE。LRU 与懒加载机制架构文档Architecture: last/last_row Cache给出了读缓存的底层设计TDengine 为最新行 / 最新非 NULL 值提供LRU 缓存采用懒加载方式——对某张表的首次查询会从内存池和磁盘读取所需值存入 LRU 缓存并返回查询模块后续插入或删除会按需更新已有缓存条目未被缓存的表的写入不会强制将其加载进缓存。同时变更缓存配置会同步更新缓存数据启用缓存后首次查询触发加载禁用则释放已分配的缓存。针对单个子表的查询只加载该子表针对超级表的查询可能加载其全部子表。可用SHOW VGROUPS查看每个 vnode 的cacheload列以字节为单位观察缓存内存占用。CACHESHARDBITS并发访问的锁粒度对于高并发场景还可通过CACHESHARDBITS控制 last-value LRU 缓存的分片数内部锁粒度。默认 -1自动计算范围 [-1, 19]实际分片数等于2^CACHESHARDBITS。自动计算规则为每个分片至少 512KB理论最大分片数 CACHESIZE / 512KB分片位数 floor(log₂(理论最大分片数))上限为 6即最多 64 个分片当CACHESIZE 512KB时分片位数为 0单分片。示例如下CACHESIZE理论最大分片数分片位数实际分片数1 MB2124 MB83832 MB64664256 MB5126封顶64分片越多并发写缓存的锁竞争越小适合高并发场景但过多分片也会增加内存管理开销。警告修改CACHESHARDBITS会立即失效数据库中所有 vnode 的全部 last-value 缓存条目后续查询需要从磁盘重新加载可能暂时抬高查询延迟见 CACHESHARDBITS。创建与调整示例读缓存可通过CREATE DATABASE设置也可通过ALTER DATABASE动态调整-- 建库时开启 CREATE DATABASE power CACHEMODEL both CACHESIZE 16; -- 对已有库开启或调整 ALTER DATABASE power CACHEMODEL both; ALTER DATABASE power CACHESIZE 32;启用后可用SHOW CREATE DATABASE确认参数生效并用SHOW VGROUPS查看各 vnode 的cacheload当前 last-cache 使用字节数。实战验证智能电表场景的 LAST / LAST_ROW 加速以下示例对比启用读缓存前后LAST/LAST_ROW的查询延迟。先用taosBenchmark生成测试数据taosBenchmark -d power -Q --start-timestamp1600000000000 --tables10000 --records10000 --time-step10000 -y该命令创建数据库power与超级表meters约 1 亿行数据10000 个子表、每个子表 10000 行、起始时间戳1600000000000即2020-09-13T20:26:4008:00、间隔 10 秒。此时默认CACHEMODEL为none。未开启读缓存时的查询taos SELECT LAST(ts, current) FROM meters; last(ts) | last(current) | 2020-09-13 20:26:40.000 | 1.1294620 | Query OK, 1 row(s) in set (0.353815s) taos SELECT LAST_ROW(ts, current) FROM meters; last_row(ts) | last_row(current) | 2020-09-13 20:26:40.000 | 1.1294620 | Query OK, 1 row(s) in set (0.344070s)开启读缓存并确认生效taos ALTER DATABASE power CACHEMODEL both; Query OK, 0 row(s) affected (0.046092s) taos SHOW CREATE DATABASE power\G; *************************** 1.row *************************** Database: power Create Database: CREATE DATABASE power BUFFER 256 CACHESIZE 1 CACHEMODEL both COMP 2 DURATION 14400m WAL_FSYNC_PERIOD 3000 MAXROWS 4096 MINROWS 100 STT_TRIGGER 2 KEEP 5256000m,5256000m,5256000m PAGES 256 PAGESIZE 4 PRECISION ms REPLICA 1 WAL_LEVEL 1 VGROUPS 10 ... Query OK, 1 row(s) in set (0.000282s)再次查询首次查询填充缓存之后的查询通常延迟显著降低taos SELECT LAST(ts, current) FROM meters; last(ts) | last(current) | 2020-09-13 20:26:40.000 | 1.1294620 | Query OK, 1 row(s) in set (0.044021s) taos SELECT LAST_ROW(ts, current) FROM meters; last_row(ts) | last_row(current) | 2020-09-13 20:26:40.000 | 1.1294620 | Query OK, 1 row(s) in set (0.046682s)本示例中延迟从约 353 / 344 ms 降至约 44 ms。实际结果取决于数据规模、硬件与并发负载。更多细节见 Read CacheLAST/LAST_ROW语义见 LAST 与 LAST_ROW。元数据缓存Metadata Cache为提升查询与写入效率每个 vnode 都配备了一个缓存机制用于存放其先前访问过的元数据。元数据缓存的大小由建库时设置的两个参数决定PAGESvnode 元数据存储引擎的缓存页数量默认 256最小 64。一个 vnode 的元数据存储占用PAGESIZE * PAGES默认即 1MB 内存见 PAGES。PAGESIZEvnode 元数据存储引擎的页大小单位 KB默认 4 KB范围 116384即 1KB 到 16MB见 PAGESIZE。以下 SQL 为数据库power中每个 vnode 创建 128 页、每页 16KB 的元数据缓存CREATE DATABASE POWER PAGES 128 PAGESIZE 16;元数据缓存的默认值与取值范围以 PAGES 与 PAGESIZE 为准。文件系统缓存与 WALWAL数据可靠性的基础保障TDengine 使用 WALWrite-Ahead Logging预写日志技术作为基础的数据可靠性保障。其核心原理是在数据真正写入数据存储层之前先记录到日志文件中。这样即使集群遭遇崩溃或其他故障数据安全仍能得到保证——TDengine 利用这些日志文件在故障后恢复失败前的状态。在 WAL 写入过程中数据以顺序追加方式写入磁盘文件因此文件系统缓存在这一过程中扮演关键角色对写入性能有显著影响。为确保数据真正写入磁盘系统会调用fsync函数将数据从文件系统缓存强制写入磁盘。WAL_LEVEL 与 WAL_FSYNC_PERIOD数据库参数wal_level与wal_fsync_period共同决定 WAL 保存行为参数定义见 WAL_LEVEL 与 WAL_FSYNC_PERIODwal_level控制 WAL 保存级别。级别 1 表示数据仅写入 WAL但不立即执行fsync级别 2 表示写入 WAL 的同时执行fsync。默认值为 1。执行fsync能增强数据持久性但会降低写入性能。wal_fsync_period当wal_level为 2 时该参数控制执行fsync的频率。设为 0 表示每次写入后立即执行fsync可保证数据安全但可能牺牲部分性能设为大于 0 的值表示fsync周期默认 3000范围 [1, 180000]单位毫秒即最长三分钟。CREATE DATABASE POWER WAL_LEVEL 2 WAL_FSYNC_PERIOD 3000;性能优先 vs 可靠性优先创建数据库时用户可根据需求选择不同的参数组合在性能与可靠性之间找到最佳平衡性能优先数据写入 WAL但不立即执行fsync操作——新写入的数据仅保存在文件系统缓存中尚未同步到磁盘。该配置可显著提升写入性能但断电等场景下存在丢失最近数据的风险。可靠性优先数据写入 WAL 的同时执行fsync立即将数据同步到磁盘确保数据持久化与更高可靠性代价是写入吞吐下降。完整参数细节参见 WAL_LEVEL 与 WAL_FSYNC_PERIOD。缓存与持久化的协同从架构层面看Architecture: Cache and Persistence写缓存与持久化存储构成闭环当 vnode 中的缓存数据积累到一定量时为避免阻塞后续写入TDengine 会启动落盘线程将缓存数据写入持久化存储设备同时创建新的数据日志文件并在成功落盘后删除旧日志文件以防止日志无限增长。基于时间序列数据特征vnode 数据被拆分为多个文件每个文件按数据库参数duration存储固定天数的数据从而在查询特定时间段时能快速定位需打开的数据文件。这正是缓存保证实时性、落盘保证持久性的整体设计。总结如何选择缓存配置综合四类缓存机制可按以下思路配置数据库以官方文档为准则写入吞吐与最近数据访问通过VGROUPS与BUFFER调节写缓存规模缓存加大到一定程度后收益递减不必无限增大。当前值查询按查询形态选择CACHEMODELlast_row/last_value/both并按内存与表规模设置CACHESIZE高并发写场景用CACHESHARDBITS增加分片降低锁竞争。元数据访问用PAGES与PAGESIZE控制 vnode 元数据缓存的内存占用。可靠性 vs 性能用WAL_LEVEL与WAL_FSYNC_PERIOD平衡写性能与崩溃恢复的持久性。上述参数均可在 Databases 文档中查到完整的默认值、取值范围与注意事项读缓存的配置步骤与验证示例见 Read Cache。【免费下载链接】tdengineTDengine is an open source, high-performance, cloud native time-series database optimized for Internet of Things (IoT), Connected Cars, Industrial IoT and DevOps.项目地址: https://gitcode.com/taosdata/tdengine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考