neo4j 的插入速度为什么越来越慢,可能是使用了过多图谱查询操作

news2024/11/20 13:40:45

文章目录

    • 背景描述
    • 分析
    • 解决
    • 代码参考
    • neo4j 工具类
      • Neo4jDriver
      • 知识图谱构建效果
      • GuihuaNeo4jClass

背景描述

使用 tqdm 显示,处理的速度;

笔者使用 py2neo库,调用 neo4j 的API 完成节点插入;
有80万条数据需要插入到neo4j图数据中,在前期处理速度200条每秒,随着程序的运行处理速度越来越慢,200 -> 100 -> 50 -> 30,速度一直降低到每秒处理30条数据;

如果保持原来的速度,1个小时就处理完了,现在就得花费8个小时才能处理完成;

分析

那么到底是什么原因导致速度会越来越慢?
笔者分析之后是因为:笔者会给节点创建关系,首先需要在neo4j图数据库中查询到该节点,再给该节点创建关系。随着图数据库中的节点数量越来越多,就导致查询时间过长,从而形成了随着程序运行插入节点速度变慢的现象。

解决

有很多种办法解决这个问题:

  • 记忆背包
    采用记忆背包的办法,将已经创建过的节点,保存在字典中。再给该节点创建关系或者属性时,不再从图谱中查询,而是直接从字典中获取;

  • 减少重复查询操作
    尽量减少多次重复的查询操作。
    假如有一个实体的属性表、有一个关系表;为了保证代码的低耦合,通常咱们先往知识图谱中,插入完成属性表;再查询节点,再给该实体插入关系。很明显第二次的节点查询就是重复的查询。
    完全可以考虑,一次性就完成节点属性添加和关系链接操作,这就能减少了一次查询操作;

  • 是否需要创建新实体
    通常创建实体时,先在图谱中查询是否有该节点,如果图谱中有则不创建,使用查询得到的节点;
    如果咱们只是想表示某个节点他有哪些关系,那么节点不唯一也可以考虑,那么便不再理会图谱中是否已有该节点,直接创建该节点,然后建立关系即可。

代码参考

我使用下述代码,一次性完成属性添加、孩子节点创建与关系链接;从而实现减少图谱的查询操作;
实现了将原本需要耗费8个小时以上的时间,缩短到2个小时完成neo4j图数据库的插入;

neo4j 工具类

我写的neo4j 工具类如下

  • Neo4jDriver: 可以直接拿去使用;
  • GuihuaNeo4jClass: 笔者自己项目的一个类,无法直接供大家使用,供大家参考;

Neo4jDriver

import json
from dataclasses import dataclass

from py2neo import Graph, Node, NodeMatcher, RelationshipMatcher, Relationship

from settings import domain_class_name, summary_class_name, ner_schema


# 连接到Neo4j数据库
class Neo4jDriver:
    def __init__(self, url, username, password):
        self.graph = Graph(url, auth=(username, password))
        self.node_matcher = NodeMatcher(self.graph)
        self.relationship_matcher = RelationshipMatcher(self.graph)

    def query_node(self, class_, **kwargs):
        if node := self.node_matcher.match(class_, **kwargs):
            # 节点存在,则获取
            return node.first()

    def create_query_node(self, class_, **kwargs):
        """
        不创建重复节点
        """
        # 节点存在,则获取
        if node := self.query_node(class_, **kwargs):
            return node
        # 节点不存在,则创建
        node = Node(class_, **kwargs)
        self.graph.create(node)
        return node
    
    def create_node(self, class_, **kwargs):
        node = Node(class_, **kwargs)
        self.graph.create(node)
        return node
        
    def query_relationship(self, start_node, rel, end_node):
        r = self.relationship_matcher.match([start_node, end_node], r_type=rel)
        return r.first()

    def create_query_relationship(self, start_node, rel, end_node):
        if r := self.query_relationship(start_node, rel, end_node):
            return r
        self.graph.create(Relationship(start_node, rel, end_node))
    
    def create_relationship(self, start_node, rel, end_node):
        self.graph.create(Relationship(start_node, rel, end_node))

知识图谱构建效果

请添加图片描述上图的schema如下:

保山市: file
加快建...是:sentence
最右侧的一列节点是sentence的下面的实体

GuihuaNeo4jClass

简要给大家分享一下,编写GuihuaNeo4jClass的思路:

all_file_node = {}
all_sents_node = {}
all_ner_node = {} # key: (ner_class, name)

如下字典为所有 GuihuaNeo4jClass 实例共有(都可以访问)的字典,用于存储在构建neo4j的过程中,创建的一些节点,这样就无需走neo4j的查询,直接走本地字典的查询,速度会快一点;

下述为创建GuihuaNeo4jClass类,初始化时,需要传入的一些参数:

driver: Neo4jDriver
text: str
prov: str
city: str
filename: str

方法讲解:
get_file_node

