春季初始化后的热身Neo4j数据库

时间:2017-06-03 18:52:24

标签: java spring neo4j

我使用的Neo4J数据库有近500k个节点。当我启动Spring应用程序并执行第一次查询时,大约需要4-5秒。这只发生在第一个查询中,因此我认为在初始化spring之后我可以进行一次预热,以使所有后续查询更快。

这是我的applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <!-- neo4j database -->
    <util:map id="config">
        <entry key="enable_remote_shell" value="false" />
    </util:map>

    <bean id="graphDbFactory" class="org.neo4j.graphdb.factory.GraphDatabaseFactory" />

    <bean id="graphDbBuilder" factory-bean="graphDbFactory"
        factory-method="newEmbeddedDatabaseBuilder">
        <constructor-arg value="/path/to/db" />
    </bean>

    <bean id="graphDbBuilderFinal" factory-bean="graphDbBuilder"
        factory-method="setConfig">
        <constructor-arg ref="config" />
    </bean>

    <bean id="graphDatabaseService" factory-bean="graphDbBuilderFinal"
        factory-method="newGraphDatabase" destroy-method="shutdown" class="org.neo4j.graphdb.GraphDatabaseService"/>

    <context:component-scan base-package="com.app.components" />
</beans>

我看到等待Spring bean初始化的一种方法是实现ApplicationListener,并且用Neo预热数据库的常规方法是调用apoc.runtime.warmup()函数,所以我做了如下:

package com.app.components;

@Component
public class StartupTasks implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOG = LogManager.getLogger(StartupTasks.class);

    @Autowired
    GraphDatabaseService db;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        executeTestSelect();
    }

    private void executeTestSelect() {
        LOG.info("Warming up Neo4j...");

        Transaction tx = db.beginTx();
        db.execute("CALL apoc.warmup.run()");
        tx.close();

        LOG.info("Warmup complete.");
    }
}

这个没有用,一切都被正确记录,但第一个neo4j查询仍然很慢。

由于这种方法不起作用,我编辑了executeTestSelect方法来运行实际查询并处理结果如下:

private void executeTestSelect() {
    String textToSearch = "a"; // returns almost all nodes, should make neo4j cache them all
    Transaction tx = db.beginTx();
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("textToSearch", textToSearch );
    Result resultSet = db.execute("MATCH (n:PROD) WHERE n.description CONTAINS {textToSearch} RETURN n",
                params);
    Iterator<Node> results = resultSet.columnAs("n");
    int count = 0;
    while (results.hasNext()) {
        results.next();
        count++:
    }
    tx.close();
    LOG.info("Neo4j cache done. Processed " + count + " nodes.");
}

这次启动只需要4-5秒来执行查询,然后打印

  

Neo4j缓存完成了。已处理0个节点。

这很奇怪,因为完全初始化应用程序时运行的完全相同的查询会返回450k个节点。

我错过了什么?是否有可能在到达onApplicationEvent时Neo4j数据库尚未初始化且无法执行查询?

如何在完整的Spring应用程序初始化后正确预热neo4j数据库?

1 个答案:

答案 0 :(得分:1)

好的,我真的很幸运地发现了这一点。

我删除了ApplicationListener并创建了一个@Service类,其中@Autowired注释用于执行预热的方法,它似乎正在运行。

我通过删除一个类字段而没有看到@Autowired注释留在这里来发现这一点,并且它有效。我试了几次,热身现在正在工作。我不知道它是否在Spring文档中的某处记录。

这是我的最后一堂课:

package com.app.components;

@Component
public class Neo4jWarmup {

    private static final Logger LOG = LogManager.getLogger(StartupTasks.class);

    @Autowired
    GraphDatabaseService db;

    /*
     * this did the trick - the method gets called 
     * after Spring initialization and the DB works as expected
     */
    @Autowired 
    public void neo4jWarmup() {
        executeTestSelect();
    }

    private void executeTestSelect() {
        LOG.info("Warming up Neo4j...");

        Transaction tx = db.beginTx();
        db.execute("CALL apoc.warmup.run()");
        tx.close();

        LOG.info("Warmup complete.");
    }
}