当前位置: 首页 > news >正文

【SkyWalking从入门到精通】第49篇:SkyWalking存储模型全景图:四大模型如何撑起海量观测数据

上一篇【第48篇】OAP集群规划实战:从理论到生产的容量设计
下一篇【第50篇】注册模型与明细模型:名字的管家和现场的记录官


一、开篇:不是所有的数据都叫"观测数据"

APM系统要存储的数据五花八门——服务名、Trace ID、响应时间、HTTP状态码、JVM指标、调用拓扑……如果像NoSQL那样用一个万能JSON把什么都往里塞,查询性能会惨不忍睹。

SkyWalking的设计者做了一个聪明的分类:把所有数据按"使用方式"分成四大类型,每种类型用专门的存储结构和优化策略。这就是四大存储模型的由来。

APM数据 ======= | | 按"怎么用"来分类 v +---+---+---+---+ | | | | | v v v v v 注册 明细 指标 采样 模型 模型 模型 模型 查得少 查得多 查得多 查得少 写得少 写得多 读得频繁 按需获取 小体积 大体积 小体积 大体积 永久 有限TTL 有限TTL 有限TTL 图1:四大模型对APM数据的分类策略

二、四种模型的特征速览

维度注册模型明细模型指标模型采样模型
存储内容服务名、端点名等实体Trace/Span明细聚合后的数值指标慢查询/异常堆栈
数据量很小(千级)极大(亿级/天)中等(万级/天)小(按规则触发)
写频率极低(几乎不变)极高(实时)中(分钟级)低(按需)
读频率高(Dashboard常查)低(问题排查时)极高(Dashboard)低(问题排查时)
保留周期永久7-14天分钟:30天/小时:90天/天:180天7-14天
典型大小KB级GB-TB级MB-GB级MB级

三、注册模型(Register Model)

3.1 解决什么问题?

当探针上报一个Span说"这是user-service调用了order-service的/createOrder接口",OAP需要把这些名字存起来。但每次Span都带完整名字太浪费,于是SkyWalking做了一层名字到ID的映射:

name = "user-service" --> serviceId = 1 name = "order-service" --> serviceId = 2 name = "/createOrder" --> endpointId = 101

3.2 注册模型的类结构

// 注册模型 - 服务注册@Stream(name="service_inventory",scopeId=0)publicclassServiceInventoryextendsRegisterSource{@Column(columnName="service_id")@IDColumnprivateintserviceId;// 服务ID(主键)@Column(columnName="name")@NeedDefaultprivateStringname;// 服务名称@Column(columnName="is_address")privateintisAddress;// 是否是地址@Column(columnName="properties")privateStringproperties;// 自定义属性}// 注册模型 - 端点注册@Stream(name="endpoint_inventory",scopeId=0)publicclassEndpointInventoryextendsRegisterSource{@Column(columnName="endpoint_id")@IDColumnprivateintendpointId;// 端点ID@Column(columnName="service_id")privateintserviceId;// 所属服务ID@Column(columnName="name")privateStringname;// 端点名称@Column(columnName="detect_point")privateintdetectPoint;// 探测点类型}// 注册模型 - 网络地址注册@Stream(name="network_address_inventory",scopeId=0)publicclassNetworkAddressInventoryextendsRegisterSource{@Column(columnName="address_id")@IDColumnprivateintaddressId;@Column(columnName="name")privateStringname;}

四、明细模型(Record Model)

4.1 解决什么问题?

明细模型存储的是"原始证据"——每一条Trace、每一个Span的完整信息。当需要排查问题时,这些"现场的细节"是必不可少的。

4.2 明细模型示例

// 明细模型 - Segment记录(一个Trace的完整记录)@Stream(name="segment",scopeId=1)publicclassSegmentRecordextendsRecord{@Column(columnName="segment_id")@IDColumnprivateStringsegmentId;// Segment唯一ID@Column(columnName="trace_id")privateStringtraceId;// 所属Trace ID@Column(columnName="service_id")privateintserviceId;// 服务ID@Column(columnName="service_instance_id")privateintserviceInstanceId;// 实例ID@Column(columnName="endpoint_id")privateintendpointId;// 端点ID@Column(columnName="start_time")privatelongstartTime;// 开始时间@Column(columnName="end_time")privatelongendTime;// 结束时间@Column(columnName="latency")privateintlatency;// 延迟(ms)@Column(columnName="is_error")privateintisError;// 是否出错@Column(columnName="data_binary")privatebyte[]dataBinary;// Span列表的二进制数据@Column(columnName="version")privateintversion;// 协议版本}// 明细模型 - 告警记录@Stream(name="alarm_record",scopeId=1)publicclassAlarmRecordextendsRecord{@Column(columnName="alarm_record_id")@IDColumnprivateStringid;@Column(columnName="rule_name")privateStringruleName;@Column(columnName="scope")privateintscope;@Column(columnName="scope_id")privateintscopeId;@Column(columnName="name")privateStringname;@Column(columnName="alarm_message")privateStringalarmMessage;@Column(columnName="start_time")privatelongstartTime;@Column(columnName="value")privateStringvalue;}

