如何将结构化数据导入Solr

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

这篇文章总结了我们在搜索中引入数据方面的经验。几乎所有搜索项目都是从使用现有数据为搜索引擎提供数据开始的。在这里,我们主要关注旧的良好关系数据库作为数据源。我什至会毫不犹豫地输入什么:SQL 数据库,还是非 NoSQL DB?。不用说,这些注意事项中的大部分都适用于任何其他数据源,如文件、Web 服务、NoSQL 数据库和分布式文件系统。

Solr 数据导入处理程序 - DIH

首先让我出来,我是 Data Import Handler 的忠实粉丝。它与任何其他 ETL 工具一样方便 - 您无需 编写调用 SolrJ 的 Java 代码 并在 IDE 中调试 SQL 查询结果。使用 DIH,您可以处理配置和复制粘贴查询,并直接在 SolrAdmin 处理查询和数据 。所以,DIH 非常适合快速原型制作,但如何在生产中运行它呢?这里有几个问题(添加你的):

  • 缺乏并发性——单线程处理使硬件处于空闲状态并需要大量时间才能完成;
  • 缺乏高性能连接(进一步观察)。

我们将在下面检查这些要点,但是,我们有证据表明 DIH 用于非常大的部署。因此,尽管有这些限制,许多人在生产中运行 DIH。为了解决并发问题,我们可以使用特制查询对数据进行逻辑分片,并并行启动每个分片的导入。连接性能问题 由持久缓存 解决,这可能会使连接操作更快,但我对这种方法持怀疑态度。现在让我们看看一些实际问题。

通过 DIH 索引块

我们被称为 Block Join proponents ,因此,希望 在 DIH 中索引块 。这在 5.1 中成为可能,并通过在子(第 2 级及更高级别)实体中指定 child=”true” 来启用。

DIH 中的嵌套实体(数据连接)

现在,让我们回到 DIH 中加入实体。正如上面提到的 SOLR-2382 中完美描述的那样:“将 SqlEntityProcessor 与子实体一起使用会导致“n+1 选择”问题。这个术语让我想起了 我之前在 IT 领域遇到的挑战 。因此,它无法处理任何大量数据在合理的时间内。您可能会问为什么不直接要求 RDBMS 连接实体并在 DIH 中处理连接的结果集。好吧,如果两个表驻留在同一个数据库中是可能的,但是,您应该记住 笛卡尔积问题 可以如果您加入两个或多个子实体,则会发生这种情况。在这种情况下,推荐的方法是使用哈希图在堆上缓存关系的一侧,并且当超出堆时(我关心的唯一情况),建议将 数据放在 BDB 的堆外文件

合并加入 DIH

事实证明, ETL 社区已经意识到这个问题,并且有很长一段时间的解决方案—— 外部合并连接 。例如,从简单的 N+1 子查询到数亿条记录的合并连接之间的增益是从几小时到几分钟的加速。值得一提的是,我们遇到了相同的算法——在搜索引擎实现中一遍又一遍地合并排序序列。这是个好消息:从 5.0 开始,通过在子实体中指定 join=”zipper”, 合并连接可用于 DIH 中的任何 EntityProcessor 。当然,您需要对两个输入进行排序,谢天谢地,RDBMS 的索引可以很好地完成这项工作。有趣的是,每次修改 merge join 配置时,Kettle ETL 是如何提醒的。 DIH 不会用这样的弹出窗口打扰您,但如果入站流未排序,则会抛出异常。 您也可以处理多对多关系,但它需要在 RDBMS 中进行连接和排序,这通常没问题。是时候谈谈线程和并发处理了。在此之前,让我们注意一下,merge join 算法不容易并行化,因此“hash join”(在缓存数据中查找)更适合多线程处理。

多线程

DIH 中没有线程。原文如此。我们在顺序处理中遇到了生产者-消费者的常见陷阱——每个人都在等待它的对应者:

我们在 DIH 拥有的 我们想要的

请注意,如果使用 SolrCloud 运行 DIH,我们会遇到同样的问题,在这种情况下,DIH 会一个一个地同步地向 Solr 提供一个文档,并阻塞直到每个文档都被 DistributingUpdateProcessor 发送到分片领导者。

好吧,问题说完了,我们来谈谈机会。我们可以并行化出站流(消费者):

  • 如果 DIH 通过 ConcurrentUpdateSolrClient CloudSolrClient 发送更新,它将解除生产者与消费者的阻塞,从而有机会充分利用 Solr 机器进行索引。然而,这在目前的 DIH 设计中是不可能的,但我们有一个很好的突破尝试 ——SOLR-7188 。小心!这将是一个伟大的胜利,让我们能够将 DIH 作为一个真正的 ETL 工具来运行。
  • 还有另一个补丁在 UpdateRequestProcessors 层上添加了线程 - SOLR-3585, 您可以将其视为服务器端 ConcurrentUpdateSolrClient 。尽管我们从生产使用中得到了 积极的反馈 ,但自从它做出贡献后我改变了主意,不认为它是一种架构明智的方法。我认为提供适当级别的并发负载和节流是客户的责任(即 ETL)。不过,如果遇到麻烦,您可以使用它,例如,如果您有一些遗留脚本将文件发布到 Solr http 端点。

我们还可以考虑如何在后台线程中预取 JdbcDataSource 中的数据,避免阻塞生产者。我一定在某个地方有这样的补丁,如果你需要,请告诉我。

另一个 ETL 工具:Kettle

我还玩过几个开源 ETL 工具,我选择 Kettle 作为示例。它内置了许多有用的工具,您绝对可以将它用作数据摄取的工具箱。但是,我们热衷于解决特定问题 - 构建 Solr XML。我面临的困难是平面关系元组(称之为行或记录)的局限性。然而,我们需要的是 像这里一样 至少有三层嵌套——父子属性。一种可能的解决方法是使用 XML DOM 作为数据结构,但是它不能按原样在转换步骤之间传输,需要转换为字符串,一次又一次地连接和解析,就像我们在这里所做的那样。 我发现 XML Join 的可扩展性不佳,它更像是内存中的 XPath 数据库,它很棒,但不是我需要的。一种可能的解决方案是在 Kettle 中引入 DOM XML 作为第一类数据类型,并让一些步骤按原样处理它。

请继续关注,我们将很快展示这样的概念验证。不要犹豫,分享您的愿景、经验和发现。这也是我在 谈论 ETL 时使用的平台。