elasticsearch是如何解决关系型数据问题

阿凡达2018-08-01 13:26

在传统的数据库中,对数据关系的描述有一对一,一对多,多对多的关系。如果我们有关联的数据,一般都会采用添加主键外键的形式来建立数据之间的关系,然后查询的时候通过join的形式来得到我们想要的数据。但是转化到elasticsearch中,我们是如何处理有关系的数据?elasticsearch本身是nosql类型的数据,弱化了对关系模型的支持,如果对关系型的支持,想必肯定会影响elasticsearch的性能。但是我们现实中的数据肯定是有关系的,那么我们就一起来看看elasticsearch是如何处理带有关系的数据?  

  • 使用inner object
这是elasticsearch默认的类型,在我们没有指定mapping的时候,也是可以插入一条数据,elasticsearch是支持动态映射,如下数据结构
{
  "order_id" : "订单ID",
  "order_item" : [
    {
      "goods_id" : "12345",
      "goods_name" : "YSL圣罗兰"
    },
    {
      "goods_id" : "12346",
      "goods_name" : "花王纸尿布"
    }
  ]
}
最终elasticsearch会平铺成以下数据,在一个文档中goods_id 和goods_name 没有存在任何关系数据
{
  "order_id" : "订单ID",
  "order_item.goods_id":["12345","12346"],
  "order_item.goods_name":["YSL圣罗兰","花王纸尿布"]
}
elasticsearch的每个field都是支持多值存储,在上面的json中我们看着像order和orderItem一对多的结构,其实最终的存储是每个字段存储多值。
然后检索的时候匹配每个field就可以获取到对应的文档信息。而这种结构goods_id和goods_name不存在任何关系,有些场景我们需要知道一个order_item中goods_id和goods_name的关系,以上结构肯定不适合。
  • 使用nested object
在以上inner object方案中,并不是严格意义上的关系,而第二层的order_item数据是没有分离的;如果我们的结果一定要分离,就必须在mapping中显示的定义order_item为nested类型,只有这样第二层的order_item数据是相互隔离,互不影响的。还是以上数据格式json:
{
  "order_id" : "订单ID",
  "order_item" : [
    {
      "goods_id" : "12345",
      "goods_name" : "YSL圣罗兰"
    },
    {
      "goods_id" : "12346",
      "goods_name" : "花王纸尿布"
    }
  ]
}
在elasticsearch中会存储成3个文档,3个文档分别为1个order文档+2个order_item文档。声明了nested类型,每一个对象都会单独存储成一个新的文档,查询的时候能够独立进行查询,性能比inner object差,因为把order_item存储成了单独的文档,查询的中带order_item的条件的时候,就需要先查下order_item文档再根据order和order_item的关系进行查询order。nested object更新的代价较大,因为每个子文档的更新都需要重新建整个结构体的索引,所以nested object 不适合经常update的嵌套多级关系的场景。
  • 使用parent/child 关系
parent/child关系和nested非常类似,但是侧重点完全不一样。则使用场景上也是完全不同。
在使用parent/child关系时,elasticsearch会在每个shard中维护一张关系表(文章上面的order和order_item的关系),在检索的时候,通过has_parent和has_child过滤器来得到关联的数据,这种关系模型下,父文档和子文档是相互独立的,但查询性能远比nested object低:
  1. nested object更新文档的时候是整个文档类型更新,则子文档和父文档是存储在同一个segment上的。
  2. parent/child关系父文档和子文档是绝对独立的,只是保证分布在同一个shard上,但不能保证同一个segment上。所以每次查询都需要从内存中获取子文档和父文档的关系。

  • 三种es处理关系型数据方案总结:
inner object:
  1. 简单,查询性能高。
  2. 对子对象的内部联系不关心
nested object:
  1. 底层数据父文档和子文档存储在同一个segment中,查询性能比parent/child关系好
  2. 更新单个子文档需要重构整个数据结构,所以不适合更新频繁的业务场景
parent/child 关系:
  1. 父文档和子文档存储完全独立,所以适合子文档多并且文档更新频繁的场景。
  2. 父文档和子文档存储在不同的segment中,所以查询性能比nested object 差
  3. 需要额外的内存来维护父子文档的关系。

本文来自网易实践者社区,经作者陈道秋授权发布。