def get_file_node(self):
		# 首先在self.all_file_node,查询是否已经有该节点,如果有则直接从字典获取该节点
    if self.filename in self.all_file_node.keys():
        return self.all_file_node[self.filename]
    
    # 字典中没有该节点,表示该节点还没有创建过,准备创建该节点
    # data 字典里面存储的是该节点的一些属性信息
    data = {"name": self.filename, "prov": self.prov, "city": self.city}
    self.file_node = self.driver.create_node("file", **data)

    # 新节点创建完成,将该节点保存到 GuihuaNeo4jClass 的文件字典 self.all_file_node中;
    # self.all_file_node 是每个实例共享的一个类字典;
    self.all_file_node[self.filename] = self.file_node
    return self.file_node

get_sentence_node: 与get_file_node 类似;
add_class: 创建sentence节点,并创建 file -> sentence的关系;
add_ner: 创建在schema定义的一些实体节点,并创建 sentence -> ner_node 的关系;

@dataclass()
class GuihuaNeo4jClass:
    all_file_node = {}
    all_sents_node = {}
    all_ner_node = {} # key: (ner_class, name)


    driver: Neo4jDriver
    text: str
    prov: str
    city: str
    filename: str

    def get_file_node(self):
        if self.filename in self.all_file_node.keys():
            return self.all_file_node[self.filename]
        
        data = {"name": self.filename, "prov": self.prov, "city": self.city}
        self.file_node = self.driver.create_node("file", **data)

        # save
        self.all_file_node[self.filename] = self.file_node
        return self.file_node
        

    def get_sentence_node(self, **kwargs):
        if self.text in self.all_sents_node.keys():
            return self.all_sents_node[self.text]
        
        for key in kwargs.keys():
            assert key in [domain_class_name, summary_class_name]
        data = {
            "sentence": self.text,
        }
        data.update(kwargs)
        node = self.driver.create_node("sentence", **data)
        self.sent_node = node

        # save
        self.all_sents_node[self.text] = self.sent_node
        return self.sent_node

    def add_class(self, domain_info, summary_info):
        self.file_node = self.get_file_node()
        # 给file下面添加text, rel: has_rel
        domain_output = domain_info.outputs[0].text
        summary_output = summary_info.outputs[0].text
        class_data = {
            domain_class_name: domain_output,
            summary_class_name: summary_output,
        }
        self.sent_node = self.get_sentence_node(**class_data)
        self.driver.create_relationship(self.file_node, "has_sentence", self.sent_node)

    def add_ner(self, ner_info):
        """
            给文本句创建节点
            建立一个静态的 llm_ner_label
        """
        try:
            llm_ner_label = ner_info.outputs[0].text
            llm_ner_label = json.loads(llm_ner_label)
        except:
            return
        if not isinstance(llm_ner_label, dict):
            return
        for key, values in llm_ner_label.items():
            if not key in ner_schema:
                continue
            for name in values:
                if (key, name) in self.all_ner_node.keys():
                    ner_node = self.all_ner_node[(key, name)]
                else:
                    ner_node = self.driver.create_node(key, name=name)
                    # save
                    self.all_ner_node[(key, name)] = ner_node
                self.driver.create_relationship(self.sent_node, 'has_ner', ner_node)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1641870.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM的校园短期闲置资源置换平台(有报告)。Javaee项目。ssm项目。

演示视频: 基于SSM的校园短期闲置资源置换平台(有报告)。Javaee项目。ssm项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,通过…

c语言从入门到函数速成(2)

温馨提醒:本篇文章适合人群:刚学c又感觉那个地方不怎么懂的同学以及以及学了一些因为自身原因停学一段时间后又继续学​​​c的学 好,正片开始! 数组 概念:数组中存放的是1个或者多个数据,但是数组元素个…

Topaz Video AI 5.0.3激活版 AI视频无损缩放增强

Topaz Video AI专注于很好地完成一些视频增强任务:去隔行,放大和运动插值。我们花了五年时间制作足够强大的人工智能模型,以便在真实世界的镜头上获得自然的结果。 Topaz Video AI 还将充分利用您的现代工作站,因为我们直接与硬件…

Redis核心数据结构——跳表(生成数据到文件和从文件中读取数据、模块合并、)

生成文件和从文件中读取数据。 需求如下: 你的任务是实现 SkipList 类中的数据持久化成员函数和数据加载成员函数。 持久化数据成员函数签名:void dump_file(); 该成员函数负责将存储引擎内的数据持久化到文件中。数据的持久化格式是将每个键值对写入文…

封装umi-request时通过 AbortController 配置取消请求

一、关键部分 一、在封装的request.ts中 声明一个 abortControllers 对象用于存储要取消的请求(我用了-s表示复数,多个abortcontroller对象,与下面👇的单个abortController区分)封装取消请求的函数cancelRequest, 传入…

GraphPad Prism:医学绘图的革命性工具,Mac/Win双平台首选

在医学研究的道路上,数据的可视化与分析是不可或缺的一环。GraphPad Prism,作为一款专业的医学绘图工具,正以其强大的功能和卓越的性能,在Mac与Win双平台上引领着医学绘图的新潮流。 GraphPad Prism不仅仅是一款绘图软件&#xf…

