任何数据库系统的关键功能是使用某种形式的过滤来获取完整数据集的子集。OpenTsdb从版本1.x开始提供了过滤功能,并从2.2开始具有了扩展功能。过滤器目前在标签值上运行,这意味着在拉取数据时,任意指标和标签Key都必须按照与数据库中完全相同的出现方式来进行指定。

示例数据

  由于每个过滤器都会在下面进行说明,都会用到下述数据集。它由单一指标组成,定义在各个标签上的多个时间序列。作为例子,在T1处仅给出一个数据点。

TS# Metric Tags Value@T1
1 sys.cpu.system dc=dal host=web01 3
2 sys.cpu.system dc=dal host=web02 2
3 sys.cpu.system dc=dal host=web03 10
4 sys.cpu.system host=web01 1
5 sys.cpu.system host=web01 owner=jdoe 4
6 sys.cpu.system dc=lax host=web01 8
7 sys.cpu.system dc=lax host=web02 4

分组

  分组,即group by,是使用所需的聚合函数和过滤器将多个时间序列组合成一个的过程。默认情况下,OpenTSDB按指标对所有内容进行分组,以便如果查询返回10个时间序列且使用sum聚合器,则所有10个序列将随着时间的推移添加到一个值中。有关时间序列如何聚合合并的详细信息,请参阅聚合。

  为了避免在没有任何聚合的情况下对每个底层时间序列进行分组和获取,请使用版本2.2中包含的聚合器。或者,可以禁用OpenTSDB2.2以及更高版本基于每个过滤器的分组。请参阅API文档了解如何操作。

OpenTSDB 1.x – 2.1

  在最初的OpenTSDB版本中,最多只有两种类型的过滤器可用,并且它们被隐式配置用于分组。允许的两个运算符如下:

  • *:星号(或通配符,wildcard)将为检测到的每个唯一标签值返回单独的结果。例如,如果标签键host有web01与web02这组值,将会有两个组发出。一组为web01,另一组为web02。
  • |:管道(或者literal_or)仅为指定的准确的标签值返回单独的结果。也就是说,它只会匹配具有给定标签值的时间序列和聚合每个匹配成组。

  多个过滤器可以提供给一个查询,过滤器之间使用AND连接,返回同时满足条件的结果。这些过滤器在2.x以及更高版本中可用。

示例

  下面示例使用v1版本的HTTP URI栈,聚合器组成的参数m,冒号,紧接着指标以及花括弧里面通过等号分隔开的标签过滤器。   例1: http://host:4242/q?start=1h-ago&m=sum:sys.cpu.system{host=web01}

包含的时间序列# Tags 聚合标签 Value@T1
1,4,5,6 host=web01 16

  在这种情况下,聚合标签集将为空,因为时间序列4和5具有与整个集合不相同的标签。   新版API实际运行情况:(与文档描述不一致)   查询条件:

{
	"start":"23h-ago",
	"end":"10h-ago",
	"queries":[
		{
			"metric":"sys.cpu.system",
			"rate":"false",
			"aggregator":"sum",
			"tags":{
				"host":"web01"
			}
		}
	]
}

  查询结果:

[
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521689251":8,
			"1521689113":3,
			"1521689167":1,
			"1521689211":4
		},
		"aggregateTags":[
			"dc"
		],
		"tags":{
			"owner":"jdoe",
			"host":"web01"
		}
	}
]

  例2: http://host:4242/q?start=1h-ago&m=sum:sys.cpu.system{host=web01,dc=dal}

包含的时间序列# Tags 聚合标签 Value@T1
1 dc=dal host=web01 3

  新版API实际运行情况:(与文档描述一致)   查询条件:

{
	"start":"1h-ago",
	"queries":[
		{
			"metric":"sys.cpu.system",
			"aggregator":"sum",
			"tags":{
				"host":"web01",
				"dc":"dal"
			}
		}
	]
}

  查询结果:

[
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521773430":3
		},
		"aggregateTags":[],
		"tags":{
			"host":"web01",
			"dc":"dal"
		}
	}
]

例3: http://host:4242/q?start=1h-ago&m=sum:sys.cpu.system{host=*,dc=dal}

包含的时间序列# Tags 聚合标签 Value@T1
1 dc=dal host=web01 3
2 dc=dal host=web02 2
3 dc=dal host=web03 10

  这次我们为host标签提供了*通配符以及显示匹配dc标签。这将对host标签进行分组,并为每个唯一的host标签值返回一个时间序列,在本例中为3个序列。

  新版API实际运行情况:(与文档描述一致)   查询条件:

{
	"start":"1h-ago",
	"queries":[
		{
			"metric":"sys.cpu.system",
			"aggregator":"sum",
			"tags":{
				"host":"*",
				"dc":"dal"
			}
		}
	]
}

  查询结果:

[
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521773430":3
		},
		"aggregateTags":[],
		"tags":{
			"host":"web01",
			"dc":"dal"
		}
	},
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521773431":2
		},
		"aggregateTags":[],
		"tags":{
			"host":"web02",
			"dc":"dal"
		}
	},
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521773432":10
		},
		"aggregateTags":[],
		"tags":{
			"host":"web03",
			"dc":"dal"
		}
	}
]

  例4: http://host:4242/q?start=1h-ago&m=sum:sys.cpu.system{dc=dal|lax}

包含的时间序列# Tags 聚合标签 Value@T1
1,2,3 dc=dal host 15
6,7 dc=lax host 12

  在这里,| 运算符仅用于匹配dc查询中提供的标签值。因此,TSD将会把拥有这些值的任意时间序列分组聚合在一起。host标签被移动到聚合标签列表中,设定的每个序列拥有一个host标签,并且标签拥有多个值。   新版API实际运行情况:(与文档描述一致)   查询条件:

{
	"start":"1h-ago",
	"queries":[
		{
			"metric":"sys.cpu.system",
			"aggregator":"sum",
			"tags":{
				"host":"*",
				"dc":"dal"
			}
		}
	]
}

  查询结果:

[
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521773430":3
		},
		"aggregateTags":[],
		"tags":{
			"host":"web01",
			"dc":"dal"
		}
	},
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521773431":2
		},
		"aggregateTags":[],
		"tags":{
			"host":"web02",
			"dc":"dal"
		}
	},
	{
		"metric":"sys.cpu.system",
		"dps":{
			"1521773432":10
		},
		"aggregateTags":[],
		"tags":{
			"host":"web03",
			"dc":"dal"
		}
	}
]

警告   由于这些过滤器有限制,如果用户像#1, #4和#5一样编写时间序列,则可能会由于聚合时间序列而返回异常结果,这些时间序列可能有一个通用的标签但附加标签不同。这个问题在2.3和显式标签中有所解决。

OpenTSDB 2.2

  在OpenTSDB 2.2版本中增加了一个更灵活的过滤框架,允许禁用分组以及增加了过滤器类型,如正则表达式和通配符。过滤框架是可插拔的,以允许尝试进入外部系统如资产管理或供应系统。

  在处理过程中,在同一个标签Key上可以有多个过滤器,比如它们之间用AND连接。如果有两个过滤器host=literal_or(web01)和host=literal_or(web02),查询将会返回空。如果同一个标签Key包含两个或者更多过滤器,并且其中一个过滤器启用了组,另一个未启用,则对于该标签Key上的所有过滤器,group by将实际为真。

警告   某些类型的过滤器可能会导致查询比其他的慢,特别是regexp,wildcard和大小写不敏感的过滤器。在从存储拉取数据之前,将对这些过滤器进行处理以基于UID创建数据库过滤器,因此使用区分大小写的literal_or过滤器总是比regexp快,因为可以将字符串解析为UID,并将它们发送到存储系统进行过滤。相反,如果要求使用pre, post或infix过滤的正则表达式或通配符,则TSD必须使用标签键UID从存储中检索所有行,然后对每个唯一行,将UID解析为字符串,然后再在结果上运行过滤器。此外,有大量文字(literals)列表的过滤器将在存储后处理,以避免为后台存储创建大量过滤器。此限制的默认为4096,并且可以通过tsd.query.filter.expansion_limit参数进行配置。

显示标签

  从2.3及更高版本开始,如果给定指标所有的标签值可以通过使用explicitTags功能大大降低查询延迟。有两个好处:

  1. 对高基数的指标,后端可以切换到更高效的查询以从存储中获取更小的数据子集。
  2. 对于具有不同标签的指标,这可以用于避免聚合不应包含在最终结果中的时间序列。   显示标签将创建一个底层存储查询,该查询仅获取具有给定标签Key的那些行。这可以让数据库跳过不相关的行,并在更短的时间内响应。   例子:   下面示例使用v1版本的HTTP URI栈,聚合器组成的参数m,冒号,紧接着指标以及花括弧里面通过等号分隔开的标签过滤器。   例1: http://host:4242/q?start=1h-ago&m=sum:explicit_tags:sys.cpu.system{host=web01}
