多层数据结构,一对多关系,如何用一个查询查询所有的数据?
需求: 一次查询父文档和子文档?
目前ES主要有以下4种常用的方法来处理数据实体间的关联关系:
(1)Application-side joins(服务端Join或客户端Join)
这种方式,索引之间完全独立(利于对数据进行标准化处理,如便于上述两种增量同步的实现),由应用端的多次查询来实现近似关联关系查询。这种方法适用于第一个实体只有少量的文档记录的情况(使用ES的terms查询具有上限,默认1024,具体可在elasticsearch.yml中修改),并且最好它们很少改变。这将允许应用程序对结果进行缓存,并避免经常运行第一次查询。
(2)Data denormalization(数据的非规范化)
这种方式,通俗点就是通过字段冗余,以一张大宽表来实现粗粒度的index,这样可以充分发挥扁平化的优势。但是这是以牺牲索引性能及灵活度为代价的。使用的前提:冗余的字段应该是很少改变的;比较适合与一对少量关系的处理。当业务数据库并非采用非规范化设计时,这时要将数据同步到作为二级索引库的ES中,就很难使用上述增量同步方案,必须进行定制化开发,基于特定业务进行应用开发来处理join关联和实体拼接。
ps:宽表处理在处理一对多、多对多关系时,会有字段冗余问题,适合“一对少量”且这个“一”更新不频繁的应用场景。宽表化处理,在查询阶段如果只需要“一”这部分时,需要进行结果去重处理(可以使用ES5.x的字段折叠特性,但无法准确获取分页总数,产品设计上需采用上拉加载分页方式)。
(3)Nested objects(嵌套文档)
索引性能和查询性能二者不可兼得,必须进行取舍。嵌套文档将实体关系嵌套组合在单文档内部(类似与json的一对多层级结构),这种方式牺牲索引性能(文档内任一属性变化都需要重新索引该文档)来换取查询性能,可以同时返回关系实体,比较适合于一对少量的关系处理。
ps: 当使用嵌套文档时,使用通用的查询方式是无法访问到的,必须使用合适的查询方式(nested query、nested filter、nested facet等),很多场景下,使用嵌套文档的复杂度在于索引阶段对关联关系的组织拼装。
(4)Parent/child relationships(父子文档)
父子文档牺牲了一定的查询性能来换取索引性能,适用于一对多的关系处理。其通过两种type的文档来表示父子实体,父子文档的索引是独立的。父-子文档ID映射存储在 Doc Values 中。当映射完全在内存中时, Doc Values 提供对映射的快速处理能力,另一方面当映射非常大时,可以通过溢出到磁盘提供足够的扩展能力。 在查询parent-child替代方案时,发现了一种filter-terms的语法,要求某一字段里有关联实体的ID列表。基本的原理是在terms的时候,对于多项取值,如果在另外的index或者type里已知主键id的情况下,某一字段有这些值,可以直接嵌套查询。具体可参考官方文档的示例:通过用户里的粉丝关系,微博和用户的关系,来查询某个用户的粉丝发表的微博列表。
ps:父子文档相比嵌套文档较灵活,但只适用于“一对大量”且这个“一”不是海量的应用场景,该方式比较耗内存和CPU,这种方式查询比嵌套方式慢5~10倍,且需要使用特定的has_parent和has_child过滤器查询语法,查询结果不能同时返回父子文档(一次join查询只能返回一种类型的文档)。而受限于父子文档必须在同一分片上,ES父子文档在滚动索引、多索引场景下对父子关系存储和联合查询支持得不好,而且子文档type删除比较麻烦(子文档删除必须提供父文档ID)。
ES中并不能像SQL中那样通过JOIN查询可以把3张表的结果拼接返回。父子文档每次只能返回一种类型文档(父文档or子文档),嵌套文档和宽表化处理通过在索引阶段将两张表拼在一起可以实现一次查询返回所有数据,但也不适合于处理3层关联(复杂度太高,要么在索引阶段,要么在查询阶段)。个人建议还是分两次查询处理比较好。第一次查询返回帖子--帖子评论(采用嵌套文档或宽表化处理),第二次根据评论用户ID去查用户信息(主键查询,速度肯定不是问题了)
以上是 多层数据结构,一对多关系,如何用一个查询查询所有的数据? 的全部内容, 来源链接: utcz.com/z/518003.html