1.后台概览框架Dashboard是携程内部历史悠久的自研监控产品。定位为企业级Metrics监控场景,主要为用户提供DefineMetrics接入,并提供基于此的实时数据分析和视图呈现的面板服务,为各种系统级性能数据和业务提供可定制的仪表盘基于时间序列的指标数据。它还可以提供灵活的数据采集接口、分布式大容量存储和灵活的呈现方式。由于时间较早,当时业界还没有像样的TSDB产品。与Prometheus类似,InfluxDB是后起之秀,所以Dashboard选型主要使用HBase来存储Metrics数据。另外,TSDB是基于HBase实现的,解决了一些HBase的热点问题。同时将部分查询聚合转移到HBase中,优化其查询性能。目前看来整体方案还是依赖于HBase/HDFS,还是有点重。近年来,随着携程监控一体机产品的推出。对于内部Metrics存储的统一也有新的要求。由于Dashboard查询存在的诸多问题以及Metrics的统一目标,我们决定对Dashboard现有的HBase存储方案进行替换升级,在Metrics场景下提供统一的查询层API。2.总体架构Dashboard产品主要分为六个组件,包括dashboard-engine、dashboard-gateway、dashboard-writer、dashboard-HBasestorage、dashboard-collector、dashboard-agent。目前实时写入的数据行数为每分钟6亿条,架构图如下:dashboard-engine是一个查询引擎。dashboard-gateway是提供给用户的查询接口。dashboard-writer是用于将数据写入HBase的组件。dashboard-collector是一个基于Netty的Metrics数据收集服务器。Dashboard-agent是用户管理的客户端,支持sum、avg、max、min聚合方式。dashboard-HBase是基于HBase实现的Metrics存储组件。产品主要特点如下:支持精确到分钟级的时序数据存储。单个指标数据可以支持多个标签。该显示提供任何形式的视图,并且可以根据标签灵活分组。3、目前存在的问题基于HBase的Metrics存储方案虽然具有良好的扩展性和较高的吞吐量,但随着时间的发展,它不再是最优的TSDB方案。可以归纳为以下痛点。TSDB场景,查询速度慢,整体性能不如专业TSDB。HBase热点问题很容易影响数据写入。HBase技术栈运维操作非常繁重。采用自研协议,不支持行业标准Prometheus协议,无法与内部一体化监控产品很好集成。4.替换难系统写入数据量大,每分钟6亿条记录。仪表板数据缺乏治理。很多不合理的高维指标数据和日志数据,经过统计,总体基数在几千亿级。这对TSDB不友好,这部分需要写成程序进行治理。如图2,top20基数统计,很多指标有上亿个基数。Dashboard系统由来已久,内部程序调用较多。替换需要对用户透明。五、更换升级计划从上述架构来看,我们目前正在更换dashboard-writer和dashboard-HBase这两个核心组件。为了用户的顺利迁移,其他组件略有改动,新的查询API可以接入dashboard-engine组件替换升级成功。对于用户端,查询接口dashboard-gateway和管理客户端dashboard-agent仍然是原来的模式,所以整个替换方案对用户是透明的。具体如下:1、dashboard-HBase升级为dashboard-vm存储,HBase方案替换为VictoriaMetrics+ClickHouse混合存储方案:VictoriaMetrics是兼容主流Prometheus协议的TSDB,TSDB场景下查询效果好,所以会访问大部分TSDB数据。元数据服务基于ClickHouse提供,主要用于接口的即席查询服务。原来这部分元数据存储在HBase中,新的方案使用ClickHouse来存储。Metadata主要存储三个结构:measurement列表,measurement-tagKey列表,measurement-tagKey-tagValue列表。目前,在ClickHouse中创建了一个表来存储这些元数据。本地表结构为:CREATETABLEhickwall.downsample_mtv(`timestamp`DateTime,`metricName`String,`tagKey`String,`tagValue`String,`datasourceId`UInt8DEFAULT40)ENGINE=ReplicatedMergeTree('/clickhouse/tables/hickwall_cluster-{shard}/downsample_mtv','{replica}')PARTITIONBYtoYYYYMMDD(timestamp)ORDERBY(timestamp,metricName,tagKey)TTLtimestamp+toIntervalDay(7)SETTINGSindex_granularity=8192分布式表结构为:CREATETABLEhickwall.downsample_mtv__dt(`timestamp`DateTime,`metricName`String,`tagKey`String,`tagValue`String,`datasourceId`UInt8DEFAULT40)ENGINE=Distributed(hickwall_cluster,hickwall,downsample_mtv,rand())ClickHouse存储少量日志类型数据由于长期缺乏一些治理,Dashboard也存储了一些日志类型的数据。这类数据是基数大但数据量小的数据,不适合存储在VictoriaMetrics中。为了实现所有数据的透明迁移,这部分数据通过白名单配置进行评估存储在ClickHouse中。需要为访问的每个日志类型指标创建表和字段。目前的做法是按照BU维度建表,为索引标签创建字段。考虑到访问的日志类索引数量较少,表的字段数会相对可控。下面以ticketFLT的表结构为例。2.Dashboard-writer升级为Dashboard-vmwriter。Dashboard-collector会将所有数据导流到Kafka。Dashboard-vmwriter的工作流程大致是消费Kafka->数据处理->数据写入存储。Dashboard-vmwriter主要实现了以下几个核心功能:Metrics元数据提取功能,负责提取measurement、tagKey、tagValue写入到ClickHouse的mtv本地表中。这个元数据存储主要依赖Redis(实时写入)和ClickHouse(查询)。索引预聚合功能用于加快查询速度。对接公司内部配置中心,下发预聚合配置。配置格式如下。下面的配置会针对ClusterName和appid两个维度的组合生成Credis预聚合指标。{"metricName":"credis.java.latency","tagNames":["ClusterName","appid"]}配置下发后,Dashboard-vmwriter会自动聚合一个预聚合的指标,存储在VictoriaMetrics中,指标命名规则为hi_agg.{measurement}_{tag1}_{tag2}_{aggregatefield}。同理,查询层API也会读取相同的预聚合配置,来判断查询的是预聚合指标还是原始指标。默认情况下,所有的measurement维度都开启了预聚合配置,因为在TSDB实现中,一次查询measurement数据会扫描所有timeseries,查询开销非常大,所以直接查看pre-aggregation比较合理这部分的聚合测量。数据治理:异常数据的自动检测和取缔,目前主要涉及以下几个方面:1)基于HyperLogLog算法统计度量级别的基数,如果度量的基数非常大,比如超过500万,那么一些标签尺寸将被丢弃。2)基于Redis和内存缓存统计measurement-tagKey-tagValue的基数。如果某个tagValue增长过快,则丢弃这个tag的维度,记录并丢弃这样的埋点。Redis主要使用set集合,key的名字是{measurement}_{tagKey},成员是[tagValue1,tagValue2,...,tagValueN],主要是通过sismember判断成员是否存在,sadd补充members,scard来判断关键的成员数量。编写程序会先检查本地内存Cache中是否存在该Key成员,如果不存在则去Redis中查找。Redis的qps是可控的。本地Cache基于LRU淘汰策略,本地内存可控。整个过程在写入时实时进行,也能保证数据的时效性和高性能。写入Redis的元数据也会实时增量同步到ClickHouse的mtv表,这样用户界面也可以实时查询元数据。数据。3)数据以高性能写入。整个消费的线程模型大致是一个进程,一个kafka消费线程,n个数据处理线程,m个数据写入线程。线程通过队列进行通信,以促进同一进程内的数据预聚合。假设配置了4个数据处理线程,那么会根据measurement做hash,分成4个bucket进行处理,这样同一个measurement的数据会在一个bucket中处理,也方便后续的索引pre-聚合处理。privateintcomputeMetricNameHash(byte[]metricName){inthash=Arrays.hashCode(metricName);hash=(hash==Integer.MIN_VALUE?0:hash);returnhash;}byte[]metricName=metricEvent.getName();hash=computeMetricNameHash(metricName);buckets[Math.abs(hash)%bucketCount].add(metricEvent);经过程序嵌入点的计算,一般情况下,整体链路的数据写入延迟控制在1s以内,百毫秒左右。3、Metrics统一查询层合约兼容Dashboard原有的查询协议,同时也支持标准的prometheus协议。在实现上,封装了VictoriaMetics+ClickHouse的统一查询,支持元数据管理、预聚合管理、限流、rollup策略等。查询层主要提供以下四个核心接口。数据接口:根据measurement、tagKey、tagValue返回时序数据,数据源为VictoriaMetrics。测量接口:返回限制数量的测量列表,数据源为ClickHouse。Measurement-tagKey接口:返回指定measurement的tagKey列表,数据源为ClickHouse。Measurement-tagKey-tagValue接口:返回指定measurement和tagkey的tagValues列表,数据源为ClickHouse。下面第一张图是新的存储架构,第二张图是VictoriaMetrics自己的架构。需要注意的是,整个数据写入层是单个机房写入的存储集群,是一个完整的单元结构。顶层通过统一的数据查询层汇总多个机房的数据进行聚合输出。在可用性方面,任何一个单机房发生故障,只会影响单机房内的数据。6、替换前后效果对比1)替换后查询耗时比MAX、AVG、STD增加了近4倍。查询时间大多在10-50ms之间。相比之前的HBase频繁查询超时,整体查询稳定性要好很多,如图6和图7所示。2)写入稳定性得到提升,彻底解决了HBase热点导致的数据积压。3)替换后支持更多优秀的特性,如指标的逻辑计算、同比比较、模糊匹配等都可以基于promQL实现。7、未来规划1)统一查询层访问所有Metrics数据。除了Dashboard,目前里面还有HickWall。Cat有大量的Metrics数据没有接入统一查询层。目前是直连openrestry+VictoriaMetrics。在做了一些简单的查询逻辑之后,这个方案以后会接入到统一查询层,这样可以在内部提供统一的元数据管理,预聚合策略等,实现统一的Metrics架构。2)提供统一的写入层。整体Metrics目前接近每秒1亿。这种写入目前是基于Kafka消费和进入存储的方式。内部写入由多个应用程序处理。如果有一个统一的写入层可以实现统一的写入逻辑,也可以和查询层的查询策略联动,减少冗余构建。3)Metrics的存储统一层提供了一个很好的模型,内部日志存储层的统一也在如火如荼的进行中,也会往这个方向发展。