包含的时间序列# Tags 聚合标签 Value@T1
4 host=web01 1

  这解决了不一致的标签key的问题,使我们只能筛选出时间序列#4。

  例2: http://host:4242/q?start=1h-ago&m=sum:explicit_tags:sys.cpu.system{host=}{dc=}

包含的时间序列# Tags 聚合标签 Value@T1
1,6 host=web01 dc 11
2,7 host=web02 dc 6
3 host=web03,dc=dal 10

  此查询使用v2版本URI语法,以避免将dc标签key置于第二组花括号中进行分组。此时只筛选同时拥有host和dc标签key的时间序列,然而仅仅根据host的值进行分组。它跳过了时间序列#4和#5。

注意:   使用HBase(0.98或更高版本)或者Bigtable时,确保tsd.query.enable_fuzzy_filter已经在配置中启用(默认启用)。它为后端提供了一个特殊的过滤器,可以跳过我们需要查询的行,而不是遍历每一个rowkey进行正则表达式匹配比较。 注意:   使用2.4版本,TSDB将会向后端发送多个get请求而不是一个scan请求。此时可以通过多种因素减少查询时间,特别是对于高基数的时间序列。但是,过滤器只能由literal_or组成。

v2.2内置过滤器

  下述列表即OpenTSDB内置的过滤器。附加的过滤器可以插件的方式加载。每一个heading都是URI或JSON查询中使用的type。在编写URI查询时,通过将过滤器名称放在标签key的等号右侧并将过滤器值放在 括号中来使用过滤器。例如{host=regexp(web[0-9]+.lax.mysite.com)}。对于JSON查询,只需使用过滤器名称作为type参数,并使用过滤器值作为filter参数。例如:

  以下示例使用URI语法:

literal_or

  采用一个literal_or或|管道符连接值列表,则会返回区分大小写敏的结果匹配时间序列。这是一个十分高效的过滤器,因为它可以将字符串解析为UID并将其发送到存储层进行预过滤。它与SQL的IN谓词类似。 例子:

  • host=literal_or(web01|web02|web03),类似于SQL:where host in(‘web01’,’web02’,’web03’)
  • host=literal_or(web01),类似于SQL: where host=’webb01’

iliteral_or

  (注意官方文中为ilteral_or,错误) 与literal_or类似但它不区分大小写。请注意:它不像literal_or一样高效,或者说它必须后处理存储中的所有行。(即TSDB会取出存储中的所有行进行过滤)

not_literal_or

与literal_or一样区分大小写,将返回与给定值列表不匹配的时间序列。因为它可以通过存储进行预处理。

not_iliteral_or

  与not_literal_or过滤效果相同,但不区分大小写

wildcard

  提供区分大小写的后缀、前缀、中缀(infix)和多个中缀(multi-infix)过滤器。通配符是星号”*”。如果只给出星号,则过滤器有效地返回包含标签key的任意时间序列(并且是可以预处理的高效过滤器)。在SQL的字段域中,它与LIKE谓词相似,但具有更多的灵活性。   例子:

  • host=wildcard(*mysite.com), SQL: where host=’%mysite.com’
  • host=wildcard(web*)
  • host=wildcard(web*mysite.com)
  • host=wildcard(webmysite)
  • host=wildcard(*),与v1版本基础的group by运算符等效,效率很高。

iwildcard

  与wildcard相同,但不区分大小写。

regexp

  从存储中获取后使用符合POSIX标准正则表达式的过滤器过滤。该过滤器使用Java内置的正则表达式操作。根据查询的使用方法,请注意转义特殊字符。   例子:

  • regexp(web.*), SQL: where host regexp ‘web.*’
  • regexp(web[0-9].mysite.com)

加载过滤器

  在OpenTSDB2.2及更高版本中显示加载的过滤器,请调用HTTP接口/api/config/filters。它将列出加载的插件以及说明和示例用法。

插件

  随着开发人员添加插件,将会在此处列出。   如果要开发一个插件,只需扩展net.opentsdb.query.filter.TagVFilter类,然后根据插件文档创建JAR包并将其放入插件目录。在TSD启动时,将会搜索插件并加载它。如果执行过程中出现错误,TSD将不会启动并且记录异常。