Elasticsearch 6.x 父子文档与嵌套文档选型咨询
针对你在Elasticsearch 6.x升级过程中,面临关系型数据建模选择嵌套文档或父子文档的困惑,我来逐一解答你的三个问题:
1. 单个字段中可存储的嵌套文档/数组的最大数量?
Elasticsearch 没有硬性的上限值,但实际使用中会受几个关键因素限制:
- 首先,
index.mapping.nested_fields.limit这个参数控制的是索引中嵌套字段类型的数量(默认50),而非单个嵌套字段内的文档数量; - 每个嵌套文档本质上会被当作独立的Lucene文档存储,所以嵌套文档数量越多,索引的磁盘占用、内存消耗以及查询/索引的性能开销都会线性增长。如果单个字段嵌套上千甚至上万个文档,会明显拖慢查询响应速度,增加索引时间;
- 另外,查询时的一些参数(比如
max_docvalue_fields_search)也会间接影响大嵌套数组的处理,但核心限制还是来自集群的资源承载能力和业务的性能需求。
建议你根据实际业务场景做压力测试,确定一个既能满足业务需求又不影响性能的合理数量,一般来说,单字段嵌套文档数控制在几百以内是比较稳妥的。
2. 若采用嵌套字段类型,针对频繁字段操作有何建议?
嵌套字段的核心特性是:更新任何一个嵌套子文档,都需要重新索引整个父文档(包含所有嵌套子文档),这意味着频繁操作嵌套字段的成本很高,给你几个针对性建议:
- 优先合并更新操作:如果需要更新多个嵌套子文档,尽量把这些更新合并成一次请求,避免多次单独更新带来的重复索引开销;
- 评估是否真的需要嵌套:如果某个子文档的更新频率极高,且和父文档的关联度不是特别紧密,建议考虑改用父子文档模式——父子文档允许单独更新子文档,无需影响父文档;
- 优化索引刷新策略:适当调大
index.refresh_interval(比如从默认的1s改成30s),减少频繁刷新Lucene分段带来的性能损耗,适合更新频繁但对实时性要求不是极高的场景; - 合理使用
include_in_parent/include_in_root:如果部分嵌套字段的查询不需要精准的嵌套上下文,可以开启这两个参数,把嵌套字段的值同步到父文档的根字段中,这样可以用普通的非嵌套查询替代性能开销更大的嵌套查询,但会增加索引的磁盘占用。
3. 当存在4种关系类型时,父子文档模式存在哪些限制?
在Elasticsearch 6.x中,父子文档是通过join字段类型实现的,当存在4种关系类型时,主要有以下限制:
- 单索引仅支持一个
join字段:所有的父子关系都必须定义在同一个join字段中,比如你可以设计成「一个父类型 + 三个子类型」的结构,或者多层级的链式关系(如A→B→C→D),但关系结构越复杂,查询逻辑的复杂度和性能开销会越高; - 父子文档必须同分片:子文档索引时必须指定和父文档一致的
routing值,否则无法关联到父文档。这会增加索引时的复杂度,若分片数据不均,还可能导致部分分片压力过大; - 多层关系查询性能损耗大:如果4种关系是多层嵌套的(比如父→子1→子2→子3),使用
has_child/has_parent进行跨层级查询时,性能会显著下降,因为每一层关联都需要额外的分片内关联操作; - 父文档必须先于子文档创建:索引子文档前,对应的父文档必须已经存在,否则会报错。批量索引时需要严格控制文档的顺序,或者使用
op_type=create避免覆盖; - 子文档无法同时属于多个父文档:每个子文档只能关联一个父文档,如果你需要多对多的关系,父子文档模式并不适用,可能需要考虑其他建模方式(比如中间关联文档)。
内容的提问来源于stack exchange,提问作者ieatbytes
相关产品推荐
相关产品推荐

