使用Elasticsearch作为主数据存储


荷兰和比利时最大的电子商务公司 bol.com开始了为期4年的重新思考和重建其整个ETL(提取,转换,加载) 管道的过程,该管道一直在阴暗的甲骨文 Oracle PL / SQL黑客的地牢中呼吸,处于腐烂状态,导致生产上的不断增加的短暂停顿。
重写是不可避免的。在起草了许多蓝图后,我们选择了Elasticsearch支持的Java服务作为主存储!
这个想法给即使是最资深的Elasticsearch聘请的顾问带来了挫折,所以为了让你放心,我会告诉你为什么我们采取如此激进的方法以及我们如何设法逃脱我们的旧系统遗产。 
在深入了解详情之前,让我分享一下2,000英尺的电子商务搜索设置概述,这将有助于您更好地了解前面讨论的主题。请注意,这种简化完全省略了合并缓存层的星云,编排多个搜索集群的系统,具有自定义刷新和重放功能的队列,就地弹性机制,维护不推荐使用的搜索实体的服务,以避免因404引起的机器人排名,断路器,节流器,负载平衡器等。但它仍然足够准确,可传达一般的想法。

搜索
[在进一步讨论之前,我想借此机会通过搜索来确定我的意思。我希望这可以帮助您更好地围绕ETL的最终消费者。话虽如此,请随意跳过本节,直接跳到下一节的ETL深入部分。]
许多人倾向于犯错误,即在电子商务中对搜索进行狭隘的观察,并将其用例仅限于在一堆产品属性中进行索引。我试图提供一般性的,但远非完整的概述,它可以实现:

  • 在数百种产品属性中搜索一个术语,其中匹配 和排名由直接或间接使用者决定的(您是PS4所有者搜索最新的“使命召唤”的吗?)和相关性(您可能通过键入”与“门和窗户”有关的“门“,但是与部门无关),
  • 浏览(基本上是没有术语的搜索)在数千个类别中使用上述搜索中使用的类似排名机制,
  • 如果输入与某些模式(EAN,ISBN,ISSN等)或商品推销规则相匹配。
  • 隐含地触发多项搜索(例如缩小到较低的类别或扩大到更高的类别等)以增强结果,
  • 装饰每个列表(您可能希望在“Harddisks”支持中搜索/浏览时看到“容量”方面而不是“鞋尺寸”的细节)。

谁/什么使用搜索?

一些使用者:

  • 客户:搜索和购买商品的人。它们看起来是无害的,直到在黑色星期五使用,类似DDoS攻击。
  • 机器人:他们定期(截至本文撰写之日,每天最多几次)尝试将整个目录消化到他们的系统中,主要用于两个目的:
    • 将目录集成到他们自己的搜索引擎(即Google)中,
    • 调整他们的定价策略(即竞争对手)

    处理机器人流量最糟糕的部分是你不能总是限制它们(例如,谷歌考虑到排名的网站延迟),你需要确保它们不会损害客户流量。深思熟虑:想象一下,你的顾客在圣诞节前夕在你的商店里蜂拥而至,谷歌决定用每秒数千个请求来捕捉整个目录。
  • 合作伙伴:您的业​​务合作伙伴还可以定期扫描您的目录以集成到他们自己的系统中 (有趣的事实:有些甚至需要每日Excel导出。)可以将它们归类为仅对数据子集感兴趣的机器人。
  • 内部服务:上次我统计,除了上面列出的用户之外,还有20多个内部服务使用搜索来增强他们的结果。它们的使用量最多可占流量的50%。

在合作伙伴和内部服务的情况下,人们可能会争论为什么他们需要搜索数据而不是直接访问原始产品属性和优惠。答案很简单:它们还使用ETL管道中包含的其他属性(例如,构成,类别)。因此,不是将内部ETL系统暴露给它们,而是在搜索网关上管理它们更方便,已知搜索网关具有经过实战考验的可扩展性和弹性测量。

性能怎么样?
在这个领域长达数十年的经验,使搜索速度提高10ms可以产生数百万欧元的额外收入,具体取决于您的业务规模。不幸的是,这个等式也是相反的。因此,您总是希望在一定的延迟和高于某个吞吐量阈值的情况下执行。

