news 2026/5/1 18:11:22

TDengine 数据缓存架构及使用详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TDengine 数据缓存架构及使用详解

TDengine 数据缓存架构及使用详解

一、设计理念

TDengine 采用**写驱动缓存(Write-driven Cache)**设计,与传统数据库的读驱动缓存截然不同。核心思想是:时序数据场景下,最新写入的数据往往是最频繁查询的数据

传统缓存 vs TDengine 缓存

传统数据库(读驱动缓存): 查询什么 → 缓存什么 ↓ 问题:冷数据占用缓存空间 问题:缓存命中率不稳定 TDengine(写驱动缓存): 写入什么 → 缓存什么 ↓ 优势:最新数据始终在缓存 优势:最新数据查询命中率 99%+ 优势:可替代 Redis 等缓存中间件

二、缓存架构总览

┌─────────────────────────────────────────────────────────┐ │ TDengine 多级缓存架构 │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────┐ │ │ │ L1: 写入缓存 (Write Buffer) │ │ │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │ │ │ Block1 │ │ Block2 │ │ Block3 │ (轮转使用) │ │ │ │ │(写入中)│ │(待落盘)│ │(落盘中)│ │ │ │ │ └────────┘ └────────┘ └────────┘ │ │ │ │ 数据结构: SkipList + Red-Black Tree │ │ │ │ 作用: 接收写入 + 缓存最新数据 │ │ │ └────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────┐ │ │ │ L2: 元数据缓存 (Meta Cache) │ │ │ │ - 表 Schema 信息 │ │ │ │ - 标签信息 │ │ │ │ - vgroup 路由信息 │ │ │ │ 数据结构: B+Tree + LRU │ │ │ │ 作用: 加速表查找和标签过滤 │ │ │ └────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────┐ │ │ │ L3: Last/Last_Row 缓存 │ │ │ │ ┌─────────────┐ ┌─────────────────┐ │ │ │ │ │ Last 缓存 │ │ Last_Row 缓存 │ │ │ │ │ │ (每列最新值) │ │ (最后一条记录) │ │ │ │ │ └─────────────┘ └─────────────────┘ │ │ │ │ 数据结构: LRU + 延迟加载 │ │ │ │ 作用: 加速 LAST/LAST_ROW 查询 │ │ │ └────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────┐ │ │ │ L4: 页面缓存 (Page Cache) │ │ │ │ - 数据页缓存 │ │ │ │ - 索引页缓存 │ │ │ │ 数据结构: Hash + LRU │ │ │ │ 作用: 加速磁盘数据访问 │ │ │ └────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘

三、核心缓存机制详解

3.1 写入缓存 (Write Buffer)

设计原理:采用多块内存轮转机制,实现写入与落盘并行

内存块轮转机制: 时刻 T1: [Block1: 写入中] → [Block2: 空闲] → [Block3: 空闲] 时刻 T2 (Block1 写满 1/3): [Block1: 待落盘] → [Block2: 写入中] → [Block3: 空闲] ↓ 触发 Block1 落盘(后台线程) 时刻 T3 (Block1 落盘完成): [Block1: 空闲] → [Block2: 写入中] → [Block3: 空闲] 优势: ✓ 写入永不阻塞 ✓ 始终保持 1/3 内存缓存最新数据 ✓ 落盘与写入并行执行

源码实现(基于 SkipList):

// 数据写入流程写入数据 ↓ 查找目标表的 SkipList(O(log n)) ↓ 插入数据到 SkipList(按时间排序) ↓ 更新统计信息 ↓ 检查内存块使用率 ↓ 超过阈值 → 触发落盘

配置参数

-- 创建数据库时配置写入缓存CREATEDATABASEsensor_db BUFFER256-- 每个 vnode 的写缓存大小(MB)PAGES128-- 元数据缓存页数PAGESIZE4;-- 每页大小(KB)-- 参数说明:-- BUFFER: 写入缓存总大小,越大可缓存越多最新数据-- PAGES × PAGESIZE = 元数据缓存大小

3.2 Last/Last_Row 缓存

设计原理:专门为时序数据的"查询最新值"场景优化

