使用投影可以提高 Hibernate 搜索索引构建性能吗?

Can Hibernate Search index build performance be improved using projection?

我有一个包含大约 40 个列和 7 个集合的实体。这些集合不是延迟加载的。使用 Hibernate Search MassIndexer 索引 500 000 个实体大约需要 2-3 小时。我使用下面的代码:

fullTextSession.createIndexer()
        .batchSizeToLoadObjects(1000)
        .threadsToLoadObjects(8).start();

我什至尝试使用延迟加载来查看有什么不同,然后大约需要 1 小时,这没有我希望的那么快。

为了尝试如果实体仅包含我要索引的列(包括 ID 列在内的 3 列)时的性能会怎样,我创建了一个仅包含这三列的实体。现在索引速度非常快,只用了3分钟。

使用不同实体的方法不是我想要的,因为每次更新原始实体时都需要手动更新索引(据我了解它是如何工作的)。然后我想到使用 flushToIndexes() 方法和投影,而不是 MassIndexer。

我在https://docs.jboss.org/hibernate/search/4.4/reference/en-US/html_single/#search-batchindex-flushtoindexes的基础上编写了下面的代码,并添加了投影部分。

Session session = sessionFactory.openSession();
try {
    int batchSize = 1000;
    FullTextSession fullTextSession = Search.getFullTextSession(session);
    fullTextSession.setFlushMode(FlushMode.MANUAL);
    fullTextSession.setCacheMode(CacheMode.IGNORE);
    Transaction transaction = fullTextSession.beginTransaction();
    //Scrollable results will avoid loading too many objects in memory
    ScrollableResults results = fullTextSession.createCriteria( Report.class )
        .setProjection(Projections.projectionList()
                .add(Projections.property("reportId"), "reportId")
                .add(Projections.property("header"), "header")
                .add(Projections.property("description"), "description")
                )
        .setResultTransformer(Transformers.aliasToBean(Report.class))
        .setFetchSize(batchSize)
        .scroll( ScrollMode.FORWARD_ONLY );
    int index = 0;
    while( results.next() ) {
        index++;
        fullTextSession.index( results.get(0) ); //index each element
        if (index % batchSize == 0) {
            fullTextSession.flushToIndexes(); //apply changes to indexes
            fullTextSession.clear(); //free memory since the queue is processed
        }
    }
    transaction.commit();
} catch (Exception e) {
    log.error(e);
} finally {
    session.close();
}

当 运行 代码尝试索引第一个元素时,我在代码中遇到异常(在 fullTextSession.index(results.get(0));):

org.hibernate.TransientObjectException: The instance was not associated with this session

我不明白为什么会出现此异常。我读过如果使用不同的 Hibernate 会话可能会发生这种情况,但在这种情况下,我在一个 Hibernate 会话中完成所有工作。

是否有人尝试过将投影与 Hibernate 搜索索引一起使用?应该可以使用吗?如有任何关于该主题的信息,我们将不胜感激。

一些版本信息:我使用的是 Hibernate 4.2.17.Final 和 Hibernate Search 4.4.6.Final。由于依赖关系,我无法使用最新版本。

使用投影(目前)不是一种选择,因为投影结果与对象无关:它是暂时的。 FullTextSession#index() 方法需要一个托管对象,因此您会得到 TransientObjectException.

在设计 MassIndexer 时,我考虑过使用投影,但它似乎并没有给我带来显着的好处;有趣的是,您报告这对您的情况很有用。您确定您所有的关系都是惰性关系,并且您确定索引过程不需要那些惰性加载的关系吗?

如果您可以确认仅通过加载一些较少的数据列就可以看到如此显着的性能优势,我们可以考虑对其进行修补。理想情况下,我们可以使此优化对用户透明,不需要添加更多配置选项。

根据我的经验,虽然主要的减速是由于数据库需要多次往返才能加载所有关系;通常你可以通过确保所有关系都是惰性的,并为你需要在索引期间加载的关系启用二级缓存来获得巨大的性能提升。根据你的模型,缓存可能比投影更有效。

但我意识到我正在对实体的建模方式做出一些假设,因此您的报告非常有趣。请打开一个新的 "Improvement" JIRA on our issue tracker.