内容有多不稳定?
非常非常非常不稳定!

  • 产品可能有多个优惠(bol.com优惠,合作伙伴优惠等),具有不同的属性(定价,可传递性,折扣等),其中两个优惠和/或其属性都是高度不稳定的。优惠可能缺货,价格可能会发生变化等。虽然面向客户的网页在运行时使用最新数据进行了增强,但搜索索引可能会落后并提供最终一致的视图。这种情况下的波动可能从几秒到几个月不等。在黄金时间,例如在情人节那天,您不希望您的搜索引擎返回几秒钟前缺货的礼品列表。
  • 您的手册(由店铺专家触发)和自动(人工智能,机器学习驱动)流程可以改变类别树,添加新方面,调整现有方面的曝光,修改搜索行为(例如,由商品推销规则触发的流量),添加上下文敏感(例如,依赖于类别)同义词条目,同义词,引入新的排名等。这些更改可能需要追溯更新数百万个文档。

ETL
在电子商务搜索领域, ETL表示输入是大量信息源(产品属性,报价,折扣,排名,构面,同义词,同义词条目等)的输出,输出是非 规范化输入构成针对搜索查询性能优化的搜索就绪文档。等一等?如果一个ETL管道只是提供了一些优化目的,这听起来不像没有它可以进行搜索吗?排序......这在某种程度上确实是可能的。如果我们暂时搁置细节,我们可以大致比较两种方法如下:

没有ETL:
优点:输入源中的每个更改都会立即生效。(因此,索引时间成本几乎为零。)
缺点:由于在查询时需要对输入源进行连接和丰富操作,因此延迟和吞吐量会受到严重影响。

有ETL:
由于满足搜索请求的所有潜在数据已经被索引到索引中,因此搜索需要最少的努力来在查询时满足请求。
输入源的每次变化都需要预处理,影响从数百到数百万的众多产品。

换句话说,ETL完全是关于索引与查询时间性能之间的权衡:

  1. 我们现有的ETL功能足够全面,
  2. Elasticsearch的查询时间性能已经因为分面faceting,内部触发的查询等而受到影响,在某种程度上外部缓存成为必需,
  3. 搜索延迟对收入有很大影响,

我们采用了厚厚的ETL管道路径。
实时内容流
在这里,ETL管道从十几个队列中侦听从产品属性到报价,特定报价折扣到排名等更新,所有这些都是用JSON格式化的。幸运的是,每个实时内容流消息都会触发单个产品更新。让我用一个案例来举例说明:当disk_capacity_bytes产品的属性发生变化时,我们
  1. 首先从存储中获取相关文档,
  2. 更新其disk_capacity_bytes属性,
  3. 应用与更新文档的最后状态匹配的配置,
  4. 并持久取得的结果。

这里有一些问题需要解决:两个关键问题:
  1. 您如何表示配置谓词,以便您可以将它们与内容进行匹配?
  2. 您如何表示配置变异,以便您可以针对内容执行它们?

配置流
配置是通过业务友好的屏幕定义的规则。每个已发布的配置最终都有三个用途:

  1. 搜索网关使用它来确定如何查询搜索索引,
  2. ETL管道使用它来处理实时内容流,
  3. 和ETL管道追溯更新可能受影响的文档。

虽然前两个是相对便宜的操作,但最后一个是房间里的大象!这是我们迄今为止描述的美丽故事中第一次我们需要将更改传播到数百万个文档。(banq注:这好像是EventSourcing,事件溯源追溯)
这里省略原文各个数据库用来实现ES的比较,其实肯定都不行。原文作者可能没有意识到事件这个概念,执着于操作变更的复杂跟踪。

旧ETL

旧的ETL是一个Oracle数据库,其中的配置是在PL / SQL中建模的。由于配置抽象语言是数据库使用的完全相同的语言,因此执行突变和谓词是毫不费力的。Hail SQL注入 功能!虽然这带来了一些显着的成本:

  • 在抽象模型中使用PL / SQL创建了功能和财务供应商锁定。功能缺陷(表达能力不足,PL / SQL泄漏到不相关的组件)多年来阻碍了许多创新,随着时间的推移,它变得越来越困难。此外,它构成了将服务迁移到云的重大障碍。它的财务方面在bol.com的规模上可以忽略不计。
  • 回滚更新配置变异的变化是PL / SQL工程在实践中努力实现的。这种困难,加上日志,测试,调试,分析等实用程序的不足,使程序员不再采取这种方式。因此,每晚有超过12个小时的完整ETL运行配置快照增量。由经验丰富的工程师驯服的这种野兽有着频繁暂停的影响,并且使得bug很难调试,查找和重现,更不用说修复了!


