neo4j 在 1000 万个节点上更新属性
neo4j update properties on 10 million nodes
我用的是neo4jjava核心api,想更新1000万个节点。
我认为使用多线程会更好,但性能不是很好(设置属性需要 35 分钟)。
解释一下:每个节点"Person"与"Point"节点至少有一个关系"POINTSREL",该节点具有属性"Points"。我想总结 "Point" 节点的点并将其设置为 属性 到 "Person" 节点。
这是我的代码:
Transaction transaction = service.beginTx();
ResourceIterator<Node> iterator = service.findNodes(Labels.person);
transaction.success();
transaction.close();
ExecutorService executor = Executors.newFixedThreadPool(5);
while(iterator.hasNext()){
executor.execute(new MyJob(iterator.next()));
}
//wait until all threads are done
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
这里是可运行的 class
private class MyJob implements Runnable {
private Node node;
/* collect useful parameters in the constructor */
public MyJob(Node node) {
this.node = node;
}
public void run() {
Transaction transaction = service.beginTx();
Iterable<org.neo4j.graphdb.Relationship> rel = this.node.getRelationships(RelationType.POINTSREL, Direction.OUTGOING);
double sum = 0;
for(org.neo4j.graphdb.Relationship entry : rel){
try{
sum += (Double)entry.getEndNode().getProperty("Points");
} catch(Exception e){
e.printStackTrace();
}
}
this.node.setProperty("Sum", sum);
transaction.success();
transaction.close();
}
}
有更好(更快)的方法吗?
关于我的设置:
具有 8 个 CPU 和 32GB 内存的 AWS 实例
neo4j-wrapper.conf
# Java Heap Size: by default the Java heap size is dynamically
# calculated based on available system resources.
# Uncomment these lines to set specific initial and maximum
# heap size in MB.
wrapper.java.initmemory=16000
wrapper.java.maxmemory=16000
neo4j.properties
# The type of cache to use for nodes and relationships.
cache_type=soft
cache.memory_ratio=30.0
neostore.nodestore.db.mapped_memory=2G
neostore.relationshipstore.db.mapped_memory=7G
neostore.propertystore.db.mapped_memory=2G
neostore.propertystore.db.strings.mapped_memory=2G
neostore.propertystore.db.arrays.mapped_memory=512M
在我看来,有些地方可以改进。
题外话
如果您使用 Java 7(或更高版本),请考虑使用 try with resource 来处理事务。它将防止您出错。
性能
首先 - 批处理。目前您是:
- 正在创建工作
- 启动线程(其实executor中有pool)
- 开始交易
对于每个节点。您应该考虑 批量 进行更新。这意味着您应该:
- 收集
N
个节点(即 N
=1000)
- 为
N
个节点创建单个作业
- 在作业中创建单个事务
- 更新该交易中的
N
个节点
- 关闭交易
设置
您有 8 个 CPU。这意味着您可以创建更大的线程池。我觉得Executors.newFixedThreadPool(16)
就可以了
黑客
您有 32GB 内存。我可以建议:
- 将 java 堆大小减少到 8GB。根据我的经验,大堆大小会导致大量 GC 暂停和性能下降
- 创建映射内存大小。只是为了确保更多的数据可以保留在缓存中。
只为你的情况。如果 所有 您的数据都可以放入 RAM,那么您可以将 cache_type
更改为 hard
以实现此更改目的。 Details.
配置
如您所说 - 您正在使用 Core API。这是 Embedded
图数据库还是服务器 extension
?
如果这是 Embedded
图形数据库 - 您应该验证您的数据库设置是否应用于创建的实例。
我发现 属性 "cache_type=soft" 存在问题。我将它设置为 "cache_type=none",执行时间从 30 分钟减少到 2 分钟。在一些更新之后,总是有线程被阻塞大约 30 秒 - 更改此 属性 有助于避免这些阻塞。我会搜索更详细的解释。
我用的是neo4jjava核心api,想更新1000万个节点。 我认为使用多线程会更好,但性能不是很好(设置属性需要 35 分钟)。
解释一下:每个节点"Person"与"Point"节点至少有一个关系"POINTSREL",该节点具有属性"Points"。我想总结 "Point" 节点的点并将其设置为 属性 到 "Person" 节点。
这是我的代码:
Transaction transaction = service.beginTx();
ResourceIterator<Node> iterator = service.findNodes(Labels.person);
transaction.success();
transaction.close();
ExecutorService executor = Executors.newFixedThreadPool(5);
while(iterator.hasNext()){
executor.execute(new MyJob(iterator.next()));
}
//wait until all threads are done
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
这里是可运行的 class
private class MyJob implements Runnable {
private Node node;
/* collect useful parameters in the constructor */
public MyJob(Node node) {
this.node = node;
}
public void run() {
Transaction transaction = service.beginTx();
Iterable<org.neo4j.graphdb.Relationship> rel = this.node.getRelationships(RelationType.POINTSREL, Direction.OUTGOING);
double sum = 0;
for(org.neo4j.graphdb.Relationship entry : rel){
try{
sum += (Double)entry.getEndNode().getProperty("Points");
} catch(Exception e){
e.printStackTrace();
}
}
this.node.setProperty("Sum", sum);
transaction.success();
transaction.close();
}
}
有更好(更快)的方法吗?
关于我的设置: 具有 8 个 CPU 和 32GB 内存的 AWS 实例
neo4j-wrapper.conf
# Java Heap Size: by default the Java heap size is dynamically
# calculated based on available system resources.
# Uncomment these lines to set specific initial and maximum
# heap size in MB.
wrapper.java.initmemory=16000
wrapper.java.maxmemory=16000
neo4j.properties
# The type of cache to use for nodes and relationships.
cache_type=soft
cache.memory_ratio=30.0
neostore.nodestore.db.mapped_memory=2G
neostore.relationshipstore.db.mapped_memory=7G
neostore.propertystore.db.mapped_memory=2G
neostore.propertystore.db.strings.mapped_memory=2G
neostore.propertystore.db.arrays.mapped_memory=512M
在我看来,有些地方可以改进。
题外话
如果您使用 Java 7(或更高版本),请考虑使用 try with resource 来处理事务。它将防止您出错。
性能
首先 - 批处理。目前您是:
- 正在创建工作
- 启动线程(其实executor中有pool)
- 开始交易
对于每个节点。您应该考虑 批量 进行更新。这意味着您应该:
- 收集
N
个节点(即N
=1000) - 为
N
个节点创建单个作业 - 在作业中创建单个事务
- 更新该交易中的
N
个节点 - 关闭交易
设置
您有 8 个 CPU。这意味着您可以创建更大的线程池。我觉得Executors.newFixedThreadPool(16)
就可以了
黑客
您有 32GB 内存。我可以建议:
- 将 java 堆大小减少到 8GB。根据我的经验,大堆大小会导致大量 GC 暂停和性能下降
- 创建映射内存大小。只是为了确保更多的数据可以保留在缓存中。
只为你的情况。如果 所有 您的数据都可以放入 RAM,那么您可以将 cache_type
更改为 hard
以实现此更改目的。 Details.
配置
如您所说 - 您正在使用 Core API。这是 Embedded
图数据库还是服务器 extension
?
如果这是 Embedded
图形数据库 - 您应该验证您的数据库设置是否应用于创建的实例。
我发现 属性 "cache_type=soft" 存在问题。我将它设置为 "cache_type=none",执行时间从 30 分钟减少到 2 分钟。在一些更新之后,总是有线程被阻塞大约 30 秒 - 更改此 属性 有助于避免这些阻塞。我会搜索更详细的解释。