metric driven (3) – basic terms and concepts

在学习metric之前,需要对metric领域涉及的基本概念有个初步的整体认识:

一 统计学指标:
以下面一段Cassandra的metric做示例:
Cassandra Metrics
Timer:
name=requests, count=171396, min=0.480973, max=4.228569, mean=1.0091534824902724, stddev=0.3463516148968051, median=0.975965, p75=1.1458145, p95=1.4784138999999996, p98=1.6538238999999988, p99=2.185363660000002, p999=4.22124273, mean_rate=0.0, m1=0.0, m5=0.0, m15=0.0, rate_unit=events/millisecond, duration_unit=milliseconds

1.1 集中量
(1)最大值、最小值、平均数:容易理解,不做赘述,即例子中的min=0.480973, max=4.228569,mean=1.0091534824902724,表明最小、最大和平均响应时间;
(2)中位数:一个样本、种群或概率分布中的一个数值,用它可以讲所有数据划分为相等的上下两部分。对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果数据总数是偶数个,通常取最中间的两个数值的平均数作为中位数。如示例中的median=0.975965,代表所有响应时间中最中间的那个数字,可见和平均数(mean=1.0091534824902724)并不相等。

1.2 差异量
(1)全距:将上述的最大值减去最小值即为全距,代表变化的范围,数值越大,说明数据越分散。例子中即为3.747596(max-min),数据跨度并不大;
(2)方差:每个样本值与全体样本值的平均数之差的平方值的平均数,用来度量随机变量和其数学期望(即均值)之间的偏离程度. 数值越小,表明数据分布越集中,例子中即为stddev=0.3463516148968051,表明数据相对很集中。

1.3 地位量
(1)分位数:分位数是将总体的全部数据按大小顺序排列后,处于各等分位置的变量值。例如上面的p95, p999都是这种概念。例如p999=4.22124273,代表99.9%的请求响应时间不大于4.22124273ms;
(2)四分位数:如果分成四等分,就是四分位数;八等分就是八分位数等。四分位数也称为四分位点,它是将全部数据分成相等的四部分,其中每部分包括25%的数据,处在各分位点的数值就是四分位数。四分位数有三个,第一个四分位数就是通常所说的四分位数,称为下四分位数,第二个四分位数就是中位数,第三个四分位数称为上四分位数,分别用Q1、Q2、Q3表示 [1] 。
第一四分位数 (Q1),又称”较小四分位数”,等于该样本中所有数值由小到大排列后第25%的数字。
第二四分位数 (Q2),又称“中位数”,等于该样本中所有数值由小到大排列后第50%的数字,即median=0.975965。
第三四分位数 (Q3),又称“较大四分位数”,等于该样本中所有数值由小到大排列后第75%的数字, 例如p75=1.1458145

二 相关的术语

2.1 QPS/TPS/PV/

QPS: Query Per Second每秒的查询次数;
TPS: Transaction per second 每秒的业务量;
PV: page view: 即文章的浏览量,常用于web服务器页面的统计。

2.2 度量类型

(1) Counter
代表一个递增的值,例如请求数,在server正常运行时,只会增加不会减少,在重启时会归0,例如:Cassandra的metric中的下列指标,都是只增不减,重启归0的值。
Counters:
name=client-timeouts, count=0
name=connection-errors, count=0
name=ignores, count=0
name=ignores-on-write-timeout, count=0
name=other-errors, count=0
name=read-timeouts, count=0
name=retries, count=0
name=speculative-executions, count=0
name=unavailables, count=0
name=write-timeouts, count=0

(2) Gauge
代表一个变化的值,表明是个瞬间的值。例如温度等可变化因素,下例中的Cassandra的metric值都是可以变化:

GAUGE:
name=blocking-executor-queue-depth, value=0
name=connected-to, value=7
name=executor-queue-depth, value=0
name=known-hosts, value=36
name=open-connections, value=8
name=reconnection-scheduler-task-count, value=0
name=task-scheduler-task-count, value=1
name=trashed-connections, value=0

(3) Histogram
即直方图: 主要使用来统计数据的分布情况,最大值、最小值、平均值、中位数,百分比(75%、90%、95%、98%、99%和99.9%)

