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

文章目录

    • 背景描述
    • 分析
    • 解决
    • 代码参考
    • 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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

目标检测发展概述

前言 本篇文章只是简单介绍一下目标检测这一计算机视觉方向的发展历史,因此重点在于介绍而不是完整阐述各个时期的代表算法,只要能够简单了解到目标检测的发展历史那么本文的目的就达到了。 目标检测的任务 从上图不难看出,目标检测是计算机…

第十五届蓝桥杯

经历半年以来的学习,终于出结果了。期间无数次想要放弃,但是我都挺过来了,因为我还不能倒下。期间有很多次焦虑,一直在想,我要是没拿奖我是不是白学了。但是学到最后就释怀了,因为在备赛期间,我…

unity制作app(1)--登录 注册 界面

把学到的知识投入到生产中反而是一件简单的事情! 1.调整canvas的形状,这里和camera没有任何关系! overlay! 2.既然自适应,空间按钮的位置比例就很重要了! game窗口中新增720*1280的分辨率! 3.再回到can…

电商直播带货运营计划主播话术脚本规划表格

【干货资料持续更新,以防走丢】 电商直播带货运营计划主播话术脚本规划表格 部分资料预览 资料部分是网络整理,仅供学习参考。 直播运营模板合集(完整资料包含以下内容) 目录 直播运营计划 1. 直播前准备阶段 - 抖店商品上架&a…

重定义大语言模型的记忆能力:对抗性压缩如何挑战现有测量法

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享,与你一起了解前沿深度学习信息! Rethinking LLM Memorization through the Lens of Adversarial Compression 引言:探索大型语言模型的记忆能力 在当今信息时代,大型…

【c++】模板编程解密:C++中的特化、实例化和分离编译

🔥个人主页:Quitecoder 🔥专栏:c笔记仓 朋友们大家好,本篇文章我们来学习模版的进阶部分 目录 1.非类型模版参数按需实例化 2.模版的特化函数模版特化函数模版的特化类模版全特化偏特化 3.分离编译模版分离编译 1.非类…

Ubuntu GUI使用Root用户登录指南

Ubuntu GUI使用Root用户登录指南 一、前言 默认情况下,Ubuntu 禁用了 root 账户,我们必须使用 sudo 命令来执行任何需要 root 权限的任务,比如像这样删除一个系统配置文件(操作危险,请勿尝试):…

一探究竟轻松畅玩:我独自升级崛起怎么玩 怎么快速上手的教程

一探究竟轻松畅玩:我独自升级崛起怎么玩 怎么快速上手的教程 最近一款漫改的MMORPG游戏《我独自升级:崛起》给玩家们带来了不少惊喜。在刚进入游戏时,玩家们需要从E级猎人开始玩起,逐步成长为S级猎人,通过升级学习新技…

ES与关系数据库的同步练习(hotel_admin)

目录 1 es与数据库同步的方法2 实践2.1 任务介绍2.2 MQ方面操作2.2.1 声明交换机队列并且绑定2.2.2 hotel_admin端web层设置mq发送消息2.3 hotel_demo端监听接受消息并执行es操作 1 es与数据库同步的方法 方式一:同步调用 优点:实现简单,粗…

jupyter notebook导出pdf文件显示不了中文

找到文件index.tex.j2,我的在 C:\Users\Administrator\miniconda3\envs\opencv2\share\jupyter\nbconvert\templates\latex 我安装miniconda3并配置opencv2所需要的环境, 配置前 最后:用文本编辑器打开,修改图中article为ctexart&#xf…

spring boot 自定义starter示例

springboot 约定规范 Starter项目的命名规范 建议自定义的starter 以 xxx-spring-boot-starter 命名,官方的Starter一般都是以spring-boot-starter-为前缀。这样做的目的是为了避免与官方或其他第三方提供的Starter产生冲突或混淆。 Starter项目的结构规范(重要) …

ubuntu22.04 cmake 配置mysql

报错信息: CMake Error at CMakeLists.txt:33 (find_package): By not providing “FindMySQL.cmake” in CMAKE_MODULE_PATH this project has asked CMake to find a package configuration file provided by “MySQL”, but CMake did not find one. Could not…

python - 3D图表绘制

Pyecharts 和 3D 图表绘制 Pyecharts 是一个用于生成各种图表的 Python 库,它基于 Echarts,支持大量的图表类型,非常适合用于数据分析和可视化。Pyecharts 主要优点是易于使用,可以直接在 Python 环境中绘制富有交互性的图表&…

【电路笔记】-石英晶体振荡器

石英晶体振荡器 文章目录 石英晶体振荡器1、概述2、石英晶体等效模型3、石英晶体振荡器示例14、Colpitts 石英晶体振荡器5、Pierce振荡器6、CMOS晶体振荡器7、微处理器水晶石英钟8、石英晶体振荡器示例21、概述 任何振荡器最重要的特性之一是其频率稳定性,或者换句话说,其在…

脸爱云一脸通智慧管理平台 SystemMng 管理用户信息泄露漏洞(XVE-2024-9382)

0x01 产品简介 脸爱云一脸通智慧管理平台是一套功能强大,运行稳定,操作简单方便,用户界面美观,轻松统计数据的一脸通系统。无需安装,只需在后台配置即可在浏览器登录。 功能包括:系统管理中心、人员信息管理中心、设备管理中心、消费管理子系统、订餐管理子系统、水控管…

关于海康相机和镜头参数的记录

对比MV-CS020-10UC和大家用的最多的MV-CS016-10UC 其实前者适合雷达站使用,后者适合自瞄使用 一:MV-CS020-10UC的参数 二:对比 三:海康镜头选型工具

SpringCloudAlibaba:3.1dubbo

dubbo 概述 简介 Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题 官方提供了 Java、Golang、Rust 等多语言 SDK 实现 Dubbo的开源故事 最早在2008年,阿里巴巴就将Dubbo捐献到开源社区,它很快成为了国内开源…

张鸣独到解读:规矩与自信的政治影响

在当今多变的政治舞台上,学者张鸣教授以其犀利而深邃的视角,对规矩与自信提出了新的解读。他的言论不仅引发了公众的广泛关注,也为我们提供了思考社会政治问题的一个新的角度。张教授指出,规矩并非僵化的教条,而应是动…

02_机器学习算法_基于XGBoost的分类预测

1. XGBoost 算法 1.1 XGBoost 的介绍 XGBoost是2016年由华盛顿大学陈天奇老师带领开发的一个可扩展机器学习系统。严格意义上讲XGBoost并不是一种模型,而是一个可供用户轻松解决分类、回归或排序问题的软件包。它内部实现了梯度提升树(GBDT)模型,并对模型中的算法进行了诸多…

linux 光驱(光盘)安装

文章目录 自带 YUM 库创建 repo创建文件夹挂载光驱开机自启动挂载安装软件YUM 安装RPM 安装 自带 YUM 库 ls /etc/yum.repos.d创建 repo vim /etc/yum.repo.d/demo.repo // 编写 repo 相关配置 [demo] namedemo baseurlfile:///mnt/cdrom gpkcheck0创建文件夹挂载光驱 /dev/…