高效地将wikidata导入neo4j(附代码)

核心思想

核心思想其实很简单,就是一定要用Batch Inserter来导入!

Batch Inserter是neo4j的java interface中的一个包,内含将数据批量导入neo4j的一些接口。其能够实现快速导入的原因有二:

  1. 其采用批量导入而非一个一个的导入,所以很多内部操作可以合并
  2. 其绕开了neo4j中包含constraints在内的多项检验,并采用先导入数据后建立索引的方式(如果需要建索引的话),所以导入数据就快了很多。但代价是不保证数据的一致性,同时其并不是多线程安全的。用户需要保证插入的数据不会破坏数据库的constraints。

导入流程

核心思想明确后,剩下的就好办了。这里,我们采用两步走的方式来导入数据(扫描两次wikidata dump):

  1. 导入所有的节点,这里的节点包括wikidata中的item以及property。这里把property也作为node纯粹是为了查询方便,以及解约存储空间。(我希望item之间的边只包含property的id,需要property具体信息时,再用这个id查找相应的property节点即可)
  2. 导入所有的边

之所以要采用两步走,是因为我觉得如果扫描一次wikidaa dump就完成所有操作的话,就必然会在建边时碰见之前没插入的节点。这时,就需要先插入一个只包含id的节点,之后遇到该节点的详细信息时再补上。这样会增加查询开销,同时可能也涉及到存储的空间不连贯性的问题。

但实际做的时候发现,貌似大部分时间花在了解析json字符串上,因为我发现插入边跟插入节点这两部用时差不多,所以可能扫描一次wikidata dump是更好的选择。不过,暂时也懒得具体做实验了,大家有兴趣可以试一试。

统计数据

我导入的是2016年1月18日的dump,其json的bz2压缩包约4G,导入后,数据库文件约4.2G

这里,我没有全部导入。只导入了英文部分,同时忽略了引用,property之间的关系,以及item的property中,客体不是item的部分(比如,image(P18))

速度的话,总共耗时3小时,其中插入节点用了5113秒,插入边用了6018秒。

大概一秒可以处理2500个item。其中,处理速度先慢后快,开始时大概1秒1000个,之后慢慢加快,最快时可以1秒5000个。

代码

代码是用java写的,放在github上了,大家可以下载下来玩一玩。地址点我

代码中我没有建任何索引,需要的话,在导入完成后,用Cypher建就好,还是挺快的,比如:

create index on :Item(wikidataId)

你可能感兴趣的:(neo4j,wikidata)