我使用的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数据库?
答案 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.");
}
}