大家好,我是Mincong,来自法国的一名工程学生。我很高兴向大家介绍我的2016年Google Summer of Code项目,该项目提供了一个替代Hibernate Search当前批量索引器实现的方法,使用了Java批处理架构(JSR 352)。我已经在这个项目上工作了4个月。在开始之前,我要感谢Google赞助这个项目,感谢Hibernate团队接受我的提案,以及感谢我的导师Gunnar和Emmanuel在这段时间内的帮助。现在,让我们开始吧!
这是什么?
Hibernate Search通过将实体状态与由Lucene(或自Hibernate Search 5.6起的Elasticsearch)维护的搜索索引同步,为您的Hibernate/JPA应用程序带来了全文搜索功能!索引同步通常在实体修改时实时发生,但在某些情况下,可能需要重新构建整个索引,例如在为现有的实体类型启用索引时,或者在直接对数据库进行更改后,绕过Hibernate(Search)。
Hibernate Search为此提供了批量索引器。我的GSoC项目的目标是开发一个使用JSR 352标准化的Java批处理应用程序API的替代方案。
我们从JSR 352中获得了什么?
使用标准化的批处理API实现批量索引功能,允许您使用运行时环境的现有工具来启动/停止和监控索引过程的状态。例如,在WildFly中,您可以使用CLI来执行此操作。
此外,JSR 352 提供了一种重启特定作业运行的方法。这在实体类型索引过程中由于数据库连接问题等中间失败时非常有用。一旦问题解决,批处理作业将从中断处继续,不会再次处理已成功处理的项。
由于 JSR 352 定义了面向批处理的通用概念,如项读取器、处理器和写入器,因此作业架构和工作流程非常易于理解。在 JSR 352 中,工作流程是用 XML 文件(“作业 XML”)编写的,该文件用于指定作业、其步骤并指导它们的执行。因此,您可以在不深入代码的情况下理解这个过程。
<job id="massIndex">
<step id="beforeChunk" next="produceLuceneDoc">
<batchlet ref="beforeChunkBatchlet"/>
</step>
<step id="produceLuceneDoc" next="afterChunk">
<chunk checkpoint-policy="custom">
<reader ref="entityReader"/>
<processor ref="luceneDocProducer"/>
<writer ref="luceneDocWriter"/>
<checkpoint-algorithm ref="checkpointAlgorithm"/>
</chunk>
...
</step>
<step id="afterChunk">
<batchlet ref="afterChunkBatchlet"/>
</step>
</job>
如您所见,它带来了一种普遍的批处理工作负载。任何有 ETL 流程经验的用户都应该没有困难理解我们的新实现。
示例用法
以下是在草稿版本中新的大规模索引器的示例用法。它允许您添加一个或多个类类型。如果您有多个根实体要索引,则可以使用 addRootEntities(Class<?>…)
方法。
long executionId = new MassIndexer()
.addRootEntity( Company.class )
.start();
long executionId = new MassIndexer()
.addRootEntity( Company.class, Employee.class )
.cacheable( false )
.checkpointFreq( 1000 )
.rowsPerPartition( 100000 )
.maxThreads( 10 )
.purgeAtStart( true )
.optimizeAfterPurge( true )
.optimizeAtEnd( true )
.start();
并行性
为了最大化性能,我们强烈建议您通过并行性加快大规模索引器的速度。默认情况下启用并行性。在 JSR 352 标准 下,确切的说法是“分区”。索引步骤可能作为多个分区运行,每个线程一个分区。每个分区都有自己的分区 ID 和参数。如果分区多于线程,则分区被视为队列进行消费:每个线程一次只能运行一个分区,并且不会在完成上一个分区之前消费下一个分区。
massIndexer = massIndexer.rowsPerPartition( 500 );
检查点
大规模索引器支持检查点算法。如果作业因任何原因中断,大规模索引器可以从批处理运行时存储的最后一个检查点重新启动。并且已经索引的实体不会丢失,因为它们已经被刷新到目录提供者。假设 N 是检查点频率的值,则分区将在处理分区内的每 N 个项目时达到检查点。您可以根据您的业务需求覆盖它。
massIndexer = massIndexer.checkpointFreq( 1000 );
运行
有关进一步使用,请检查我的 GitHub 存储库 gsoc-hsearch。如果您想尝试它,可以下载代码并用 Maven 构建。
$ git clone -b 1.0 git://github.com/mincong-h/gsoc-hsearch.git
$ cd gsoc-hsearch
$ mvn install
当前状态和下一步
目前,新实现接受不同类型的实体作为入口,提供高级别的作业属性定制和并行索引。作业定期保存其当前进度,以启用从最后的一致性点重新启动。已考虑负载平衡,以避免任何单个线程过载。此索引批处理作业可在 Java SE 和 Java EE 下使用。
还有许多事情要做,例如与性能改进、集成到 WildFly、监控、更细粒度选择要重新索引的实体等相关。以下是其中一些想法
-
核心:复合 ID 的分区映射
-
集成:将批处理作业打包为 WildFly 模块
-
集成:从
FullTextSession
和FullTextEntityManager
启动索引批处理作业 -
集成:将此项目嵌入到 Hibernate Search 中
-
监控:增强基本监控,例如重启作业的进度状态
-
性能:确保此实现的出色性能
这些任务以 GitHub 问题形式跟踪,您可以在这里检查完整的 TODO 列表。
反馈
如果您正在使用Hibernate Search并且希望有一个更标准化的批量索引方法,这个项目显然是为您准备的。
在将其集成到Hibernate Search核心代码库作为模块之前,我们仍需要进行一些改进和润色,但任何对该项目的错误报告或评论都将非常有帮助。所以请试试看,并告诉我们您的反馈。只需在下方留言或在GitHub上提出问题。
期待收到您的来信!