简介

数据去规范化动机多样,当出现因数据复杂操作影响系统稳定性、业务响应/并发要求不满足等都是触发因素。
业务稳定性问题:面向 C 端的互联网应用特征是并发量较高,SQL 偏向点查点写,相对简单,但是沉淀下来的数据需要做运营往往涉及传统企业级应用对于数据库的操作特征。大范围数据删查、表关联、排序等实时操作,以及满足报表/BI 等更加复杂的数据库需求。通过去规范化和负载分离是较合理的选择。
复杂查询性能问题:企业级应用经常涉及表关联、聚合、多维筛选、排序等操作,并常常带来性能问题。通过去规范化的一些方式,如下文提到的数据冗余和预计算方式,显著改善性能。

表级处理 宽表预构建 / Cube 预构建
主要操作就是构建宽表,或者构建数据立方体(Data Cube)。构建好的宽表包含了用户查询时需要的所有维度、度量信息。

MySQL 关联表在 ES 上的设计

关系型数据库中的表 join 关系在 ES 可以用几种数据类型来表达,包括 objected,nested,join 三种。

objected

object 类型可以存储嵌套结构。
优点:表示主 field 和 object 内部 field 之间的一对多关系,支持 doc 的 join 查询。由于所有查询时依赖的关联数据也都在一个文档内,避免了 ES 内部的 join,查询效率较高。
缺点:一对多关系只能保留一层,多于一层的会被打平,会丢失嵌套 field 内部的关联关系。

nested

nested 类型可以存储嵌套结构,表示一对多关系,是 object 类型的拓展。
优点
不会出现 object 的缺点,整个嵌套关系是完整维护的,子文档内部关联关系保存是完整的
关联数据通过实现上自然关联到主文档上,搜索时性能较好(相对于 join)
缺点
一个 nested field 只能属于一个主文档
在 nested 类型中,子文档和主文档之间强绑定,主文档更新的时候会强制更新子文档。在写多读少的场景,新能开销较大。
child 文档的查询必须通过父文档再找到子文档
子文档频繁修改的话会影响别的子文档和父文档,因为本质上在 lucene 实现上是父文档下的冗余存档

join

join 类型可以配置父子文档,通过父子文档来实现一对多的能力,一个索引只能建一个。相比 nested 类型,该类型更加灵活。父子文档之间通过 parentId 来关联,实际实现上他们就是独立的文档。
优点
子文档更新不影响父文档和其他子文档
一个子文档可以单独搜索
一个文档在作为子文档时可以自己选择属于哪个父文档。通过 relation 可以指定不同的 join 列
缺点
需要建个全局序数,用于服务于父子文档的关联关系,这个会影响搜索性能

join 和 nested 类型比较

join 适合写多读少场景,更加适合关注索引性能的场景。这意味着更新的生效会更快,但是搜索时的开销也相对大些
nested 适合读多写少的场景,更加关注搜索的性能