count=171396, min=0.480973, max=4.228569, mean=1.0091534824902724, stddev=0.3463516148968051, median=0.975965, p75=1.1458145, p95=1.4784138999999996, p98=1.6538238999999988, p99=2.185363660000002, p999=4.22124273

注意关于百分比的相关参数并不是实际整个server运行期间的百分比,而是基于一定的统计方法来计算的值,试想,不可能存储所有的请求的数据,只能从时间和空间两个维度来推算。

例如:
com.codahale.metrics.SlidingWindowReservoir

package com.codahale.metrics;</code>

import static java.lang.Math.min;

/**
* A {@link Reservoir} implementation backed by a sliding window that stores the last {@code N}
* measurements.
*/
public class SlidingWindowReservoir implements Reservoir {
private final long[] measurements;
private long count;

/**
* Creates a new {@link SlidingWindowReservoir} which stores the last {@code size} measurements.
*
* @param size the number of measurements to store
*/
public SlidingWindowReservoir(int size) {
this.measurements = new long[size];
this.count = 0;
}

@Override
public synchronized int size() {
return (int) min(count, measurements.length);
}

@Override
public synchronized void update(long value) {
measurements[(int) (count++ % measurements.length)] = value;
}

@Override
public Snapshot getSnapshot() {
final long[] values = new long[size()];
for (int i = 0; i &lt; values.length; i++) {
synchronized (this) {
values[i] = measurements[i];
}
}
return new Snapshot(values);
}
}

(4) Meters
用来度量某个时间段的平均处理次数(request per second),每1、5、15分钟的TPS。比如一个service的请求数,统计结果有总的请求数,平均每秒的请求数,以及最近的1、5、15分钟的平均TPS:

mean_rate=0.0, m1=0.0, m5=0.0, m15=0.0, rate_unit=events/millisecond, duration_unit=milliseconds

(5) Timer
主要是用来统计某一块代码段的执行时间以及其分布情况,具体是基于Histograms和Meters来实现的,所以结果是上面两个指标的合并结果。

三 Metric处理的一些术语:

3.1 Event time / Ingestion time / Processing Time
在流程序中支持不同概念的时间。以读取一条存储在kafka中的数据为例,存在多个时间字段:

Tue Jul 03 11:02:20 CST 2018 offset = 1472200, key = null, value = {"path":"/opt/application/logs/metrics_07012018_0.24536.log","component":"app","@timestamp":"2018-07-01T03:14:53.982Z","@version":"1","host":"10.224.2.116","eventtype":"metrics","message":{"componentType":"app","metricType":"innerApi","metricName":"demo","componentAddress":"10.224.57.67","featureName":"PageCall","componentVer":"2.2.0","poolName":"test","trackingID":"0810823d-dc92-40e4-8e9a-18774d549e21","timestamp":"2018-07-01T03:14:53.728Z"}}

Event time: 是事件发生的时间,而不考虑事件记录通过什么系统、什么时候到达、以什么样的顺序到达等因素。上例中2018-07-01T03:14:53.728Z即为server日志产生的时间;相当于邮寄快递时的邮寄时间。

Ingestion time:即摄入时间,是事件进入Metric系统的时间,在源操作中每个记录都会获得源的当前时间作为时间戳,当一个metric经过n条系统时,相对每条进入的数据,摄入时间就会存在多个。上例中的”@timestamp”:”2018-07-01T03:14:53.982Z”即为logstash获取这个server log的时间。相当于邮寄快递时,快递员的揽收时间。

Processing Time: 是处理系统开始处理某个事件的时间,上例中“Tue Jul 03 11:02:20 CST”为处理时间。相当于邮寄快递中的签收时间。

明确各种时间概率后,可见事件时间是最重要的。

3.2 tulbmimg window / hop window / session window

(1) Tumbling windows
Tumbling即翻跟头的意思,窗口不可能重叠的;在流数据中进行滚动,这种窗口不存在重叠,也就是说一个metric只可能出现在一个窗口中。

(2)Sliding Windows
是可能存在重叠的,即滑动窗口。

(3)session window
以上两种都是基于时间的,固定的窗口,还存在一种window: session window
当我们需要分析用户的一段交互的行为事件时,通常的想法是将用户的事件流按照“session”来分组。session 是指一段持续活跃的期间,由活跃间隙分隔开。通俗一点说,消息之间的间隔小于超时阈值(sessionGap),如果两个元素的时间戳间隔小于 session gap,则会在同一个session中。如果两个元素之间的间隔大于session gap,且没有元素能够填补上这个gap,那么它们会被放到不同的session中。