Last 缓存: ┌─────────────────────────────────────────┐ │ sensor_001: │ │ temperature: (ts=2024-01-15 12:00, 25.5)│ │ humidity: (ts=2024-01-15 12:00, 60) │ │ pressure: (ts=2024-01-15 11:55, 101) │ ← 各列可能时间不同 └─────────────────────────────────────────┘ Last_Row 缓存: ┌─────────────────────────────────────────┐ │ sensor_001: │ │ 最后一行: (ts=2024-01-15 12:00, │ │ temperature=25.5, │ │ humidity=60, │ │ pressure=NULL) ← 该行可能有 NULL│ └─────────────────────────────────────────┘ 区别: - LAST(col): 返回每列最后一个非 NULL 值(时间可能不同) - LAST_ROW(*): 返回表中最后一条记录(可能含 NULL)

配置方式

-- 方式1: 创建数据库时配置CREATEDATABASEsensor_db CACHEMODEL'both'-- 启用 last 和 last_row 缓存CACHESIZE10;-- 缓存大小(MB)-- CACHEMODEL 选项:-- 'none': 不启用缓存-- 'last_row': 只缓存 last_row-- 'last_value': 只缓存 last-- 'both': 同时缓存两者(推荐)-- 方式2: 修改已有数据库ALTERDATABASEsensor_db CACHEMODEL'both';ALTERDATABASEsensor_db CACHESIZE20;

查询加速效果

-- 无缓存时SELECTLAST(temperature)FROMsensor_001;执行: 扫描数据文件 → 找到最后非NULL值 耗时:10-100ms-- 有缓存时SELECTLAST(temperature)FROMsensor_001;执行: 直接从Last缓存返回 耗时:<1ms 性能提升:100x+

3.3 元数据缓存 (Meta Cache)

设计原理:基于 B+Tree + LRU 的元数据管理

// 源码结构 (tdbPCache.c)structSPCache{intszPage;// 页面大小intnPages;// 总页面数SPage**aPage;// 页面数组tdb_mutex_tmutex;// 并发锁intnFree;// 空闲页面数SPage*pFree;// 空闲链表intnHash;// 哈希表大小SPage**pgHash;// 页面哈希表intnRecyclable;// 可回收页面数SPage lru;// LRU 链表头};

缓存页面生命周期

页面状态流转: [空闲列表] → 分配 → [活跃使用] ↓ 引用计数归零 [LRU 队列] ↓ 空间不足时回收 [空闲列表] 关键操作: 1. Fetch: 从缓存获取页面(命中)或从磁盘加载 2. Pin: 固定页面,防止被回收 3. Unpin: 释放固定,允许回收 4. Release: 减少引用计数

3.4 页面缓存实现细节

哈希查找 + LRU 淘汰

// 页面查找流程 (简化)SPage*tdbPCacheFetchImpl(SPCache*pCache,constSPgid*pPgid){// 1. 计算哈希值uint32_th=tdbPCachePageHash(pPgid)%pCache->nHash;// 2. 在哈希表中查找SPage*pPage=pCache->pgHash[h];while(pPage){if(pPage->pgid.pgno==pPgid->pgno&&memcmp(pPage->pgid.fileid,pPgid->fileid,TDB_FILE_ID_LEN)==0)break;pPage=pPage->pHashNext;}// 3. 命中:从 LRU 移除(Pin 住)if(pPage){tdbPCachePinPage(pCache,pPage);returnpPage;}// 4. 未命中:从空闲列表或 LRU 获取页面if(pCache->pFree){pPage=pCache->pFree;pCache->pFree=pPage->pFreeNext;}elseif(!pCache->lru.pLruPrev->isAnchor){// 从 LRU 尾部回收pPage=pCache->lru.pLruPrev;tdbPCacheRemovePageFromHash(pCache,pPage);}// 5. 加载数据并加入哈希表// ...returnpPage;}

四、缓存优化最佳实践

4.1 ✅ 推荐配置

场景1:高频实时查询(IoT 监控)
-- 最新数据查询频繁,需要大缓存CREATEDATABASEiot_monitor BUFFER512-- 大写入缓存(512MB)CACHEMODEL'both'-- 启用双缓存CACHESIZE100-- Last 缓存 100MBPAGES256-- 元数据缓存页数PAGESIZE4;-- 4KB 页面-- 适用场景:-- - 设备状态实时监控-- - 最新数据仪表盘展示-- - 实时告警系统
场景2:历史数据分析(数据仓库)
-- 历史查询为主,缓存可以小一些CREATEDATABASEdata_warehouse BUFFER128-- 中等写入缓存CACHEMODEL'none'-- 不需要 Last 缓存PAGES512-- 大元数据缓存(历史表多)PAGESIZE4;-- 适用场景:-- - 历史数据报表-- - 批量数据分析-- - 数据归档存储
场景3:混合负载(通用场景)
-- 平衡配置CREATEDATABASEmixed_workload BUFFER256CACHEMODEL'both'CACHESIZE50PAGES256PAGESIZE4;