在其先前的版本中,内容属性以<id, content_id, key, value>标准化形式存储。这种方法开始受到铰链中效率疼痛的影响,将ETL数据拉到搜索索引。当时雇佣的Oracle顾问检查了这些用法,并建议使用非规范化结构,其中每个属性都存储为一列。除了临时包扎相关的伤口之外,这还允许DBA让他们的想象力变得狂野,将属性映射到列。问题:

  • PL / SQL中的任务并行化非常困难。我们尝试通过内部Oracle AQ修补此漏洞,但我不确定它是否改善或恶化了状态。
  • 在预计运行12个小时以上的数据库程序中,出现墨菲定律,我们明智地(!)设计了系统,以便在某些检查点保持其状态,这些检查点构成可重复的处理,这样在早上来时可看到ETL崩溃。
  • 移动组件的数量需要使用支持Oracle的专有调度工具。该计划与bash脚本粘合在一起,设计在仅适用于Windows的专有开发环境中,并在运行GNU / Linux的Oracle机器上推出。GNU / Linux和使用开发人员的Windows都不喜欢这种情况。

  • 由于ETL出现故障的成本很高,企业也无法轻易改变和/或进行业务优化。这是一个非常令人失望的问题,对技术和商业有影响的人士却需要使用它。


存储引擎之战
鉴于我们的功能要求,我们评估了几个不同的ETL管道存储解决方案:PostgreSQL、MongoDB、Elasticsearch。

ostgreSQL结果被省略的原因是无论我们抛出什么样的优化,基准测试总是花费超过2小时,无论分区如何,而MongoDB和Elasticsearch花了几分钟。
让我分享一些结果的意见:

  • 增加并发性可提高Elasticsearch性能(最多32个并发批处理),但对MongoDB没有太大影响。
  • Elasticsearch在性能方面受到了冲击,尽管它受到整个文档更新的影响,而MongoDB只是尝试更新单个属性。使用32个并发批次,Elasticsearch和MongoDB分别花费了175s和518s来完成基准测试。
  • 与MongoDB相比,Elasticsearch产生了更可预测的性能数据。请注意75和99百分位数之间的差异。
  • Elasticsearch段合并在运行期间出乎意料地非常稳定,而我们预计它会因高更新率而成为瓶颈。但是,在_version字段上播放的比较和交换循环允许必要的数据完整性而不会出汗。

在测试时,由于我们的操作障碍,我们最初无法在MongoDB中启用分片。尽管Elasticsearch的结果非常有希望,但即使是受雇的Elasticsearch顾问也感到震惊,我们决定采用它,我们拥有多年的生产经验。如果我们将白名单配置谓词字段问题的必要性放在一边 - 也就是说,可以查询的内容需要明确的索引 - MongoDB也很可能是一个可行的选择。


为什么Elasticsearch有一个不被推荐为主要数据存储的声誉?我认为这一切都始于几年前官方项目网站包含一个明确的声明,承认Elasticsearch并不打算用作主数据存储。

下面是它的一些特点:

  • 安全性:Elasticsearch不提供开箱即用的任何安全措施(加密等)。我们不使用Elasticsearch来存储任何类型的PII
  • 事务:Elasticsearch没有事务支持。虽然我们通过在_version 字段上执行比较和交换循环来解决它。
  • 工具:Elasticsearch工具......只是一块垃圾。它没有适当的开发环境 - 你只能运行一个完全成熟的Kibana才能使用它的 控制台。它的Java客户端以Elasticsearch整个像银河系一样多的组件为依赖,这是一个等待爆炸的JAR地狱定时炸弹。此外,最近推出的高级REST客户端 泄漏了Apache HTTP Client API模型等。对于泄露的模型和传递依赖性,您无能为力 - 您只需学会与它们共存。对于IDE,您只需使用您喜欢的HTTP客户端保留一大堆 HTTP请求配方,例如 cURLPostman, httpie
  • 文档:Elasticsearch没有文档; PostgreSQL有文档MongoDB有文档。Elasticsearch所拥有的是一堆表面令人搔痒的博客文章,以类似文档的网站形式提供。Elasticsearch还有Stack Overflow 和论坛帖子的海洋,您可以在方便的时候游泳。话虽这么说,人们需要承认,随着时间的推移情况正在改善。(是的,情况更糟!)
  • 弹性:是的,Elasticsearch可能会崩溃,就像另一个软件一样。为了应对这些紧急情况,除了热备用集群之外,我们还会定期拍摄快照 并将ETL管道处理的消息保存到单独的存储中,从而提供高效的写入和批量读取操作,例如PostgreSQL,Google BigQuery等。在需要的情况下,我们只需从快照恢复并重放必要的消息集以恢复丢失的状态。

 
更多详细资料可点击标题查看原文,个人认为作者没有采取业界通用的模式,就是在主数据库与ETL之间采用Apache Kafka,将Elasticsearch成为Kafka的一个数据源,对于特定的用户比如外部客户,机器人或内部客户采取不同的数据源,针对客户设计不同的数据结构,通过Kafka导出转换。