分类 :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 拆分子任务
└─ 并行发送到各 DNode
6. 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;
代码示例
客户端连接优化
python
# 不好:频繁建连
for _ in range(1000):
conn = taosws.connect("...")
conn.execute("SELECT ...")
conn.close()
# 每次都要重新拉元数据
# 好:长连接复用
conn = taosws.connect("...")
for _ in range(1000):
conn.execute("SELECT ...")
conn.close()
# 更好:连接池
from queue import Queue
class ConnectionPool:
def __init__(self, size=10):
self.pool = Queue()
for _ in range(size):
self.pool.put(taosws.connect("..."))
def execute(self, sql):
conn = self.pool.get()
try:
return conn.execute(sql)
finally:
self.pool.put(conn)
大量建表优化
python
# 不好:逐表 CREATE
for i in range(100000):
conn.execute(f"CREATE TABLE d_{i} USING meters TAGS ('Beijing')")
# 元数据更新 10 万次
# 好:写入时自动建表
for i in range(100000):
conn.execute(f"""
INSERT INTO d_{i} USING meters TAGS ('Beijing')
VALUES (NOW, 25.3)
""")
# 更好:Schemaless 批量
lines = []
for i in range(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 最佳实践》
预聚合
索引
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 taosAdapter》
- 02-《TDengine taosX 与 Explorer》
- 03-《TDengine Grafana 集成》
- 04-《TDengine 第三方工具》
应用案例
- 01-《TDengine IoT 设备监控》
- 02-《TDengine 工业大数据与智能制造》
- 03-《TDengine 车联网与新能源汽车》
- 04-《TDengine 能源与电力监控》
- 05-《TDengine IT 运维与可观测性》
产品对比
内部机制
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。