4.2 ✅ 加速查询的技巧

1. 利用 LAST_ROW 替代 ORDER BY LIMIT
-- ❌ 慢:需要排序SELECT*FROMsensor_001ORDERBYtsDESCLIMIT1;-- ✅ 快:直接从缓存返回SELECTLAST_ROW(*)FROMsensor_001;性能提升:100x+
2. 使用 LAST 获取各列最新值
-- ❌ 慢:多次查询SELECT(SELECTtemperatureFROMsensor_001ORDERBYtsDESCLIMIT1)astemp,(SELECThumidityFROMsensor_001ORDERBYtsDESCLIMIT1)ashum;-- ✅ 快:一次查询,命中缓存SELECTLAST(temperature),LAST(humidity)FROMsensor_001;性能提升:50x+
3. 批量获取多表最新值
-- ✅ 高效:利用 Last 缓存SELECTtbname,LAST(temperature),LAST(humidity)FROMsensorsWHERElocation='北京'GROUPBYtbname;-- 执行过程:-- 1. 标签过滤(内存操作,毫秒级)-- 2. 每张表从 Last 缓存获取值-- 3. 组装返回结果-- 1000 张表查询耗时: < 100ms
4. 实时数据展示优化
-- ✅ 推荐:最近 N 分钟数据(命中写入缓存)SELECT*FROMsensor_001WHEREts>=NOW-10m;-- 执行过程:-- 1. 检查写入缓存(MemTable)-- 2. 大部分数据在内存中命中-- 3. 少量数据可能需要读磁盘-- 缓存命中率: 90%+

4.3 ❌ 要避免的误区

1. 避免不合理的缓存配置
-- ❌ 差:缓存太小,写入频繁落盘CREATEDATABASEsensor_db BUFFER16;-- 问题:-- - 频繁触发落盘-- - 写入性能下降-- - 最新数据缓存命中率低-- ✅ 好:根据写入量配置-- 计算公式: BUFFER >= 写入速率 × 期望缓存时间-- 例如: 10MB/s × 30s = 300MBCREATEDATABASEsensor_db BUFFER300;
2. 避免未启用 Last 缓存却频繁查询最新值
-- ❌ 差:未启用缓存CREATEDATABASEsensor_db CACHEMODEL'none';-- 然后频繁执行SELECTLAST(temperature)FROMsensor_001;-- 每次都读磁盘-- ✅ 好:启用缓存ALTERDATABASEsensor_db CACHEMODEL'both';
3. 避免 CACHESIZE 设置过小
-- ❌ 差:缓存太小,频繁淘汰CREATEDATABASEsensor_db CACHEMODEL'both'CACHESIZE1;-- 只有 1MB-- 问题:-- - 10000 张表,每表缓存条目约 100 字节-- - 1MB 只能缓存约 10000 条-- - 频繁 LRU 淘汰,缓存命中率低-- ✅ 好:根据表数量配置-- 计算公式: CACHESIZE >= 表数量 × 0.001 MB-- 例如: 100000 张表 → CACHESIZE 100CREATEDATABASEsensor_db CACHEMODEL'both'CACHESIZE100;
4. 避免用 SELECT * 查询最新数据
-- ❌ 差:SELECT * 可能不走缓存SELECT*FROMsensor_001ORDERBYtsDESCLIMIT1;-- ✅ 好:使用专用函数SELECTLAST_ROW(*)FROMsensor_001;-- 或SELECTLAST(col1),LAST(col2),...FROMsensor_001;

五、性能监控与调优

5.1 查看缓存配置

-- 查看数据库缓存配置SHOWDATABASES;-- 查看详细参数SELECT*FROMinformation_schema.ins_databasesWHEREname='sensor_db';-- 关注字段:-- buffer: 写入缓存大小-- cachemodel: 缓存模式-- cachesize: Last 缓存大小-- pages: 元数据缓存页数

