分类:17.内部机制 |篇章:04 Catalog 与元数据缓存
免费详情
Catalog 是数据库的"地图":库、表、列、Tag、路由信息。本文讲解 Catalog 设计、客户端缓存、一致性机制。
Catalog 内容速查
| 项 | 说明 |
|---|---|
| 数据库 | 库元信息 |
| 超级表 | Schema + Tag 定义 |
| 子表 | Tag 值 + 所在 VGroup |
| 路由 | VGroup → DNode 映射 |
| 用户 | 权限信息 |
| 索引 | 索引元数据 |
详细解析
1. 元数据层级
集群元数据(MNode 维护): Cluster │ ├── DNode 列表(节点) │ ├── Database 列表 │ │ │ ├── STable 列表 │ │ │ │ │ ├── Schema(列定义) │ │ ├── Tag Schema │ │ └── 子表索引 │ │ │ └── VGroup 列表 │ │ │ └── 副本分配 │ ├── User 列表 │ └── 流/订阅 列表 权威性: - MNode RAFT 副本保证 - 强一致 - 操作通过 Leader - 失效后自动选举2. 元数据存储
MNode 持久化: ① 内存常驻 - 全部元数据加载到内存 - 高效查询 ② 持久化机制 - 写日志 + Snapshot - 启动时恢复 - RAFT 复制到副本 VNode 元数据: ① 包含 VGroup 内的: - 超级表 Schema - 子表信息(Tag + 列) - 索引 ② 持久化在 VNode 数据目录 大小估算: - 单超级表元数据 ~ KB - 单子表 Tag ~ 百字节 - 100 万子表 ~ 100 MB 内存3. 客户端缓存
客户端 Catalog Cache: ① 连接时获取: - 库列表 - 表列表(按需) - 路由表 ② 解析 SQL 用: - 查表是否存在 - 校验列名 - 路由到 VGroup ③ 缓存策略: - LRU - TTL - 版本号检测 缓存命中率: - 静态环境:> 99% - 频繁建表:低 - 影响每次查询的额外开销4. 元数据一致性
版本号机制: ① 每次 DDL 操作: - MNode 版本号递增 - 元数据更新持久化 - 通知关联 DNode/VNode ② 客户端检测: - 每个请求带版本号 - 服务端返回最新版本 - 客户端发现落后 → 主动刷新 ③ VNode 检测: - 收到请求版本号过旧 → 拒绝 - 收到过新 → 主动拉取 一致性保证: - DDL 操作:通过 MNode 串行化 - 客户端缓存:最终一致 - VNode:强一致(RAFT)5. 路由解析
SQL 执行的路由步骤: ① 解析 SQL → AST └─ 提取库名 + 表名 + WHERE 条件 ② Catalog 查询: └─ 库存在? └─ 表存在? └─ Schema 信息? ③ Tag 过滤优化: └─ WHERE 含 Tag 条件 └─ 利用 Tag 索引缩小子表集 ④ 路由计算: └─ 子表 → VGroup └─ VGroup → DNode(取 Leader) ⑤ 任务下发: └─ 按 VGroup 拆分子任务 └─ 并行发送到各 DNode6. DDL 操作流程
CREATE TABLE / ALTER TABLE 流程: ① 客户端 → MNode(任意节点中转) ② MNode Leader 接收: - 验证语法 - 验证权限 - 检查冲突 ③ RAFT 写入: - 写日志 - 复制到 Follower - majority 确认 ④ 应用到状态机: - 更新元数据 - 版本号递增 ⑤ 通知 VNode: - 异步推送变更 - VNode 应用 schema ⑥ 返回客户端 延迟: - 简单 DDL:< 100ms - 复杂(大量子表):秒级 - 跨节点同步:异步7. 缓存失效
失效场景: ① 主动失效: - DDL 操作(DROP TABLE) - 用户权限变化 ② 检测失效: - 请求带旧版本号 - 服务端返回错误 - 客户端清缓存重试 ③ TTL 失效: - 缓存条目过期 - 主动刷新 客户端策略: - 失败重试时刷新 - 周期心跳获取最新版本 - 收到 invalidation 通知(视实现) 典型情况: - DROP TABLE 后查询 → 错误 → 客户端刷新 → 重试 → 报"表不存在" - ALTER ADD COLUMN → 旧客户端可继续工作,新列需更新8. 调优与监控
元数据相关问题: ① 大量子表导致内存高 - 解决:减少 Tag 组合,合并子表 ② DDL 慢 - 解决:避免高频建表 - Schemaless 自动创建走优化路径 ③ 客户端缓存命中低 - 解决:长连接,避免频繁重连 - 检查 SQL 是否每次都不同表 ④ 元数据查询延迟 - 解决:MNode 节点资源 - 增加 MNode 数(少数情况) 监控: -- 子表数量 SELECT db_name, COUNT(*) FROM information_schema.ins_tables GROUP BY db_name; -- 超级表数量 SELECT COUNT(*) FROM information_schema.ins_stables; -- MNode 状态 SHOW MNODES; SELECT * FROM information_schema.ins_mnodes;代码示例
客户端连接优化
# 不好:频繁建连for_inrange(1000):conn=taosws.connect("...")conn.execute("SELECT ...")conn.close()# 每次都要重新拉元数据# 好:长连接复用conn=taosws.connect("...")for_inrange(1000):conn.execute("SELECT ...")conn.close()# 更好:连接池fromqueueimportQueueclassConnectionPool:def__init__(self,size=10):self.pool=Queue()for_inrange(size):self.pool.put(taosws.connect("..."))defexecute(self,sql):conn=self.pool.get()try:returnconn.execute(sql)finally:self.pool.put(conn)大量建表优化
# 不好:逐表 CREATEforiinrange(100000):conn.execute(f"CREATE TABLE d_{i}USING meters TAGS ('Beijing')")# 元数据更新 10 万次# 好:写入时自动建表foriinrange(100000):conn.execute(f""" INSERT INTO d_{i}USING meters TAGS ('Beijing') VALUES (NOW, 25.3) """)# 更好:Schemaless 批量lines=[]foriinrange(100000):lines.append(f"meters,device=d_{i},location=Beijing current=25.3{ts}")conn.execute_schemaless(lines)性能考量
元数据操作开销
| 操作 | 开销 |
|---|---|
| 查询缓存命中 | < 1µs |
| 查询缓存失效 | 1~10ms(拉元数据) |
| CREATE TABLE | 10~100ms |
| ALTER TABLE | 10~100ms |
| 子表 INSERT(自动建) | 10ms 首次 |
容量限制
| 项 | 典型上限 |
|---|---|
| 单库子表数 | 千万级 |
| 单 STable 列数 | 4096 |
| 单 STable Tag 数 | 128 |
| 单库 STable 数 | 万级 |
FAQ
Q1: 元数据丢了怎么办?
MNode RAFT REPLICA 3 提供保护。极端情况:
- 从备份恢复
- 联系官方支持
Q2: 客户端缓存怎么手动刷新?
通常自动处理。手动可重连。
Q3: 子表数过多影响性能?
- 内存占用大(每子表 Tag)
- 元数据查询慢
- 建议合理设计 Tag,避免过细
Q4: SHOW TABLES 慢?
子表数过多时慢。优化:
- 按超级表过滤
- 用 LIMIT
- 查 ins_tables 系统表带过滤
Q5: MNode 数怎么选?
- 小集群:1(无 HA)
- 标准生产:3
- 超大集群:3 或 5
不需要太多,MNode 不存数据。
参考
系统构架篇
- 01-《TDengine 整体架构全景》
- 02-《集群拓扑深度解析》
- 03-《MNode 内部机制深度解析》
- 04-《RPC 通信层深度解析》
- 05-《VNode 生命周期》
- 06-《RAFT 共识协议》
- 07-《端到端的消息流》
数据模型
- 01-《数据库创建与参数详解》
- 02-《超级表/子表/普通表》
- 03-《支持数据类型深度解析》
- 04-《TDengine Tag 设计哲学与 Schema 变更机制》
- 05-《TDengine 虚拟表实现原理》
存储引擎
- 01-《TDengine 存储引擎概览》
- 02-《TDengine MemTable 深度解析》
- 03-《TDengine WAL 预写日志机制》
- 04-《TDengine 数据文件格式》
- 05-《TDengine Commit 与 Flush 机制 》
- 06-《TDengine Compaction 合并策略 》
- 07-《TDengine 数据保留与 TTL》
- 08-《TDengine 压缩编码机制》
- 09-《TDengine Cache 与 Last 查询加速》
- 10-《TDengine 逻辑计划生成》
查询引擎
- 01-《TDengine 查询引擎概览》
- 02-《TDengine SQL 解析与词法分析》
- 03-《TDengine 语义分析与 AST 重写》
- 04-《TDengine 逻辑计划生成》
- 05-《TDengine 物理计划生成》
- 06-《TDengine 扫描算子》
- 07-《TDengine 聚合算子》
- 08-《TDengine 连接算子》
- 09-《TDengine 排序、填充与投影》
- 10-《TDengine 分布式查询执行》
- 11-《TDengine EXPLAIN 与查询优化》
数据写入
- 01-《TDengine SQL INSERT》
- 02-《TDengine 无模式写入》
- 03-《TDengine STMT 写入》
- 04-《TDengine 写入内部流程》
- 05-《TDengine 数据更新删除》
数据订阅
- 01-《TDengine 数据订阅》
- 02-《TDengine 订阅 vs Kafka》
- 03-《TDengine TMQ 消费流程》
- 04-《TDengine 内部机制》
- 05-《TDengine TMQ 最佳实践》
预聚合
- 01-《TDengine RSMA》
- 02-《TDengine TSMA — 时间维度的物化聚合视图》
- 03-《TDengine SMA 内部实现》
索引
- 01-《TDengine Tag 索引》
- 02-《TDengine SMA 索引》
SQL 语句
- 01-《TDengine DDL》
- 02-《TDengine DML SELECT》
- 03-《TDengine DML 函数完整参考》
- 04-《TDengine JOIN 完整语法》
- 05-《TDengine 窗口完整语法》
- 06-《TDengine 操作符与表达式》
- 07-《TDengine 系统表》
- 08-《TDengine SQL 与标准 SQL 差异》
客户端与连接器
- 01-《TDengine 的连接方式》
- 02-《TDengine C/C++ 连接器》
- 03-《TDengine java 连接器》
- 04-《TDengine Python 连接器》
- 05-《TDengine Go 与 Rust 连接器》
- 06-《TDengine Node.js 与 C# 连接器》
运维
- 01-《TDengine 部署指南》
- 02-《TDengine 配置详解》
- 03-《TDengine 监控系统》
- 04-《TDengine 备份与恢复》
- 05-《TDengine 版本升级》
- 06-《TDengine 加密使用指南》
安全
- 01-《TDengine 认证与权限》
- 02-《TDengine 认证与权限》
- 03-《TDengine 审计日志》
生态
- 01-《TDengine taosAdapter》
- 02-《TDengine taosX 与 Explorer》
- 03-《TDengine Grafana 集成》
- 04-《TDengine 第三方工具》
应用案例
- 01-《TDengine IoT 设备监控》
- 02-《TDengine 工业大数据与智能制造》
- 03-《TDengine 车联网与新能源汽车》
- 04-《TDengine 能源与电力监控》
- 05-《TDengine IT 运维与可观测性》
产品对比
- 01-《TDengine vs InfluxDB》
- 02-《TDengine vs TimescaleDB》
- 03-《TDengine vs IoTDB/ClickHouse》
内部机制
- 01-《TDengine 内存管理》
- 02-《TDengine 线程模型》
- 03-《TDengine 错误处理》
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。