五、指标模型(Metrics Model)

指标模型是SkyWalking当之无愧的"主角",因为Dashboard上的每个图表背后都有指标模型在支撑。

// 指标模型 - 服务响应时间指标@Stream(name="service_resp_time",scopeId=2)@MetricsExtension(supportDownSampling=true)publicclassServiceRespTimeMetricsextendsMetricsimplementsLongValueHolder{@Column(columnName="entity_id")@IDColumnprivateStringentityId;@Column(columnName="value",dataType=Column.ValueDataType.COMMON_VALUE,function=Function.Avg)privatelongvalue;// 平均值@Column(columnName="total")privatelongtotal;// 调用次数@Column(columnName="sum")privatelongsum;// 延迟总和@Column(columnName="time_bucket")privatelongtimeBucket;// 时间窗口}// 指标模型 - 百分位响应时间指标@Stream(name="service_percentile",scopeId=2)publicclassServicePercentileMetricsextendsMetricsimplementsMultiLongValuesHolder{@Column(columnName="entity_id")@IDColumnprivateStringentityId;@Column(columnName="p50")privatelongp50;@Column(columnName="p75")privatelongp75;@Column(columnName="p90")privatelongp90;@Column(columnName="p95")privatelongp95;@Column(columnName="p99")privatelongp99;@Column(columnName="total")privatelongtotal;@Column(columnName="time_bucket")privatelongtimeBucket;}

六、采样模型(Sampling Model)

采样模型存储的是"按需抓取"的数据——不是所有数据都存,只有满足特定条件时才记录。

// 采样模型 - 慢查询记录@Stream(name="top_n_database_statement",scopeId=3)publicclassTopNDatabaseStatementextendsRecord{@Column(columnName="trace_id")privateStringtraceId;@Column(columnName="id")@IDColumnprivateStringid;@Column(columnName="statement")privateStringstatement;// SQL语句@Column(columnName="latency")privatelonglatency;// 执行时间@Column(columnName="service_id")privateintserviceId;@Column(columnName="time_bucket")privatelongtimeBucket;}// 采样模型 - Profile堆栈快照@Stream(name="profile_task_segment_snapshot",scopeId=3)publicclassProfileThreadSnapshotRecordextendsRecord{@Column(columnName="task_id")privateStringtaskId;@Column(columnName="segment_id")privateStringsegmentId;@Column(columnName="dump_time")privatelongdumpTime;@Column(columnName="stack_binary")privatebyte[]stackBinary;// 线程堆栈数据}

七、注解驱动的存储字段编排

SkyWalking使用一套优雅的注解体系来声明字段如何存储:

// @Column注解——定义字段的存储行为public@interfaceColumn{// 存储中的列名StringcolumnName();// 值的数据类型ValueDataTypedataType()defaultValueDataType.COMMON_VALUE;// 如果是指标,使用的聚合函数Functionfunction()defaultFunction.Avg;// 是否可搜索booleanmatchQuery()defaultfalse;// 是否可全文搜索booleancontentQuery()defaultfalse;// 存储类型StorageTypestorageType()defaultStorageType.COMMON;enumValueDataType{COMMON_VALUE,// 普通数值CONSTANT_VALUE,// 常数值NOT_VALUE,// 非数值SAMPLED_RECORD,// 采样记录值LABELED_VALUE// 标签值}}// @IDColumn——标记主键字段public@interfaceIDColumn{}// @Stream——定义存储流(索引/表)public@interfaceStream{Stringname();// 流名称intscopeId();// 范围IDClassbuilder();// Builder类Classprocessor();// Processor类}// @MetricsExtension——指标扩展属性public@interfaceMetricsExtension{booleansupportDownSampling()defaulttrue;booleansupportUpdate()defaultfalse;booleantimeRelativeID()defaulttrue;}

八、四种模型的读写流程