5.2 动态调整缓存

-- 增大 Last 缓存ALTERDATABASEsensor_db CACHESIZE200;-- 修改缓存模式ALTERDATABASEsensor_db CACHEMODEL'both';-- 注意: BUFFER 和 PAGES 创建后不可修改

5.3 内存使用估算

总内存使用 ≈ vnode数 × BUFFER + vnode数 × PAGES × PAGESIZE + CACHESIZE + 系统开销 示例计算: - 4 个 vnode - BUFFER = 256MB - PAGES = 128, PAGESIZE = 4KB - CACHESIZE = 50MB 总计 ≈ 4×256 + 4×128×0.004 + 50 + 100 ≈ 1024 + 2 + 50 + 100 ≈ 1176 MB

六、缓存与其他数据库对比

特性TDengineMySQLRedis
缓存驱动写驱动读驱动读驱动
最新数据命中99%+不确定需要应用层维护
专用函数LAST/LAST_ROW
缓存一致性自动保证需要应用层处理需要应用层处理
内存效率
配置复杂度

七、总结

TDengine 缓存核心优势

  1. 写驱动设计:最新数据自动缓存,命中率 99%+
  2. 多级缓存:写入缓存 + 元数据缓存 + Last 缓存 + 页面缓存
  3. 专用优化:LAST/LAST_ROW 函数直接利用缓存
  4. 配置简单:几个参数即可完成优化
  5. 自动管理:无需应用层处理缓存一致性

性能提升效果

查询类型无缓存有缓存提升
LAST_ROW10-50ms< 1ms100x+
LAST10-100ms< 1ms100x+
最近时间范围50-200ms5-20ms10x+
元数据查询10-50ms< 1ms50x+

最佳实践速查

-- 高频实时查询场景CREATEDATABASEiot_db BUFFER512CACHEMODEL'both'CACHESIZE100PAGES256PAGESIZE4;-- 查询最新数据SELECTLAST_ROW(*)FROMtable_name;SELECTLAST(col1),LAST(col2)FROMtable_name;-- 查询最近数据(命中写入缓存)SELECT*FROMtable_nameWHEREts>=NOW-10m;

TDengine 的缓存机制是其高性能的关键因素之一,通过合理配置和正确使用,可以实现毫秒级最新数据查询,大幅提升时序数据应用的用户体验。

关于 TDengine

TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 0:35:20

MinerU终极指南:一站式PDF智能解析完整教程

还在为PDF文档的结构化解析而头疼吗&#xff1f;面对复杂的学术论文、技术文档或商业报告&#xff0c;传统OCR工具往往无法准确识别版面结构、表格内容和数学公式&#xff0c;导致信息提取不完整、格式混乱等问题。MinerU作为一款开源的高质量PDF解析工具&#xff0c;专门解决这…

作者头像 李华
网站建设 2026/4/30 23:23:56

企业级Android SDK管理实战:从下载到CI/CD集成

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个企业级Android SDK管理工具&#xff0c;支持&#xff1a;1) 多版本SDK并行管理&#xff1b;2) 团队共享SDK仓库功能&#xff1b;3) 与Jenkins/GitLab CI的深度集成&#xf…

作者头像 李华
网站建设 2026/4/30 23:24:09

DBeaver连接MySQL效率提升300%的5个技巧

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个DBeaver效率优化工具。功能&#xff1a;1) 连接响应时间测试 2) SSH隧道自动配置 3) 连接池参数优化建议 4) 网络延迟诊断 5) 生成优化报告。通过AI分析历史连接数据给出个…

作者头像 李华
网站建设 2026/5/1 0:34:04

RT-Thread开发新姿势:AI自动生成嵌入式代码

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 基于RT-Thread操作系统开发一个温湿度监测系统&#xff0c;使用STM32F103芯片和DHT11传感器。要求&#xff1a;1.创建两个线程&#xff0c;分别负责传感器数据采集和LCD显示&#x…

作者头像 李华
网站建设 2026/4/30 23:24:10

从零到发布:Prism框架如何提升WPF开发效率3倍

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个对比演示项目&#xff0c;展示使用Prism框架与传统WPF开发的效率差异&#xff1a;1.基础框架搭建时间对比&#xff0c;2.添加新功能模块的步骤对比&#xff0c;3.实现跨模块…

作者头像 李华