【大数据】 Neo4J+redis优化查询速度

156 阅读3分钟

image.png

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情

前言

昨天的文章中,程序通过python flask + uwsgi + nginx进行了打包,并且通过postman调用接口能够正常返回数据,但是发现有几个问题,首先,match (n) return n limit 25 或者 limit n 返回的数据节点数量无法把控,比如根节点返回三个,子节点返回n-3个,但是实际我们需要多个根节点,这种情况并不适配,此外如果叶子节点数目过于庞大的话,会导致根节点数目进一步变少,并且关系不全,随着数目越多问题约凸显。此外,数据量的增加会导致查询效率低下,图数据库返回大量节点耗时较长,因此对于首页这种经常刷新的网页,并不适合从数据库中查询,基于以上问题,寻求解决办法。


引入Redis

之前的文章中提到过redis,作为缓存数据库,其将数据加载于内存,查询效率较高,并且IO时间较小,因此十分适合首页等数据经常刷新的地方,对于数据更新,可以在flask程序中起定时任务,按照时间间隔进行数据刷新操作,基于以上想法,在程序中引入redis部分。

redis.ini

[REDIS]
REMOTE_HOST=127.0.0.1
REMOTE_PORT=26379
PASSWORD=m0jXXXXXXXwsx3xxm3vnrWp%234wp

redis配置文件,其中第一行为IP地址,第二行为端口,第三行为密码

redisUtils.py

from configparser import RawConfigParser


import redis


class redisUtils():
    def __init__(self):
        self.getMysqlCfg()
        self.getRedisConnect()

    def getMysqlCfg(self):
        config = RawConfigParser()
        # 获取配置文件的真实路径
        path = r"config/redis.ini"
        config.read(path, encoding="utf-8")
        self.REMOTE_HOST = config.get("REDIS","REMOTE_HOST")
        self.REMOTE_PORT = int(config.get("REDIS","REMOTE_PORT"))
        self.PASSWORD = config.get("REDIS","PASSWORD")

    def getRedisConnect(self):
        self.redisConn = redis.Redis(host=self.REMOTE_HOST, port=self.REMOTE_PORT, db=0, password=self.PASSWORD)

    def getKey(self, key):
        if self.redisConn.exists(key):
            return self.redisConn.get(key).decode("utf-8")
        else:
            return None

    def setKey(self, key, value):
        self.redisConn.set(key, value)

    def delKey(self, key):
        self.redisConn.delete(key)

    def setKeyWithTime(self, key, value, time):
        self.redisConn.set(key, value, ex=time)

首先获取配置文件,根据配置文件完成redis的连接操作,通过getRedisConnect函数,其余为类的一些方法,与getkey()获取关键字对应的值,setkey()往数据库进行插入操作,delkey() redis的删除操作,最后一个是配有设置过期时间的关键字插入方法。

node2redis.py


from py2neo.data import Node

# {"company_name":{"product_name":["cvenumber1","cvenumber1","cvenumber1"]}}}

# vulnerability = [{company_name:[{product_name:[cve_number]}]}]
from Utils.connectUtils import connect2Neo4J
from Utils.redisUtils import redisUtils


def getData():
    conn = connect2Neo4J().conn
    vulnerability = []
    conpany_list = conn.run("MATCH (n:company)-[r]->() RETURN n,count(r) order by count(r) DESC limit 50")
    for index, item in enumerate(conpany_list):
        print(index,item)
        conpany_dic = {}
        print(item.data())
        conpany_name = (str(item.data().get("n")).split("name: '")[1].rstrip("'})"))
        product_number = int(item.data().get("count(r)"))
        print(product_number)
        conpany_dic[conpany_name] = []
        conpany_dic["product_number"] = product_number
        product_list = conn.run(f"MATCH (n:company{{name:'{conpany_name}'}})-[r]->(m:product) -[t]-> () RETURN m,count(t) order by count(t) DESC limit 50")
        product_list_all = []
        for item in product_list:
            product_dic = {}
            product_name = (str(item.data().get("m")).split("name: '")[1].rstrip("'})"))
            cvenode_number = int(item.data().get("count(t)"))
            print(cvenode_number)
            cve_list = conn.run(f"MATCH (n:company{{name:'{conpany_name}'}})-[r]->(m:product{{name:'{product_name}'}}) -[t]-> (o:cvenumberNode)   RETURN o order by o.name DESC limit 50")
            cve_list_all = []
            for item in cve_list:
                cve_number = (str(item.data().get("o")).split("name: '")[1].rstrip("'})"))
                cve_list_all.append(cve_number)
            product_dic[product_name] = cve_list_all
            product_dic["cve_number"] = cvenode_number
            product_list_all.append(product_dic)
        conpany_dic[conpany_name].append(product_list_all)
        vulnerability.append(conpany_dic)
    print(vulnerability)
    redisUtils().setKey("vulnerability",str(vulnerability))

if __name__ == '__main__':
    getData()

基本逻辑是将图数据库保存为如下格式:

vulnerability = [{company_name:[{product_name:[cve_number]}]}]

每一个labels对应的值都以list进行存储,这样就能够对数据进行split操作,根据limit值返回对应的数据量,这样能够保证数据的雨露均沾,此外,数据也经过了排序,对于公司,则根据公司产品数目进行排序,对于产品则根据漏洞数量进行排序,对于漏洞,则根据漏洞名称【时间先后进行排序】,对于格式化后的数据,存储每个厂商对应产品对应漏洞的键值对,键为“vulnerability”,这样通过查询接口对数据进行查询,速度由于通过neo4J进行查询,并且数据分布较为均匀。

明天将对redis进行优化,并且准备模糊查询的方法,(#^.^#)~