Elasticsearch数据建模
在进行Elasticsearch相关咨询和培训的过程中,笔者发现大家普遍更关注实战,下面选取几个 常见且典型的问题和大家一起分析。
问题1:订单表、账单表父子文档可以实现类似SQL的左连接吗?若通过canal同步到 Elasticsearch中,能否实现类似左连接的效果?具体应该如何建模?
问题2:一个人管理1000家连锁门店,如何更高效地查询自己管辖的商品类目?企微上,一 个人维护了1000个员工,如何快速查询自己管辖的员工信息?
问题3:随着业务的增长,一个索引的字段数据不断膨胀(因商品场景变化,业务侧一直在增 加字段),有什么解决方法?
问题4:一个索引字段个数设置为1500个,超出这个限制会不会消耗CPU资源,造成写入堆 积?
问题5:在进行机器学习基线的日志诊断时需要将消息(message)分离出来,怎么在数据写 入前完成这项工作呢?
如果我们对上述实战问题进行归类,就都可以归为Elasticsearch数据建模问题。
(1)宽表方案
这是空间换时间的方案,就是允许部分字段冗余存储的存储方式。实战举例如下。
用户索引:user。博客索引:blogpost。一个用户可以发表多篇博客。若按照传统的MySQL建 表思想,则对两个表建立用户外键即可。而对于Elasticsearch,我们更愿意在每篇博文后面都 加上用户信息,即采用宽表存储方案。这样看似存储量大了,但是一次检索就能搞定搜索结 果。
(2)Nested方案 适用一对少量的场景,比如子文档偶尔更新、查询频繁的场景。
如果需要索引对象数组并保持数组中每个对象的独立性,则应使用嵌套数据类型Nested而不是 对象数据类型Object。
Nested文档的优点是可以将父子关系的两部分数据(如博客正文和评论)关联起来,我们可以
基于Nested类型做任何的查询。但它的缺点是查询速度相对较慢,更新子文档需要更新整篇文 档。
(3)Join父子文档方案
适用于一对多的场景,尤其是当子文档的数量明显大于父文档的数量时,或子文档需要频繁更 新时。例如,可以将“供应商”视为父文档,将“供应商”的各种“产品”视为子文档,这就构成了一 对多的关系。在这种情况下,使用has_child或has_parent查询可以实现父子文档之间的关联查 询。该方案的优点是父子文档可以独立更新,但缺点是维护Join关系需要额外的内存,且查询 资源消耗比nested类型更大。
(4)业务层面编程方案 简言之,对于需要多次检索才能获取关键字段的情况,你可以在业务层自己编写代码进行处
理。 以上4种方法构成了Elasticsearch的完整多表关联方案。在实际应用中,应根据自己的业务场
景,通过Kibana Dev Tools进行模拟实现,找到最适合自己业务的多表关联方案。
需要强调的是,多表关联可能会引发性能问题,特别是在数据量巨大且对检索性能要求高的场 景中,需要谨慎使用。Elasticsearch官方文档也明确指出:应尽可能避免多表关联,因为 Nested类型可能导致查询速度慢几倍,而Join类型则可能导致查询速度慢数百倍。
最后,我们来总结一下建模过程中其他核心考量因素。 1)尽量空间换时间:能利用多个字段解决的不要用脚本实现。
2)尽量前期进行数据预处理,而不要后期使用脚本。优先选择ingest process进行数据预处 理,尽量不要留到后面用script脚本实现。
3)能指定路由时要提前指定路由。写入的时候指定路由,检索的时候也同样可以指定路由。 4)能前置的操作应尽量前置,让后面检索聚合的步骤更加清爽。比如前置索引字段排序就是
一种非常好的方式。 数据建模是Elasticsearch开发实战中非常重要的一环,从项目管理角度来看也是设计环节的重
中之重,一定要重视。千万不要着急写业务代码,“代码之前,设计先行”。