乐趣区

关于prometheus:高基数和prometheus中判定高基数的三种方法

写在最前

prometheus 断定高基数的三种办法

  1. prometheus tsdb 的统计接口
  2. prometheus 能够依据 query_log 中的 queryPreparationTime 来定位
  3. prometheus 通过 count by 统计

什么是高基数 high-cardinality

基数狭义上是指汇合中值的数量

在数据库畛域,基数是指数据库的特定列或字段中蕴含的惟一值的数量。

工夫序列数据集的基数通常由每个独自的索引列的基数的叉积定义

高基数示例:工业物联网

  • 设想一下一个 IoT 场景,其中某个采石场中有大量的重型设施在开采岩石,破碎岩石和分选岩石。
  • 假如有 10,000 件设施,每个设施带有 100 个传感器,运行 10 个不同的固件版本,散布在 100 个站点中:
timestamp  | temper | mem_f | equipm | senso | firmwar   | sit  | (lat,long)
           | ature  | ree   | ent_id | r_id  | e_version | e_id |
--------------------+-------+--------+-------------------+------+-----------
2019-04-04 | 85.2   | 10.2  | 1      | 98    |  1.0      | 4    | (x,y) 
09:00:00   |        |       |        |       |           |      | 
2019-04-04 | 68.8   | 16.0  | 72     | 12    |  1.1      | 20   | (x1,y1)
09:00:00   |        |       |        |       |           |      |     
2019-04-04 | 100.0  | 0.0   | 34     | 58    |  2.1      | 55   | (x2,y2) 
09:00:00   |        |       |        |       |           |      |      
2019-04-04 | 84.8   | 9.8   | 12     | 75    |  1.4      | 81   | (x3,y3)
09:00:00   |        |       |        |       |           |      |   
2019-04-04 | 68.7   | 16.0  | 89     | 4     |  2.1      | 13   | (x4,y4)
09:00:00   |        |       |        |       |           |      | 
...        |        |       |        |       |           |      | 
      
  • 而后,此数据集的最大基数变为 10 亿[10,000 x 100 x 10 x 100]。
  • 当初,假如设施也能够挪动,并且咱们想存储准确的 GPS 地位(纬度,经度),并将其用作索引的元数据进行查问。因为(lat,long)是一个间断字段(与诸如 equipment_id 之类的离散字段绝对),所以通过在地位上建设索引,此数据集的最大基数当初无限大(无界)。

高基数查问

  • 艰深的说就是返回的 series 或者查问到的 series 数量过多
  • 查问体现进去返回工夫较长,对应调用服务端资源较多的查问
  • 数量多少算多 10w~100w
  • 个别咱们定义在 1 小时内的 range_query 响应工夫超过 3 秒 则认为较重了

prometheus 断定高基数的三种办法

  1. prometheus tsdb 的统计接口
  2. prometheus 能够依据 query_log 中的 queryPreparationTime 来定位
  3. prometheus 通过 count by 统计

办法一 tsdb 的统计接口

  • http://192.168.43.114:9090/ts…
  • 接口地址 /api/v1/status/tsdb
  • 是基于内存中的倒排索引 算最大堆取 top10
  • 10 个最多的 metric_name 排序

    seriesCountByMetricName: [{name: "namedprocess_namegroup_memory_bytes", value: 245},…]
    0: {name: "namedprocess_namegroup_memory_bytes", value: 245}
    1: {name: "namedprocess_namegroup_states", value: 245}
    2: {name: "mysql_global_status_commands_total", value: 148}
    3: {name: "namedprocess_namegroup_context_switches_total", value: 98}
    4: {name: "namedprocess_namegroup_cpu_seconds_total", value: 98}
    5: {name: "node_scrape_collector_success", value: 80}
    6: {name: "node_scrape_collector_duration_seconds", value: 80}
    7: {name: "namedprocess_namegroup_threads_wchan", value: 73}
    8: {name: "namedprocess_namegroup_thread_cpu_seconds_total", value: 66}
    9: {name: "namedprocess_namegroup_thread_io_bytes_total", value: 66}
  • 思考采集器如果不是 prometheus 怎么办?

    • 比方 m3db 没有提供高基数查问的接口
  • 源码解析

    // Stats calculates the cardinality statistics from postings.
    func (p *MemPostings) Stats(label string) *PostingsStats {
      const maxNumOfRecords = 10
      var size uint64
    
      p.mtx.RLock()
    
      metrics := &maxHeap{}
      labels := &maxHeap{}
      labelValueLength := &maxHeap{}
      labelValuePairs := &maxHeap{}
      numLabelPairs := 0
    
      metrics.init(maxNumOfRecords)
      labels.init(maxNumOfRecords)
      labelValueLength.init(maxNumOfRecords)
      labelValuePairs.init(maxNumOfRecords)
    
      for n, e := range p.m {
          if n == "" {continue}
          labels.push(Stat{Name: n, Count: uint64(len(e))})
          numLabelPairs += len(e)
          size = 0
          for name, values := range e {
              if n == label {metrics.push(Stat{Name: name, Count: uint64(len(values))})
              }
              labelValuePairs.push(Stat{Name: n + "=" + name, Count: uint64(len(values))})
              size += uint64(len(name))
          }
          labelValueLength.push(Stat{Name: n, Count: size})
      }
    
      p.mtx.RUnlock()
    
      return &PostingsStats{CardinalityMetricsStats: metrics.get(),
          CardinalityLabelStats:   labels.get(),
          LabelValueStats:         labelValueLength.get(),
          LabelValuePairsStats:    labelValuePairs.get(),
          NumLabelPairs:           numLabelPairs,
      }
    }

办法二 query_log

  • 能够依据 log 中的 queryPreparationTime 来定位

办法三 通过 count 统计

topk(5,count({__name__=~".+"}) by(__name__) > 100 )
  • scrape_samples_scraped 能够阐明 job 的 instance 维度 sample 数量,也可能定位
退出移动版