Ubuntu22.04有线网络连接但是没网,网络图标显示问号

Ubuntu22.04有线网络连接但是没网,网络图标显示问号 问题描述解决方案 问题描述 有线网络连接 但是没网 且网络图标显示问号 解决方案 进入设置->隐私->在 连接情况 中关闭连接检查 关闭后 网络正常

使用Qt for android 获取android PDA设备扫码数据

安装Qt Android 模块、Qt Creator、JDK8 Qt Creator版本太高不行,亲测最新版的会导致无法使用jdk 8(2024-05-04 我的Qt版本是5.15.2,所以我选择了Qt Creator5.0.3进行开发 5.0.3 下载JDK8 JDK8 安装Qt Creator、JDK8 安装Qt Creator没什么…

【银角大王——Django课程——用户表的基本操作2】

用户表的基本操作2 编辑用户按钮删除按钮入职日期——不显示时分,只显示年月日——使用DataField函数不使用DateTimeField修改models记得重新执行命令,更新数据库结构修改前修改后 编辑用户按钮 点击编辑,跳转到编辑页面(将编辑的…

函数之对决!!:数学函数 VS C++函数

前言 有人问过我,C里的函数是否跟我们数学里的函数一样?于是,我就写下了这篇文章。 一、数学函数 1、一次函数 一次函数,也称为线性函数,是数学中函数的一种,通常表示为ykxb(其中k和b是常数&am…

springboot+vue中小学文具商城购物系统网站

技术栈 前端:vue.jsElementUI 开发工具:IDEA 或者eclipse都支持 编程语言: java 框架: ssm/springboot 数据库: mysql 版本不限 数据库工具:Navicat/SQLyog都可以 详细技术:javaspringbootvueMYSQLMAVEN文具网站为用户…

jQuery的简单使用

jQuery的简单使用 jQuery查找父、子、兄弟节点jQuery查找内容元素筛选遍历元素操作元素width() / height() 设置宽高.css() 设值样式attr() / prop() 设置属性增加、删除、切换class删除和清空 操作元素总结选择表达式链式操作取值和赋值函数 HTML_1 <table id"table_…

第16章 基于结构的测试技术(白盒测试技术)

一、静态测试技术 &#xff08;一&#xff09;概述 不运行程序代码的情况下&#xff0c;通过质量准则或其他准则对测试项目进行检查的测试类型&#xff0c;人工或工具检查。 1、代码检查 2、编码规则检查 3、静态分析 静态分析概述 不需要执行程序 控制流分析 通过生成…

高边沿开关LM5050

可以用于高边沿单片机控制电路 LM5050-1/-Q1 高侧 OR-ing FET 控制器与外部 MOSFET 配合工作&#xff0c;当与电源串联时则用作理想的二 极管整流器。此 ORing 控制器可使 MOSFET 替换电 源分配网络中的二极管整流器&#xff0c;从而降低功率损耗和压 降。 LM5050-1/-Q1 控制器…

企业计算机服务器中了rmallox勒索病毒怎么处理,rmallox勒索病毒处理建议

在网络技术不断发展的时代&#xff0c;网络在企业中的应用广泛&#xff0c;可以为企业带来更多的便利&#xff0c;大大提升了企业的生产效率&#xff0c;但网络作为虚拟世界&#xff0c;在为企业提供便利的同时&#xff0c;也为企业数据安全带来严重威胁。近期&#xff0c;云天…

USB3.0线束特征阻抗测试报告解读

一. 衰减 从低频到高频&#xff0c;每个数据点都按照相对应的规范进行设置&#xff0c;形成一条标准线&#xff0c;如图1中所示&#xff0c;紫色线即为标准线&#xff0c;蓝色线为实测线。实测线在紫色线之上&#xff0c;说明线束衰减符合标准&#xff0c;反之表明线束衰减不符…

预防耳石症后遗症,了解RD的成因。

耳石症的后遗症&#xff0c;我们把它叫做RD RD的这个症状实际上它跟多因素有关。 第一个因素&#xff0c;就是跟这个病人的性格有关系的。 第二个因素&#xff0c;就是跟这个耳石复位后他这个机体的这个状态有关系。 第三个因素&#xff0c;还跟耳石的有一部分可能是真的没有…

AI大模型探索之路-训练篇12:语言模型Transformer库-Datasets组件实践

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

如何使用提示测试为LLMs构建单元测试?

原文地址&#xff1a;how-to-build-unit-tests-for-llms-using-prompt-testing 确保您的人工智能交付&#xff1a;快速测试完美生成应用程序的基本指南 2024 年 4 月 26 日 如果你曾经编写过软件&#xff0c;你就会知道测试是开发过程中必不可少的一部分。特别是单元测试&#…

windows系统网页卡死的时候 解决办法

第一种办法 同时按下ctrlshiftdelete键&#xff0c;清除缓存 如果这种办法不行&#xff0c;强制退出再打开还不行的话&#xff0c;再试第二种办法 第二种办法 打开f12开发者工具&#xff0c;如图在application标签页下&#xff0c;打开local storage&#xff0c; 右键选中virt…