欢迎参与 8 月 1 日中午 11 点的线上分享,了解 GreptimeDB 联合处理指标和日志的最新方案! 👉🏻 点击加入

Skip to content
On this page
技术
2025-4-15

GreptimeDB 可观测数据建模实战指南

可观测性离不开 Metrics,Logs 和 Traces,本文围绕 GreptimeDB,系统梳理了从字段基数、主键设计到宽表建模、索引加速、数据去重与分区策略的实战方法。

当我们谈论可观测性数据时,总绕不开指标(Metrics)、日志(Logs)和追踪(Traces)这三个核心要素。就像医生需要体温、血液报告和 CT 影像来诊断病情,这三个数据维度能帮助我们全面洞察系统的健康状态。今天我们就来聊聊,如何用 GreptimeDB 为这些数据打造一个既高效又灵活的“病历系统”。

🧩 理解数据特性:先摸清家底再设计

在设计表结构前,我们需要先理解两个核心概念:

1. 列的基数:你的数据有多“独特”?

想象你正在组织一个图书馆:

  • 低基数列就像“图书分类”——科幻、历史、艺术等,通常不超过几千个值,常见的有:
    • region(地域)
    • cluster_name(集群名称)
    • http_method(请求方法)
  • 高基数列则像每本书的 ISBN 码或每个读者的 ID,可能有数百万到亿万个不同值,例如:
    • trace_id(追踪 ID)
    • user_id(用户 ID)
    • request_id(请求 ID)

在某电商监控项目中,region(区域)是低基数列(只有 7 个值),而 user_id 是高基数列(亿级用户)。这个区别看似简单,但对性能影响巨大。

2. 三种列类型:数据的角色定位

在 GreptimeDB 中,每一列都扮演着特定角色:

  • Tag:定义主键,确定数据如何排序和去重;
  • Field:存储实际的测量值和其他非主键数据;
  • Timestamp:通过 TIME INDEX 标识的时间列。
sql
CREATE TABLE http_logs (
  access_time TIMESTAMP TIME INDEX,  -- 🕒 时间戳(必须项)
  application STRING,                -- 🏷️ 标签(建议作为主键)
  http_status INT,                   -- 📊 数值型指标
  request_id STRING,                 -- 🔍 高基数特征
  PRIMARY KEY(application)          
);

🔑 主键设计:选择合适的“数据门牌号”

场景一:无主键设计,高性能日志收集

对于纯粹的日志收集,我常用“无主键+追加模式”的设计:

sql
CREATE TABLE access_logs (
  timestamp TIMESTAMP TIME INDEX,
  service STRING,
  status_code INT,
  response_time DOUBLE,
  trace_id STRING
) with ('append_mode'='true');

这种设计通常写入速度最快,适合“写入一次,永不更新”的场景,写入速度提升 30%+!

场景二:优先选择低基数列

当需要对数据进行更新或按特定维度查询时,主键就变得很重要了。以下是我总结的黄金法则:

  1. 只选择低基数列避免request_idtrace_id 等作为主键;
  2. 控制主键数量:主键值笛卡尔积建议不超过 10 万个;
  3. 限制主键列数:通常不超过 5 个列;
  4. 偏好字符串和整数:避免浮点数和时间戳类型作为主键。

一个不错的主键组合示例:

sql
CREATE TABLE service_metrics (
  datacenter STRING,     -- 低基数:北美、欧洲、亚太等
  service_name STRING,   -- 低基数:用户服务、支付服务等
  cpu_util DOUBLE,
  memory_util DOUBLE,
  ts TIMESTAMP,
  PRIMARY KEY(datacenter, service_name),
  TIME INDEX(ts)
);

🔍 查询加速三剑客:索引选择指南

GreptimeDB 提供了三种索引类型,我们可以根据数据特性选择需要的索引:

倒排索引:精准分类查找

适合低基数的有限分类的快速筛选:

sql
CREATE TABLE api_metrics (
  `endpoint` STRING,                   -- 🎯 服务接口名
  service_name STRING INVERTED INDEX,  -- 🎯 服务名称索引
  http_status INT INVERTED INDEX,      -- 🎯 状态码索引
  latency DOUBLE,
  ts TIMESTAMP,
  PRIMARY KEY(service_name, `endpoint`),
  TIME INDEX(ts)
);