通过以上不同类别的基本指标和术语的了解,可以大体了解metric领域的一些知识,为以后的metric工作的进展做铺垫。

参考文献:
https://baike.baidu.com/item/%E4%B8%AD%E4%BD%8D%E6%95%B0
https://www.jianshu.com/p/68ab40c7f347
https://yq.aliyun.com/articles/64818
https://kafka.apache.org/11/documentation/streams/developer-guide/dsl-api.html#windowing
https://prometheus.io/docs/concepts/metric_types/
https://www.app-metrics.io/getting-started/metric-types/
http://www.cnblogs.com/nexiyi/p/metrics_sample_1.html

metric driven (2) – select metrics strategy

对metric方案的选择:

  • 功能性角度:
    单纯衡量metric方案,大多已经满足基本功能,但是除此之外,更需要考虑功能的完整性:
    (1) 是否支持硬件层次(CPU、Memory、Disk、Network等)的数据收集和展示;(2) 是否对常见服务有更轻便的支持。

市场上流行的服务都比较集中,例如数据库有oracle、mysql,缓存有memcached、redis等,服务器容器有tomcat,jetty等,消息中间件有rabbitmq、kafka。所以很多metric系统除了通用方案外,还额外对这些常见服务有更轻便的直接接入支持。

(3) 是否集成Alert功能

Metrics里面含有的数据越丰富可以做的事情也越多:
a. 根据主机metric,判断主机故障,例如磁盘是否快满了;
b. 根据错误信息判断是否当前存在故障;
c. 根据metric趋势,判断是否需要扩容;
d. 根据用户行为信息判断是否存在恶意攻击,

当判断出这些信息,仅仅展示是不够的,更应该是提供预警和报警功能,以立马能够解决。同时报警的通知方式是否多样化(邮件、电话、短信、其他及时通信系统的集成)或者进行了分级(轻重缓解不同,不同方式)。

有了更丰富的功能,则避免多种方案的东拼西凑,有利于一体化。

  • 扩展性角度:
    (1) 容量是否具有可扩容性:
    当数据量小时,传统的Sql数据库甚至excel、csv都能存储所有的历史metric数据,并能满足查询等需求,但是除非可预见业务量永不会有突破,否则初始调研时,就应该考虑容量可扩展的方案。例如influxdb单机版是免费的,但是想使用集群模式的时候就变成了收费模式。所以在不喜欢额外投资,只热衷开源方案的企业,长远计划时则不需要选择这类产品。

(2) 切换新方案或者新增多层方案时,方案的可移植性:
很少有一种metric系统能满足所有需求,特别是定制化需求比较多的时候,而对于初创公司而言,可能更换metric系统更为频繁,所以假设选择的方案本身具有强耦合性,不具有可移植性时,就会带来一些问题:
a. 并存多种metric系统,每种方案都对系统资源有所占用

例如方案A通过发http请求,方案B通过写日志,方案C通过直接操作数据库。最后系统本身变成了metric系统的战场。

b. 切换新老metric系统时,需要做的工作太多。

参考问题a,每种方案的方式都不同,例如使用new relic时,需要的是绑定一个new relic jar,根据这个jar定制的规则,不见得适合其他的metric方案,例如influxdb.所以迁移时,不仅要重新修改代码,甚至修改数据结构。
所以方案本身的扩展性不仅体现在本身容量要具有可扩展性,还在于方案是否容易切换或者与其他方案并存,并与业务系统解耦,所以在实际操作时,可能需要加入一个中间层去解耦,例如常见的ELK增加一个kafka来解耦和隔离变化。

  • 技术性能角度:

1. Invasive->Non-invasive
从技术角度看,选择的metric方案本身是否具有侵入性是需要考虑的第一要素,一般而言,侵入性方案提供的功能更具有可定制性和丰富性,但是代价是对系统本身会有一定的影响,例如new relic,除了常用的功能外,还能根据不同的数据库类型显示slow query等,但是它采用的方案是使用java agent在class 被加载之前对其拦截,已插入我们的监听字节码。所以实际运行的代码已不单纯是项目build出的package。不仅在业务执行前后做一些额外的操作,同时也会共享同一个jvm内的资源:例如cpu和memory等。所以在使用new relic时,要求“开辟”更多点的内存,同时也要求给项目本身的影响做一定的评估。当然new relic本身也考虑到,对系统本身的影响,所以引入了“熔断器”来保护应用程序:

com.newrelic.agent.config.CircuitBreakerConfig:

	this.memoryThreshold = ((Integer) this.getProperty("memory_threshold", Integer.valueOf(20))).intValue();
	this.gcCpuThreshold = ((Integer) this.getProperty("gc_cpu_threshold", Integer.valueOf(10))).intValue();

com.newrelic.agent.circuitbreaker.CircuitBreakerService:

内存控制:

double percentageFreeMemory = 100.0D * ((double) (Runtime.getRuntime().freeMemory()
						+ (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory()))
						/ (double) Runtime.getRuntime().maxMemory());

CPU控制:

获取年老代:

GarbageCollectorMXBean lowestGCCountBean = null;
Agent.LOG.log(Level.FINEST, "Circuit breaker: looking for old gen gc bean");
boolean tie = false;
long totalGCs = this.getGCCount();
Iterator arg5 = ManagementFactory.getGarbageCollectorMXBeans().iterator();

while (true) {
	while (arg5.hasNext()) {
		GarbageCollectorMXBean gcBean = (GarbageCollectorMXBean) arg5.next();
		Agent.LOG.log(Level.FINEST, "Circuit breaker: checking {0}", gcBean.getName());
		if (null != lowestGCCountBean
				&amp;amp;amp;amp;&amp;amp;amp;amp; lowestGCCountBean.getCollectionCount() &amp;amp;amp;lt;= gcBean.getCollectionCount()) {
			if (lowestGCCountBean.getCollectionCount() == gcBean.getCollectionCount()) {
				tie = true;
			}
		} else {
			tie = false;
			lowestGCCountBean = gcBean;
		}
	}

	if (this.getGCCount() == totalGCs &amp;amp;amp;amp;&amp;amp;amp;amp; !tie) {
		Agent.LOG.log(Level.FINEST, "Circuit breaker: found and cached oldGenGCBean: {0}",
				lowestGCCountBean.getName());
		this.oldGenGCBeanCached = lowestGCCountBean;
		return this.oldGenGCBeanCached;
	}

	Agent.LOG.log(Level.FINEST, "Circuit breaker: unable to find oldGenGCBean. Best guess: {0}",
			lowestGCCountBean.getName());
	return lowestGCCountBean;
}
				
 

年老代GC时间占比计算:

	long currentTimeInNanoseconds = System.nanoTime();
	long gcCpuTime = this.getGCCpuTimeNS() - ((Long) this.lastTotalGCTimeNS.get()).longValue();
	long elapsedTime = currentTimeInNanoseconds - ((Long) this.lastTimestampInNanoseconds.get()).longValue();
	double gcCpuTimePercentage = (double) gcCpuTime / (double) elapsedTime * 100.0D;

2  Tcp -> Udp

使用tcp方式可靠性高,但是效率低,占用资源多,而使用udp可靠性低,但是效率高,作为metric数据本身,udp本身更适合,因为不是核心数据,丢弃少数也无所谓。

3 Sync->Async
同步方式直接影响业务请求响应时间,假设写metric本身消耗10ms,则请求响应也相应增加对应时间,但是使用异步时,不管是操作时间长的问题还是操作出错,都不会影响到业务流程。同时也容易做batch处理或者其他额外的控制。

4 Single-> Batch
对于metric数据本身,需要考察是否提供了batch的模式,batch因为数据内容更集中,从而可以减少网络开销次数和通信“头”格式的额外重复size等,同时batch方式也更容易采用压缩等手段来节约空间,毕竟metric数据本身很多字段key应该都是相同的。当然要注意的是过大的batch引发的问题,例如udp对size大小本身有限制,batch size过大时,操作时间会加长,是否超过timeout的限制。
以influxdb为例,使用udp模式的batch(小于64k)和single时,时间消耗延时如下表:

Mode\ms 300 500 800 1000
Single 34 85 111 173
batch 25 22 28 29
  • 总结

