quot;: "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "White" }}
]
}
},
"inner_hits": {
"highlight": {
"fields": {
"user.first": {}
}
}
}
}
}
}'
当有一个主实体比如一篇博客文章,带有一些有一定关系但又不是非常重要的其他实体比如评论时,内嵌对象会非常有用。如果能根据评论内容来查询到博客文章,那就很不错,而且内嵌查询和过滤器一起提供了更快的join查询能力。
内嵌对象模型的缺点如下:
为了 增加 、修改 或 删除 一个内嵌对象文档,整个文档必须重建索引;这就导致内嵌文档越多开销就越大。
搜索请求返回整个文档,而不是只返回匹配的内嵌文档。虽然已经以后计划支持返回根文档的部分最配内嵌文档,但目前仍然不支持。
有时候可能需要把主文档和其关联实体分离,这种分离由父子关系来提供。
通过建立另一个文档的父类型mapping,可以在相同索引的文档之间建立父子关系:
父子join对管理实体关系非常有用,尤其是在索引时间比检索时间很重要的情形下,但是它会带来较大的开销;父子查询比同等的内嵌查询要慢5到10倍。
2.全局序列号和延迟
父子关系使用了全局序列号来加速join操作。无论父子map是否使用了内存缓存或磁盘上的doc value,全局序列号仍然需要在索引发生任何改变时进行重建。
分片中的父代越多,全局序列号构建就越耗时。相对于需要父代和较少的子代, 父子关系最适合每个父代有很多子代的情形。
全局序列号默认是 延迟 构建:refresh后的第一个父子查询或聚合请求将会触发构建全局序列号。这会让用户感知到一个明显的潜在峰值。可以使用eager_global_ordinals 来把查询期构建全局序列号的成本转移到refresh期,通过如下方式mapping _parent属性:
这里,_parent属性的全局序列号将会在一个新的段搜索可见时被构建。
对于很多的父代,全局序列号要花费数秒钟来构建。此时,需要增加refresh_interval,以便refresh的频率更低,而全局序列号保持可用的时间更长。这将大幅减少每秒钟重建全局序列号的CPU消耗。
3.多代关系
对多代数据的Join(参考Grandparents and Grandchildren)能力听起来很吸引人,但需要思考其代价:
- Join越多,性能越差。
- 每一个父代都需要把自己的string _id属性保存在内存,这可能会消耗大量RAM。
- 当考虑关系型方案及父子关系是否适合时,可参考下列关于父子关系的建议:
- 保守使用父子关系,仅当子代比父代多很多时才考虑。
- 避免在单个查询中使用多父子关系来join。
- 避免对使用has_child过滤器,或score_mode为 none 的has_child查询来打分。
- 父ID尽量简短,以便在doc value中更好地压缩,从而在瞬时加载时消耗更少的内存。
4.为文件系统缓存分配内存
对于运行中Elasticsearch,内存是需要密切监控的重要资源之一。Elasticsearch和Lucene通过JVM堆内存和文件系统缓存两种方式来消耗内存。由于Elasticsearch运行在Java虚拟机(JVM)中,所以JVM的GC周期和频率也需要重点监控。
JVM堆内存
对于Elasticsearch一个“刚好合适”的JVM堆大小是非常重要的——不能设置过大或过小,原因见后文。一般来说Elasticsearch的经验值是分配少于50%的可用RAM给JVM堆,且不要超过32GB。
为El