NEO4J亿级数据全文索引构建优化

2024-02-25 06:38

本文主要是介绍NEO4J亿级数据全文索引构建优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

NEO4J亿级数据全文索引构建优化

  • 一、数据量规模(亿级)
  • 二、构建索引的方式
  • 三、构建索引发生的异常
  • 四、全文索引代码优化
    • 1、Java.lang.OutOfMemoryError
    • 2、访问数据库时
    • 3、优化方案
    • 4、优化代码
    • 5、执行效率测试

如果使用基于NEO4J的全文检索作为图谱的主要入口,那么做好图谱搜索引擎的优化是非常关键的。

一、数据量规模(亿级)

count(relationships):500584016

count(nodes):765485810

二、构建索引的方式

使用脚本后服务器台执行构建全文索引的操作。
使用后台脚本执行构建索引程序:

index.sh
#!/usr/bin/env bash
nohup /neo4j-community-3.4.9/bin/neo4j-shell -file build.cql >>indexGraph.log 2>&1 &
build.cql
CALL zdr.index.addChineseFulltextIndex('IKAnalyzer', ['description','fullname','name','lnkurl'], 'LinkedinID') YIELD message RETURN message;

三、构建索引发生的异常

ERROR (-v for expanded information):TransactionFailureException: The database has encountered a critical error, and needs to be restarted. Please see database logs for more details.-host      Domain name or IP of host to connect to (default: localhost)-port      Port of host to connect to (default: 1337)-name      RMI name, i.e. rmi://<host>:<port>/<name> (default: shell)-pid       Process ID to connect to-c         Command line to execute. After executing it the shell exits-file      File containing commands to execute, or '-' to read from stdin. After executing it the shell exits-readonly  Connect in readonly mode (only for connecting with -path)-path      Points to a neo4j db path so that a local server can be started there-config    Points to a config file when starting a local serverExample arguments for remote:-port 1337-host 192.168.1.234 -port 1337 -name shell-host localhost -readonly...or no arguments for default values
Example arguments for local:-path /path/to/db-path /path/to/db -config /path/to/neo4j.config-path /path/to/db -readonly
Caused by: java.lang.OutOfMemoryError: Java heap space | GB+Tree[file:/u02/isi/zdr/graph/neo4j-community-3.4.9/data/databases/graph.db/schema/index/lucene_native-2.0/134/string-1.0/index-134, layout:StringLayout[version:0.1, identifier:24016946018123776], generation:16587/16588]at org.neo4j.io.pagecache.impl.muninn.CursorFactory.takeWriteCursor(CursorFactory.java:62)at org.neo4j.io.pagecache.impl.muninn.MuninnPagedFile.io(MuninnPagedFile.java:186)at org.neo4j.index.internal.gbptree.FreeListIdProvider.releaseId(FreeListIdProvider.java:217)at org.neo4j.index.internal.gbptree.InternalTreeLogic.createSuccessorIfNeeded(InternalTreeLogic.java:1289)at org.neo4j.index.internal.gbptree.InternalTreeLogic.insertInLeaf(InternalTreeLogic.java:513)at org.neo4j.index.internal.gbptree.InternalTreeLogic.insert(InternalTreeLogic.java:356)at org.neo4j.index.internal.gbptree.GBPTree$SingleWriter.merge(GBPTree.java:1234)at org.neo4j.kernel.impl.index.schema.NativeSchemaIndexUpdater.processAdd(NativeSchemaIndexUpdater.java:132)at org.neo4j.kernel.impl.index.schema.NativeSchemaIndexUpdater.processUpdate(NativeSchemaIndexUpdater.java:86)at org.neo4j.kernel.impl.index.schema.NativeSchemaIndexUpdater.process(NativeSchemaIndexUpdater.java:61)at org.neo4j.kernel.impl.index.schema.fusion.FusionIndexUpdater.process(FusionIndexUpdater.java:41)at org.neo4j.kernel.impl.api.index.updater.DelegatingIndexUpdater.process(DelegatingIndexUpdater.java:40)at org.neo4j.kernel.impl.api.index.IndexingService.processUpdate(IndexingService.java:516)at org.neo4j.kernel.impl.api.index.IndexingService.apply(IndexingService.java:479)at org.neo4j.kernel.impl.api.index.IndexingService.apply(IndexingService.java:463)at org.neo4j.kernel.impl.transaction.command.IndexUpdatesWork.apply(IndexUpdatesWork.java:63)at org.neo4j.kernel.impl.transaction.command.IndexUpdatesWork.apply(IndexUpdatesWork.java:42)at org.neo4j.concurrent.WorkSync.doSynchronizedWork(WorkSync.java:231)at org.neo4j.concurrent.WorkSync.tryDoWork(WorkSync.java:157)at org.neo4j.concurrent.WorkSync.apply(WorkSync.java:91)