+-------------------+ | Agent探针 | +--------+----------+ | gRPC上报 | v +--------+----------+ | SegmentParser | | 解析原始Segment | +--------+----------+ | +------------+------------+ | | v v +-----------+-----------+ +----------+--------+ | 注册模型处理 | | 明细模型处理 | | | | | | 1. 检查服务名是否已注册 | | 1. 创建SegmentRecord| | 2. 检查端点名是否已注册 | | 2. 记录latency等字段| | 3. 未注册则生成新ID | | 3. 写入存储 | | 4. 将ID映射放入缓存 | | | +-----------+-----------+ +---------+---------+ | | v v +-----------+-----------+ +---------+---------+ | 后续使用 | | 指标模型处理 | | | | | | 其他模型引用注册ID | | 1. 拿到服务ID | | (如指标模型中的entityId)| | 2. 解析OAL指标定义 | | | | 3. 执行聚合计算 | | | | 4. 写入指标数据 | +-----------------------+ +---------+---------+ | (慢调用/异常时) | v +---------+---------+ | 采样模型触发 | | | | 1. 判断是否满足条件 | | 2. 抓取堆栈/慢SQL | | 3. 存储采样数据 | +--------------------+ 图2:四种模型的协同工作流程

九、存储模型的时间窗口分层

指标模型的数据按时间粒度分为三层存储:

原始数据写入(分钟级) | v [分钟窗口] ── TTL: 默认90天 | | 每小时聚合一次 v [小时窗口] ── TTL: 默认45天 | | 每天聚合一次 v [天窗口] ── TTL: 默认180天 查询时自动选择最合适的粒度: - 查询最近1小时 → 使用分钟粒度数据 - 查询最近24小时 → 使用小时粒度数据 - 查询最近30天 → 使用天粒度数据

十、总结

SkyWalking的四大存储模型是一种"根据数据使用方式分层存储"的经典设计:

  • 注册模型:名→ID映射,体量最小,永久存储
  • 明细模型:原始证据,体量最大,较短TTL
  • 指标模型:聚合结果,体量中等,是Dashboard的数据源
  • 采样模型:按需抓取,体量按规则决定

这套模型体系让SkyWalking既能在Dashboard上秒级响应查询,又能完整保留问题排查所需的原始数据,实现了"快"和"全"的完美平衡。

下一篇文章,我们将深入注册模型和明细模型的细节实现。


上一篇【第48篇】OAP集群规划实战:从理论到生产的容量设计
下一篇【第50篇】注册模型与明细模型:名字的管家和现场的记录官


http://www.cnnetsun.cn/news/3362951.html

相关文章:

  • 【SkyWalking从入门到精通】第48篇:OAP集群规划实战:从理论到生产的容量设计
  • TMC7300驱动有刷直流电机的PID控制优化方案
  • ADP5350与MSP432P401R电源管理方案详解
  • C++条件变量std::condition_variable详解:从原理到线程安全队列实战
  • STM32驱动压电蜂鸣器实现高音量警报系统设计
  • Agentic Harness Engineering:编码智能体的动态工具箱管理技术
  • C++项目集成Snappy压缩库:从源码编译到工程实践全指南
  • UKEY硬件安全设备实战指南:从原理到代码签名与数据加密应用
  • 深入Google Test与Google Mock源码:从原理到实战的单元测试进阶指南
  • FLUX.2-klein-base-4B-bf16商业应用案例:10个成功实践分享
  • GHelper终极指南:彻底告别臃肿官方软件,用轻量神器掌控华硕笔记本性能
  • PHP性能优化终极指南:缓存策略与字节码加速全解析 | PHP The Right Way
  • 【Kali】从零到一:VMware虚拟机环境下的Kali Linux完整安装与初始化配置指南
  • Linux基础IO操作与文件描述符详解
  • Unity多语言插件Localization Package核心模块与实战指南
  • Windows系统垃圾清理与优化工具使用指南
  • C++内存泄漏全解析:从原理到实战,智能指针与RAII的防御之道
  • MAS-CN项目架构解析:开源Windows激活工具的代码实现原理
  • RePKG终极指南:3分钟掌握Wallpaper Engine壁纸资源提取与转换
  • Python车载自动化实战:从CAN通信到pytest测试框架集成
  • 终极指南:3步掌握ClosedXML .NET Excel处理库
  • 3个场景揭秘:为什么安全研究员都爱用pwndbg调试工具?
  • 3步掌握RWTS-PDFwriter:macOS用户的终极PDF打印解决方案
  • RePKG终极指南:5分钟掌握Wallpaper Engine壁纸资源提取技巧
  • 终极指南:如何在Windows 11上搭建Android应用开发环境?Windows Subsystem for Android深度解析
  • 手把手实现可调试的遗传算法:以子集和问题为例
  • 【数据结构】链表进化论:C语言实现带哨兵位的双向循环链表,解锁O(1)删除的奥秘
  • BepInEx框架:Unity游戏模组加载原理与三步安装实战指南
  • ChatGPT Plus / Pro 与 Codex:多智能体系统真正困难的不是分工,而是如何形成一致决策
  • 工业信号采集抗干扰方案:FOD4216光耦与PIC18F24K50应用