我一直在努力使用 Neo4J 3.0.3 为我的应用程序实现可接受的性能。这里有一些背景:
我正在尝试用 Neo4j 替换 Apache Solr,以扩展其功能,同时保持或提高性能。
在 Solr 中,我的文档基本上看起来像这样:
{
"time": "2015-08-05T00:16:00Z",
"point": "45.8300018311,-129.759994507",
"sea_water_temperature": 18.49,
"sea_water_temperature_depth": 4,
"wind_speed": 6.48144,
"eastward_wind": 5.567876,
"northward_wind": -3.3178043,
"wind_depth": -15,
"sea_water_salinity": 32.19,
"sea_water_salinity_depth": 4,
"platform": 1,
"mission": 1,
"metadata": "KTDQ_20150805v20001_0016"
}
由于 Solr 是一个键值对数据存储,我对 Neo4J 的初始转换将很简单,因此我可以对使用 API 有所了解。
我的方法本质上是让每个 Solr 记录等同于一个 Neo4J 节点,其中每个键值都将成为一个节点属性。
显然需要进行一些调整(将 None 更改为“None”(python),将 ISO 时间更改为纪元时间(neo4j 不支持索引日期时间),将点更改为 lat/lon(neo4j 空间索引)等)。
我的目标是使用这个模型加载 Neo4J,不管它有多么幼稚。
这是我在加载单个记录时进行的休息调用的示例(使用 http:localhost:7474/db/data/cypher 作为我的端点):
{
"query" :
"CREATE (r:record {lat : {lat}, SST : {SST}, meta : {meta}, lon : {lon}, time : {time}}) RETURN id(r);",
"params": {
"lat": 40.1021614075,
"SST": 6.521100044250488,
"meta": "KCEJ_20140418v20001_1430",
"lon": -70.8780212402,
"time": 1397883480
}
}
请注意,我实际上已经删除了很多用于测试 neo4j 的参数。
目前我有严重的性能问题。对我来说,将这样的文档加载到 Solr 大约需要 2 秒。对于 Neo4J,它需要:
使用 REST API 约 20 秒
使用 BOLT 约 45 秒
使用 py2neo 约 70 秒
我有大约 50,000,000 条记录需要加载。在 Solr 中执行此操作通常需要 24 小时,因此 Neo4J 可能需要将近一个月!
我记录了这些时间,没有对我的“元”属性使用唯一性约束,也没有将每个节点添加到空间索引中。这种情况下的时间结果非常糟糕。
遇到这个问题,我尝试在线搜索性能调整。以下事情并没有改善我的情况:
- 将打开文件限制从 1024 增加到 40000
- 使用 ext4,并按照此处记录的方式对其进行调整
- 将页面缓存大小增加到 16 GB(我的系统有 32 个)
到目前为止,我只讨论了加载时间。在我一夜之间加载了大约 50,000 个节点后,我尝试对我的空间索引进行查询,如下所示:
CALL spatial.withinDistance('my_layer', lon : 34.0, lat : 20.0, 1000)
以及我的时间索引,如下所示:
MATCH (r:record) WHERE r.time > {} AND r.time < {} RETURN r;
这些简单的查询实际上需要几分钟,可能只返回几个节点。
在 Apache Solr 中,空间索引非常快并且在 5 秒内响应(即使加载了所有 50000000 个文档)。
在这一点上,我担心这种性能滞后是否是由于我的数据模型的性质、我的服务器的配置等造成的。
我的目标是从这个模型中推断出来,并将几种测量类型移动到它们自己的节点类中,并创建从我的基本记录节点到这些的关系。
我是否有可能滥用 Neo4j,并且需要重新创建此模型以使用关系和几种不同的节点类型?我应该期待看到显着的改善吗?
作为旁注,我最初计划使用三重存储(特别是议会)来存储这些数据,在努力使用 RDF 之后,我认为 Neo4J 看起来很有前途,而且更容易启动和运行。回到 RDF 是否值得?
欢迎任何建议,提示,评论。先感谢您。
编辑:
正如评论中所建议的,我已经改变了加载脚本的行为。
以前我以这种方式使用python:
from neo4j.v1 import GraphDatabase
driver = GraphDatabase('http://localhost:7474/db/data')
session = driver.session()
for tuple in mydata:
statement = build_statement(tuple)
session.run(statement)
session.close()
使用这种方法,实际的 .run() 语句几乎可以立即运行。.close() 语句是所有运行时间发生的地方。
我修改的方法:
transaction = ''
for tuple in mydata:
statement = build_statement(tuple)
transaction += ('\n' + statement)
with session.begin_transaction() as tx:
tx.run(transaction)
session.close()
我有点困惑,因为它的行为几乎相同。.close() 仍然需要大约 45 秒,只是它没有提交。由于我在每个语句中重用相同的标识符(CREATE (r:record {...}) .... CREATE (r:record {...}) ...),因此我收到了有关此的 CypherError行为。目前我真的不知道如何避免这个问题,此外,运行时间似乎根本没有改善(我希望一个错误实际上会使这个终止更快)。