JAVA代码实现索引

    /*** @param* @return* @Description: TODO(构建索引并返回MESSAGE - 不支持自动更新)*/private String chineseFulltextIndex(String indexName, String labelName, List<String> propKeys) {Label label = Label.label(labelName);// 按照标签找到该标签下的所有节点ResourceIterator<Node> nodes = db.findNodes(label);System.out.println("nodes:" + nodes.toString());int nodesSize = 0;int propertiesSize = 0;// 循环存在问题 更新到3000万之后程序开始卡顿while (nodes.hasNext()) {nodesSize++;Node node = nodes.next();System.out.println("current nodes:" + node.toString());// 每个节点上需要添加索引的属性Set<Map.Entry<String, Object>> properties = node.getProperties(propKeys.toArray(new String[0])).entrySet();System.out.println("current node properties" + properties);// 查询该节点是否已有索引,有的话删除if (db.index().existsForNodes(indexName)) {Index<Node> oldIndex = db.index().forNodes(indexName);System.out.println("current node index" + oldIndex);oldIndex.remove(node);}// 为该节点的每个需要添加索引的属性添加全文索引Index<Node> nodeIndex = db.index().forNodes(indexName, FULL_INDEX_CONFIG);for (Map.Entry<String, Object> property : properties) {propertiesSize++;nodeIndex.add(node, property.getKey(), property.getValue());}// 计算耗时}String message = "IndexName:" + indexName + ",LabelName:" + labelName + ",NodesSize:" + nodesSize + ",PropertiesSize:" + propertiesSize;return message;}

四、全文索引代码优化

1、Java.lang.OutOfMemoryError

Java.lang.OutOfMemory是java.lang.VirtualMachineError的一个子类,当Java虚拟机中断,或是超出可用资源时抛出。

2、访问数据库时

访问数据库时程序会获取锁和内存,在事务没有被完成之前锁和内存是不会释放的。因此现在很容易理解上述BUG的出现的原因。(三)实现的索引程序中,是获取节点之后在WHILE循环中执行构建索引,直到索引构建完毕事务才会自动被关闭,自动执行内存回收等操作。当获取的数据量巨大的时候,必然会出现内存溢出。

3、优化方案

使用批量事务提交的机制。

4、优化代码

 /*** @param* @return* @Description: TODO(构建索引并返回MESSAGE - 不支持自动更新)*/private String chineseFulltextIndex(String indexName, String labelName, List<String> propKeys) {Label label = Label.label(labelName);int nodesSize = 0;int propertiesSize = 0;// 按照标签找到该标签下的所有节点ResourceIterator<Node> nodes = db.findNodes(label);Transaction tx = db.beginTx();try {int batch = 0;long startTime = System.nanoTime();while (nodes.hasNext()) {nodesSize++;Node node = nodes.next();boolean indexed = false;// 每个节点上需要添加索引的属性Set<Map.Entry<String, Object>> properties = node.getProperties(propKeys.toArray(new String[0])).entrySet();// 查询该节点是否已有索引,有的话删除if (db.index().existsForNodes(indexName)) {Index<Node> oldIndex = db.index().forNodes(indexName);oldIndex.remove(node);}// 为该节点的每个需要添加索引的属性添加全文索引Index<Node> nodeIndex = db.index().forNodes(indexName, FULL_INDEX_CONFIG);for (Map.Entry<String, Object> property : properties) {indexed = true;propertiesSize++;nodeIndex.add(node, property.getKey(), property.getValue());}// 批量提交事务if (indexed) {if (++batch == 50_000) {batch = 0;tx.success();tx.close();tx = db.beginTx();// 计算耗时startTime = indexConsumeTime(startTime, nodesSize, propertiesSize);}}}tx.success();// 计算耗时indexConsumeTime(startTime, nodesSize, propertiesSize);} finally {tx.close();}String message = "IndexName:" + indexName + ",LabelName:" + labelName + ",NodesSize:" + nodesSize + ",PropertiesSize:" + propertiesSize;return message;}

5、执行效率测试

50_000为批次进行提交,依次累加nodeSize和propertieSize,consume还是每批提交的耗时。
可以看到在刚开始提交的时候耗时较多,之后基本上稳定在每批提交耗时:2s~5s/5万条。10亿nodes,耗时估算11h~23h之间。

Build index-nodeSize:50000,propertieSize:148777,consume:21434ms
Build index-nodeSize:100000,propertieSize:297883,consume:18493ms
Build index-nodeSize:150000,propertieSize:446936,consume:17140ms
Build index-nodeSize:200000,propertieSize:595981,consume:17323ms
Build index-nodeSize:250000,propertieSize:745039,consume:19680ms
Build index-nodeSize:300000,propertieSize:894026,consume:18451ms
Build index-nodeSize:350000,propertieSize:1042994,consume:20266ms
Build index-nodeSize:400000,propertieSize:1160186,consume:12787ms
Build index-nodeSize:450000,propertieSize:1210186,consume:1946ms
Build index-nodeSize:500000,propertieSize:1260186,consume:3174ms
Build index-nodeSize:550000,propertieSize:1310186,consume:3090ms
Build index-nodeSize:600000,propertieSize:1360186,consume:3063ms
Build index-nodeSize:650000,propertieSize:1410186,consume:1868ms
Build index-nodeSize:700000,propertieSize:1460186,consume:2036ms
Build index-nodeSize:750000,propertieSize:1510186,consume:3784ms
Build index-nodeSize:800000,propertieSize:1560186,consume:3037ms
Build index-nodeSize:850000,propertieSize:1610186,consume:2627ms
Build index-nodeSize:900000,propertieSize:1660186,consume:1900ms
Build index-nodeSize:950000,propertieSize:1710186,consume:2944ms
Build index-nodeSize:1000000,propertieSize:1760186,consume:3369ms
Build index-nodeSize:1050000,propertieSize:1810186,consume:3289ms
Build index-nodeSize:1100000,propertieSize:1860186,consume:2763ms
Build index-nodeSize:1150000,propertieSize:1910186,consume:3237ms
Build index-nodeSize:1200000,propertieSize:1960186,consume:3408ms
Build index-nodeSize:1250000,propertieSize:2010186,consume:3644ms
Build index-nodeSize:1300000,propertieSize:2060186,consume:3661ms
Build index-nodeSize:1350000,propertieSize:2110186,consume:2964ms
Build index-nodeSize:1400000,propertieSize:2160186,consume:3219ms
Build index-nodeSize:1450000,propertieSize:2210186,consume:3356ms
Build index-nodeSize:1500000,propertieSize:2260186,consume:4115ms
Build index-nodeSize:1550000,propertieSize:2310186,consume:3188ms
Build index-nodeSize:1600000,propertieSize:2360186,consume:3364ms
Build index-nodeSize:1650000,propertieSize:2410186,consume:3799ms
Build index-nodeSize:1700000,propertieSize:2460186,consume:4301ms
Build index-nodeSize:1750000,propertieSize:2510186,consume:3772ms
Build index-nodeSize:1800000,propertieSize:2560186,consume:3692ms
Build index-nodeSize:1850000,propertieSize:2610186,consume:3428ms
Build index-nodeSize:1900000,propertieSize:2660186,consume:2930ms

备注:在本次测试的数据集上执行索引构建2小时之后,此时已经被索引了1495万个NODES,速度下降明显,需要进一步优化。

Build index-nodeSize:13850000,propertieSize:14610186,consume:97290ms
Build index-nodeSize:13900000,propertieSize:14660186,consume:7441ms
Build index-nodeSize:13950000,propertieSize:14710186,consume:3730ms
Build index-nodeSize:14000000,propertieSize:14760186,consume:3512ms
Build index-nodeSize:14050000,propertieSize:14810186,consume:4545ms
Build index-nodeSize:14100000,propertieSize:14860186,consume:12100ms
Build index-nodeSize:14150000,propertieSize:14910186,consume:83071ms
Build index-nodeSize:14200000,propertieSize:14960186,consume:7417ms
Build index-nodeSize:14250000,propertieSize:15010186,consume:3579ms
Build index-nodeSize:14300000,propertieSize:15060186,consume:64841ms
Build index-nodeSize:14350000,propertieSize:15110186,consume:7553ms
Build index-nodeSize:14400000,propertieSize:15160186,consume:63141ms
Build index-nodeSize:14450000,propertieSize:15210186,consume:64316ms
Build index-nodeSize:14500000,propertieSize:15260186,consume:187510ms
Build index-nodeSize:14550000,propertieSize:15310186,consume:247571ms
Build index-nodeSize:14600000,propertieSize:15360186,consume:224611ms
Build index-nodeSize:14650000,propertieSize:15410186,consume:244539ms
Build index-nodeSize:14700000,propertieSize:15460186,consume:354684ms
Build index-nodeSize:14750000,propertieSize:15510186,consume:236970ms
Build index-nodeSize:14800000,propertieSize:15560186,consume:308532ms
Build index-nodeSize:14850000,propertieSize:15610186,consume:429815ms
Build index-nodeSize:14900000,propertieSize:15660186,consume:409451ms
Build index-nodeSize:14950000,propertieSize:15710186,consume:456980ms

构建程序在运行4个小时之后,被索引了1530万NODES,索引构建速度几乎慢到不可接受,持续优化中…

Build index-nodeSize:14750000,propertieSize:15510186,consume:236970ms
Build index-nodeSize:14800000,propertieSize:15560186,consume:308532ms
Build index-nodeSize:14850000,propertieSize:15610186,consume:429815ms
Build index-nodeSize:14900000,propertieSize:15660186,consume:409451ms
Build index-nodeSize:14950000,propertieSize:15710186,consume:456980ms
Build index-nodeSize:15000000,propertieSize:15760186,consume:447474ms
Build index-nodeSize:15050000,propertieSize:15810186,consume:580270ms
Build index-nodeSize:15100000,propertieSize:15860186,consume:840488ms
Build index-nodeSize:15150000,propertieSize:15910186,consume:573554ms
Build index-nodeSize:15200000,propertieSize:15960186,consume:748670ms
Build index-nodeSize:15250000,propertieSize:16010186,consume:1305363ms
Build index-nodeSize:15300000,propertieSize:16060186,consume:2495139ms

上述测试案例的源码位置

这篇关于NEO4J亿级数据全文索引构建优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/744666

相关文章

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

C#使用iText获取PDF的trailer数据的代码示例

《C#使用iText获取PDF的trailer数据的代码示例》开发程序debug的时候,看到了PDF有个trailer数据,挺有意思,于是考虑用代码把它读出来,那么就用到我们常用的iText框架了,所... 目录引言iText 核心概念C# 代码示例步骤 1: 确保已安装 iText步骤 2: C# 代码程

Pandas处理缺失数据的方式汇总

《Pandas处理缺失数据的方式汇总》许多教程中的数据与现实世界中的数据有很大不同,现实世界中的数据很少是干净且同质的,本文我们将讨论处理缺失数据的一些常规注意事项,了解Pandas如何表示缺失数据,... 目录缺失数据约定的权衡Pandas 中的缺失数据None 作为哨兵值NaN:缺失的数值数据Panda

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

使用Node.js和PostgreSQL构建数据库应用

《使用Node.js和PostgreSQL构建数据库应用》PostgreSQL是一个功能强大的开源关系型数据库,而Node.js是构建高效网络应用的理想平台,结合这两个技术,我们可以创建出色的数据驱动... 目录初始化项目与安装依赖建立数据库连接执行CRUD操作查询数据插入数据更新数据删除数据完整示例与最佳

python库pydantic数据验证和设置管理库的用途

《python库pydantic数据验证和设置管理库的用途》pydantic是一个用于数据验证和设置管理的Python库,它主要利用Python类型注解来定义数据模型的结构和验证规则,本文给大家介绍p... 目录主要特点和用途:Field数值验证参数总结pydantic 是一个让你能够 confidentl

JAVA实现亿级千万级数据顺序导出的示例代码

《JAVA实现亿级千万级数据顺序导出的示例代码》本文主要介绍了JAVA实现亿级千万级数据顺序导出的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 前提:主要考虑控制内存占用空间,避免出现同时导出,导致主程序OOM问题。实现思路:A.启用线程池

Docker多阶段镜像构建与缓存利用性能优化实践指南

《Docker多阶段镜像构建与缓存利用性能优化实践指南》这篇文章将从原理层面深入解析Docker多阶段构建与缓存机制,结合实际项目示例,说明如何有效利用构建缓存,组织镜像层次,最大化提升构建速度并减少... 目录一、技术背景与应用场景二、核心原理深入分析三、关键 dockerfile 解读3.1 Docke

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性