倒排索引支持丰富的比较操作:=<>INBETWEEN 等。

跳数索引:大海捞针利器

对于很高基数的列的筛选,比如处理千万级用户 ID 的查询:

sql
CREATE TABLE user_events (
  user_id STRING SKIPPING INDEX, -- 对用户ID建立跳数索引
  event_type STRING,
  device STRING,
  ts TIMESTAMP,
  TIME INDEX(ts)
) with ('append_mode'='true');

跳数索引就非常有效,它占用空间更小,对写入的影响也很小,但它只支持等值查询。

全文索引:日志关键词搜索

sql
CREATE TABLE error_logs (
  message STRING FULLTEXT INDEX WITH(analyzer = 'English'),
  `level` STRING INVERTED INDEX,
  ts TIMESTAMP,
  TIME INDEX(ts)
) with ('append_mode'='true');

这使你可以执行类似 SELECT * FROM error_logs WHERE matches(message, 'connection timeout') 的查询。

🧮 宽表与多表设计:如何组织多种指标

宽表优势:一站式数据超市

从我们的实践经验来看,把同时采集的多个指标放在一个宽表中通常是最佳选择:

sql
CREATE TABLE node_metrics (
  host STRING,
  cpu_user DOUBLE,
  cpu_system DOUBLE,
  memory_used DOUBLE,
  disk_read_bytes DOUBLE,
  disk_write_bytes DOUBLE,
  net_in_bytes DOUBLE,
  net_out_bytes DOUBLE,
  ts TIMESTAMP,
  PRIMARY KEY(host),
  TIME INDEX(ts)
);

宽表设计带来三大好处:

  1. 数据压缩率更高(节省 30-50% 存储空间);
  2. 关联指标查询更简单(无需复杂的 JOIN)。

何时应该分表(多表)

只有在以下场景才考虑使用多表设计:

  • 不同指标有完全不同的采集频率(如秒级 CPU 指标与小时级账单数据);
  • 不同指标关联的元数据(标签)差异巨大;
  • 出于数据访问权限管理需求。

🔄 数据去重与更新策略

在处理指标数据时,有时需要对同一时间点的数据进行更新。GreptimeDB 提供了两种合并模式:

last_row 模式:整行更新

默认情况下,GreptimeDB 使用 last_row 模式,保留最新插入的整行数据。

last_non_null 模式:字段级更新

当你只想更新个别字段时,last_non_null 模式更为合适:

sql
CREATE TABLE device_telemetry (
  device_id STRING,
  temperature DOUBLE,
  humidity DOUBLE,
  battery DOUBLE,
  ts TIMESTAMP,
  PRIMARY KEY(device_id),
  TIME INDEX(ts)
) with ('merge_mode'='last_non_null');

这样,你就可以只更新某个字段,不会影响其他字段的值:

sql
INSERT INTO device_telemetry(device_id, temperature, ts)
VALUES ('device1', 25.5, now()); -- 只更新温度,保留其他字段现有值

🌐 分布式表设计:扩展到 PB 级数据

当数据量达到 TB 级别,单机存储不足以支撑时,我们可以考虑分区表设计:

sql
CREATE TABLE global_metrics (
 region STRING,
 datacenter STRING,
 host STRING,
 cpu DOUBLE,
 memory DOUBLE,
 ts TIMESTAMP,
 PRIMARY KEY(region, datacenter, host),
 TIME INDEX(ts)
 ) PARTITION ON COLUMNS (region) (
 region = 'region1',
 region = 'region2',
 region = 'region3'
 );

选择分区键的关键是找到分布均匀且与查询模式匹配的列。根据我们的经验,地域、业务线或应用名称通常是不错的选择。

🛠️ 实战案例分析

案例一:大规模 API 监控系统

在一个处理每天数十亿API请求的系统中,我们使用以下设计:

sql
CREATE TABLE api_metrics (
 region STRING,
 service STRING,
 `endpoint` STRING,
 status_code INT INVERTED INDEX,
 p50_latency DOUBLE,
 p95_latency DOUBLE,
 p99_latency DOUBLE,
 error_count INT,
 success_count INT,
 ts TIMESTAMP,
 PRIMARY KEY(region, service, `endpoint`),
 TIME INDEX(ts)
 ) PARTITION ON COLUMNS (region) (
 region = 'us-east-1',
 region = 'eu-central-1',
 region = 'ap-southeast-2'
 ) with ('merge_mode'='last_non_null');

这个设计的优势是:

  • 使用多级低基数主键,便于按服务或端点筛选;
  • 将相关指标放在同一行,简化查询和提高压缩率;
  • 对状态码建立索引,支持快速筛选错误请求;
  • 按地域分区,减少跨区域查询的网络开销。

案例二:IoT 设备监控平台

对于一个监控数十万 IoT 设备的平台,我们采用了这样的设计:

sql
CREATE TABLE device_metrics (
 device_type STRING,
 `location` STRING,
 device_id STRING SKIPPING INDEX,
 temperature DOUBLE,
 humidity DOUBLE,
 battery_level DOUBLE,
 signal_strength DOUBLE,
 alert_count INT,
 ts TIMESTAMP,
 PRIMARY KEY(device_type, `location`),
 TIME INDEX(ts)
 ) PARTITION ON COLUMNS (device_type) (
 device_type = 'sensor',
 device_type = 'actuator',
 device_type = 'controller'
 );

优势:

  • 使用 device_typelocation 作为低基数主键,而非高基数的 device_id
  • device_id 建立跳数索引,支持快速查找特定设备;
  • 将相关传感器数据存储在同一行,提高查询效率;
  • 按设备类型分区,便于管理不同类型设备的数据保留策略。

💡 性能优化小贴士

基于我们的实际经验,这里分享六个能显著提升性能的小技巧:

  1. 从简单开始:先创建简单的无主键表,建立性能基准;
  2. 避免过度索引:索引会增加写入开销,只为常用查询条件创建索引;
  3. 定期监控表大小:当单表超过 500GB 时考虑分区;
  4. 时间分区已内置:GreptimeDB 的数据存储已经按照时间来分区组织,无需额外操作;
  5. 注意热点分区:确保写入和查询负载均匀分布在各分区;
  6. 合理设置 TTL:对不同重要程度的数据设置不同的保留期;
  7. 容量规划:写入满足的情况下,预留 50% 的系统资源给查询请求;
  8. 采样策略:使用 Flow 流计算任务来对数据做降采样,比如秒级降低为分钟级等。

Flow 流计算文档

数据建模是一门艺术,也是一门科学。通过合理的设计,你可以在保证查询性能的同时,大幅降低存储和运维成本。希望这份指南能帮助你在GreptimeDB上构建高效的可观测性数据平台!🎯

如果有任何问题,在评论区留言或加入我们的社区讨论。

作者注:本文基于GreptimeDB v0.13 及以上版本的特性撰写,随着新版本发布可能有所变化。

欢迎阅读数据建模指南官方文档


关于 Greptime

Greptime 格睿科技专注于为可观测、物联网及车联网等领域提供实时、高效的数据存储和分析服务,帮助客户挖掘数据的深层价值。目前基于云原生的时序数据库 GreptimeDB 已经衍生出多款适合不同用户的解决方案,更多信息或 demo 展示请联系下方小助手(微信号:greptime)。

欢迎对开源感兴趣的朋友们参与贡献和讨论,从带有 good first issue 标签的 issue 开始你的开源之旅吧~期待在开源社群里遇见你!添加小助手微信即可加入“技术交流群”与志同道合的朋友们面对面交流哦~

Star us on GitHub Now: https://github.com/GreptimeTeam/greptimedb

官网:https://greptime.cn/

文档:https://docs.greptime.cn/

Twitter: https://twitter.com/Greptime

Slack: https://greptime.com/slack

LinkedIn: https://www.linkedin.com/company/greptime/

加入我们的社区

获取 Greptime 最新更新,并与其他用户讨论。