InfluxQL 内部实现
了解 InfluxQL 的实现,以理解结果是如何处理的以及如何创建高效的查询:
查询生命周期
InfluxQL 查询字符串被分解为令牌,然后解析成抽象语法树(AST)。这是查询本身的代码表示。
抽象语法树(AST)被传递给
QueryExecutor,它将查询指向适当的处理程序。例如,与元数据相关的查询由 元服务 执行,而SELECT语句则由分片本身执行。查询引擎然后确定与
SELECT语句的时间范围匹配的分片。从这些分片中,为语句中的每个字段创建迭代器。迭代器被传递给发射器,它会处理这些迭代器并合并结果点。发射器的工作是将简单的时间/值点转换为返回给客户端的更复杂的结果对象。
理解迭代器
迭代器提供了一种简单的接口,用于循环遍历一组点。 例如,这是一个用于浮点数的迭代器:
type FloatIterator interface {
Next() *FloatPoint
}
这些迭代器是通过 IteratorCreator 接口创建的:
type IteratorCreator interface {
CreateIterator(opt *IteratorOptions) (Iterator, error)
}
IteratorOptions提供有关字段选择、时间范围和维度的参数,这些参数可以在规划迭代器时由迭代器创建者使用。IteratorCreator接口在许多级别上使用,例如Shards、Shard和Engine。这允许在适用时进行优化,例如返回预计算的COUNT()。
迭代器不仅仅用于从存储中读取原始数据。迭代器可以组合,以便在输入迭代器周围提供额外的功能。例如,DistinctIterator 可以计算输入迭代器每个时间窗口的不同值。或者,FillIterator 可以生成输入迭代器中缺失的额外点。
这个组合也非常适合聚合。
例如,在以下SQL中, MEAN(value) 是一个 MeanIterator,它封装了来自底层分片的迭代器:
SELECT MEAN(value) FROM cpu GROUP BY time(10m)
以下示例将 MEAN(value) 包装在一个额外的迭代器 (DERIVATIVE()) 中,以确定均值的导数:
SELECT DERIVATIVE(MEAN(value), 20m) FROM cpu GROUP BY time(10m)
游标
一个 游标 根据元组 (时间, 值) 通过分片标识单个系列 (测量, 标签集和字段) 的数据。游标遍历存储为日志结构合并树的数据,并处理各级的去重、已删除数据的墓碑,以及合并缓存 (写前日志)。游标按时间以升序或降序对 (time, value) 元组进行排序。
例如,一个评估1,000个系列在3个分片上的一个字段的查询构建了至少3,000个游标(每个分片1,000个)。
辅助字段
因为 InfluxQL 允许用户使用选择函数,比如 FIRST(),
LAST(), MIN() 和 MAX(), 引擎必须提供一种方法来同时返回与选择点相关的数据。
让我们来看看以下查询:
SELECT FIRST(value), host FROM cpu GROUP BY time(1h)
我们选择每小时出现的第一个 value,但我们也希望检索与该点相关的 host。由于 Point 类型仅为效率指定一个单一的类型 Value,我们将 host 推入点的辅助字段。这些辅助字段附加到点上,直到它被传递给发射器,在那里字段被分离到它们自己的迭代器中。
内置迭代器
有许多辅助迭代器可以让我们构建查询:
合并迭代器 - 这个迭代器将一个或多个迭代器合并成一个相同类型的新迭代器。这个迭代器保证在开始下一个窗口之前,窗口内的所有点都会被输出,但不提供窗口内的排序保证。这允许快速访问对聚合查询的访问,而这些查询不需要更强的排序保证。
排序合并迭代器 - 像
MergeIterator一样,这个迭代器将一个或多个迭代器组合成一个新的相同类型的迭代器。然而, هذا 迭代器保证每个点的时间顺序。这使得它比MergeIterator更慢,但这种顺序保证对于返回原始数据点的非聚合查询是必需的。限制迭代器 - 此迭代器限制每个名称或标签组的点数。这是
LIMIT和OFFSET语法的实现。填充迭代器 - 如果输入迭代器中缺少点,则该迭代器会注入额外的点。它可以提供
null点、带有前一个值的点或带有特定值的点。缓冲迭代器 - 该迭代器提供了将一个点“未读”回缓冲区的能力,以便下次可以再次读取。这在窗口处理上被广泛使用,以提供前瞻性。
减少迭代器 - 该迭代器为窗口中的每个点调用一个减少函数。 当窗口完成时,该窗口的所有点将被输出。 这用于简单的聚合函数,例如
COUNT()。减少切片迭代器 - 此迭代器首先收集窗口的所有点,然后一次性将它们传递给一个归约函数。结果从迭代器返回。这用于聚合函数,例如
DERIVATIVE()。变换迭代器 - 该迭代器对输入迭代器中的每个点调用一个变换函数。这用于执行二元表达式。
去重迭代器 - 此迭代器仅输出唯一点。由于它占用大量资源,因此此迭代器仅用于小型查询,例如元查询语句。
调用迭代器
在InfluxQL中,函数调用分为两个层次:
某些调用可以在多个层次上包装以提高效率。例如,可以在分片级别执行一个
COUNT(),然后多个CountIterator可以用另一个CountIterator包装,以计算所有分片的计数。这些迭代器可以使用NewCallIterator()创建。一些迭代器更复杂或需要在更高级别实现。 例如,
DERIVATIVE()函数需要在执行计算之前检索窗口的所有点。 这个迭代器是由引擎本身创建的, 并且从未请求在较低级别创建。