通过以上分析,可知选择一个metric系统不应该仅仅局限当前需求,而更应该从多个角度兼顾未来发展,同时对应用产生侵入性低、隔离变化、易于切换都是选择方案必须追求的要素,否则没有搞成想要的metrics却拉倒了应用则得不偿失。

 

redis analyst (9)- redis cluster issues/puzzles on producation

上篇文章枚举了诸多互联网公司分享的应用redis cluster中遇到的问题,本文罗列所在公司上线后出现的一些问题,也包括一些小的困惑。

问题1:出现auto failover

现象:监控redis半个多月的时候,偶然发现其中1台master自动触发failover。
原因:

1.1 查看出现的时间点的日志:

Node 912a1efa1f4085b4b7333706e546f64d16580761 reported node 3892d1dfa68d9976ce44b19e532d9c0e80a0357d as not reachable.

从日志看到node not reachable,首先想到2个因素:
(1)redis是单进程和单线程,所以有任何一个耗时操作都会导致阻塞时间过长,最终导致failover.
(2)网络因素;
首先排除了可能原因(1),因为从系统应用分析,并无任何特殊操作。都是最普通的操作且请求量很小,可能原因(2)不能排除。

1.2 查看出现问题时间点的系统资源应用情况:

查看了所有的常见指标,除了最近1分钟的load异常外,均正常,锁定原因为:系统load过高,达到7,导致系统僵死。其他节点认为这个节点挂了。

解决:考虑到所有其他指标:cpu/memory/disk等都正常,以及其他2台master一直也正常,业务量非常小,这种情况偶发,所以归结问题原因是这台虚拟机有问题,所以反馈问题并迁移虚拟机,迁移后system load一直平稳无问题。也没有出现failover.

困惑2:slave的ops远小于master的ops

现象:已知所有操作都是删除操作,并无查询操作。所以很好奇,为什么master和slave的ops差别这么大:前3台为master,达到100ops,相反slave不到10.

解惑:CRUD中,不见得只要是CUD就肯定会“传播”命令到slave,还有一个条件是必须“库”发生了改变。例如当前的业务中,处于测试阶段,所有主流操作都是删除操作,而且这些删除操作都是删除一个没有key的操作。所以并没有发生改变(即下文中dirty为0)。
计算dirty值:

    /* Call the command. */
    c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);
    // 保留旧 dirty 计数器值
    dirty = server.dirty;
    // 计算命令开始执行的时间
    start = ustime();
    // 执行实现函数
    c->cmd->proc(c);
    // 计算命令执行耗费的时间
    duration = ustime()-start;
    // 计算命令执行之后的 dirty 值
    dirty = server.dirty-dirty;

只有dirty值发生改变:

  
        // 如果数据库有被修改,即判断dirty,那么启用 REPL 和 AOF 传播
        if (dirty)
            flags |= (REDIS_PROPAGATE_REPL | REDIS_PROPAGATE_AOF);

        if (flags != REDIS_PROPAGATE_NONE)
            propagate(c->cmd,c->db->id,c->argv,c->argc,flags);

所有的操作,不见得都会改变dirty值:

void delCommand(redisClient *c) {
    int deleted = 0, j;

    for (j = 1; j < c->argc; j++) {

        // 尝试删除键
        if (dbDelete(c->db,c->argv[j])) {
            //改变server.dirty
            server.dirty++;
        }
    }

 }

问题3:socket timeout

现象:查看最近1周数据访问量,有4个socket timeout错误:


redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:202)
at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)
at redis.clients.jedis.Protocol.process(Protocol.java:151)
at redis.clients.jedis.Protocol.read(Protocol.java:215)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:265)
at redis.clients.jedis.Jedis.del(Jedis.java:197)
at redis.clients.jedis.JedisCluster$110.execute(JedisCluster.java:1205)
at redis.clients.jedis.JedisCluster$110.execute(JedisCluster.java:1202)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:120)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:31)
at redis.clients.jedis.JedisCluster.del(JedisCluster.java:1207)
at com.webex.dsagent.client.redis.RedisClientImpl.deleteSelectedTelephonyPoolsInfo(RedisClientImpl.java:77)
at sun.reflect.GeneratedMethodAccessor75.invoke(Unknown Source)

配置:
connectionTimeout=800
soTimeout=1000

