全局序号(Global Ordinals)edit
有种可以用来降低字符串 fielddata 内存使用的技术叫做 序号 。
设想我们有十亿文档,每个文档都有自己的 status 状态字段,状态总共有三种: status_pending 、 status_published 、 status_deleted 。如果我们为每个文档都保留其状态的完整字符串形式,那么每个文档就需要使用 14 到 16 字节,或总共 15 GB。
取而代之的是我们可以指定三个不同的字符串,对其排序、编号:0,1,2。
Ordinal | Term
-------------------
0 | status_deleted
1 | status_pending
2 | status_published
序号字符串在序号列表中只存储一次,每个文档只要使用数值编号的序号来替代它原始的值。
Doc | Ordinal
-------------------------
0 | 1 # pending
1 | 1 # pending
2 | 2 # published
3 | 0 # deleted
这样可以将内存使用从 15 GB 降到 1 GB 以下!
但这里有个问题,记得 fielddata 是按分 段 来缓存的。如果一个分段只包含两个状态( status_deleted 和 status_published )。那么结果中的序号(0 和 1)就会与包含所有三个状态的分段不一样。
如果我们尝试对 status 字段运行 terms 聚合,我们需要对实际字符串的值进行聚合,也就是说我们需要识别所有分段中相同的值。一个简单粗暴的方式就是对每个分段执行聚合操作,返回每个分段的字符串值,再将它们归纳得出完整的结果。
尽管这样做可行,但会很慢而且大量消耗 CPU。
取而代之的是使用一个被称为 全局序号 的结构。 全局序号是一个构建在 fielddata 之上的数据结构,它只占用少量内存。唯一值是 跨所有分段 识别的,然后将它们存入一个序号列表中,正如我们描述过的那样。
现在, terms 聚合可以对全局序号进行聚合操作,将序号转换成真实字符串值的过程只会在聚合结束时发生一次。这会将聚合(和排序)的性能提高三到四倍。
构建全局序号(Building global ordinals)edit
当然,天下没有免费的晚餐。 全局序号分布在索引的所有段中,所以如果新增或删除一个分段时,需要对全局序号进行重建。
重建需要读取每个分段的每个唯一项,基数越高(即存在更多的唯一项)这个过程会越长。
全局序号是构建在内存 fielddata 和 doc values 之上的。实际上,它们正是 doc values 性能表现不错的一个主要原因。
和 fielddata 加载一样,全局序号默认也是延迟构建的。首个需要访问索引内 fielddata 的请求会促发全局序号的构建。由于字段的基数不同,这会导致给用户带来显著延迟这一糟糕结果。一旦全局序号发生重建,仍会使用旧的全局序号,直到索引中的分段产生变化:在刷新、写入或合并之后。
预构建全局序号(Eager global ordinals)edit
单个字符串字段 可以通过配置预先构建全局序号:
PUT /music/_mapping/_song
{
"song_title": {
"type": "string",
"fielddata": {
"loading" : "eager_global_ordinals"
}
}
}
设置 eager_global_ordinals 也暗示着 fielddata 是预加载的。
正如 fielddata 的预加载一样,预构建全局序号发生在新分段对于搜索可见之前。
序号的构建只被应用于字符串。数值信息(integers(整数)、geopoints(地理经纬度)、dates(日期)等等)不需要使用序号映射,因为这些值自己本质上就是序号映射。
因此,我们只能为字符串字段预构建其全局序号。
也可以对 Doc values 进行全局序号预构建:
PUT /music/_mapping/_song
{
"song_title": {
"type": "string",
"doc_values": true,
"fielddata": {
"loading" : "eager_global_ordinals"
}
}
}
这种情况下,fielddata 没有载入到内存中,而是 doc values 被载入到文件系统缓存中。
与 fielddata 预加载不一样,预建全局序号会对数据的 实时性 产生影响,构建一个高基数的全局序号会使一个刷新延时数秒。
选择在于是每次刷新时付出代价,还是在刷新后的第一次查询时。如果经常索引而查询较少,那么在查询时付出代价要比每次刷新时要好。如果写大于读,那么在选择在查询时重建全局序号将会是一个更好的选择。
针对实际场景优化全局序号的重建频次。如果我们有高基数字段需要花数秒钟重建,增加 refresh_interval 的刷新的时间从而可以使我们的全局序号保留更长的有效期,这也会节省 CPU 资源,因为我们重建的频次下降了。
全局序号是一种优化字符串fielddata内存使用的手段,通过将字符串转换为序号以减少内存占用,并利用全局序号结构提高聚合操作性能。
6560

被折叠的 条评论
为什么被折叠?