BTW: 确实消耗了&gt;1000ms
componentType":"Redis","totalDurationInMS":1163

原因: 查看4个错误发生的时间,都发生在某天的一个时间点,且用key计算分段,也处于同一个机器上,所以归结到网络原因或虚拟机问题,无法重现。

linux commands note

1 查看文件大小,倒序按数字:

du -sh *|sort -n -r

-r 倒序 -n 按数字排

2 查看磁盘按G为单位:

df -h 

3 永久修改主机名:

vi /etc/sysconfig/network 

区别临时修改 hostname

4 设置自动启动:

修改/etc/rc.d/rc.local

5 sed替换

sed  -i 's/properties/property/g'  file.xml

-i为直接替换源文件,否则不直接替换源文件。

6 iptable转port:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 5601
iptables -t nat -L
iptables -t nat -F

7 根据ip反查dns:

# host 10.224.2.138

8 查DNS Server:

more /etc/resolv.conf 
nameserver 10.224.91.8

9 根据ip查信息:

nbtstat -A 10.154.144.205

metric driven (1) – aware spike erosion

现象

查看某个host的最近2周的load(last 1 minute)情况,可观察到尖峰时刻发生在5月3号,数值为0.167.

当试图查看具体什么时间点达到尖峰时(缩小时间范围),发现这个尖峰时刻的数据值不再是0.167,而是2:

从现象上表明:时间范围越大,数据的可信度越低,继续查看图1中的其他低点的尖峰,缩小时间范围后,竟然超过了第一尖峰,验证了这个设想。

原因

按常理,绘制图像的时候,直接将所有的数据展示出来即可,简单明了,但是实际操作中,对于一个时间范围较小的数据量较小时,并不存在问题,但是考虑到假设需要展示1年或者一个数据量超级大的数据时,存在两个问题:

  • 性能问题:显而易见,“点”越多,绘制的时间越长,性能也越差,可能等几分钟才能等到一幅图渲染完成。例如下图:如此稠密的图,展示就已经耗费很多时间,而实际上稠密的部分并无太多意义:

  • 显示问题:假设所用的电脑屏幕分辨率是1920 x 1080,同时X轴以时间为单位,当展示大范围的时间范围(例如1年)的时候,则每个肉眼有价值的点是“1年*365天*24小时/1920=4.56天”,而56天的数据范围,要不全部展示(则出现问题1),要不采取一定的策略,仅展示一部分。

所以结合现实(电脑分辨率)和性能问题(数据量太大),很多metric绘制采取了一定的策略。例如上文提及中观察到奇怪现象原因在于:

当展示一段时间范围内的数据时,采用的是平均值。所以当时间范围越大,尖峰越不准确,因为这个尖峰实际上所显示时间范围的平均值。

解决:

了解现象的原因,自然可以注意到并接纳这个现象,同时也大体能思考出如何解决, 既然展示的是平均值,那真正需要“尖峰”,展示最大值即可。

(1)对于没有提供辅助方案或者懒于处理数据的用户:

接纳这个现象,真正需要尖峰数据时,多选择尖峰时刻,多缩小范围。

(2)很多metric系统提供了辅助方案:

例如Cassandra自带的opscenter中的一些图: 显示平均值之外,显示最大值和最小值:

例如circonus提供了Aggregation Overlays,在原有默认展示图的基础之上,展示各种定制的“图层”

增加图层后:最大值变成了6.1,而不是最开始的0.167,时间点也不是图1所展示的尖峰时刻。

小结

注意metric图形中是否有spike erosion现象,如果存在,则接受之并适当处理,才能获取到真正想要的数据。而不是诧异于数据的诡异。

再如一类似例子:

下图为redis监控的一个指标,表示当前连接的客户端的数目,但是奇怪的是,偶尔会出现小数结果:例如图中的37.5.

实际上,虽然是gauge的值类型,也是整型值,但是显示时间范围不同时,怎么展示值是个问题?是最后一个数据,还是第一个数据,还是平均数还是最大值,而在这里可以注意到value type是average value即平均值。例如时间范围选择2天时,会以10分钟为频度计算平均值,所以会出现小数点值。

总结:metric的阅读中,可能存在很多无法理解的现象,要考虑到实际绘制的原理和数据如何产生等问题,才能有效理解metric.