持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第27天,点击查看活动详情
前言
昨天文章中,考虑到图谱首页的查询速度等因素,直接从Neo4J查询并不是一个明智的选择,因为查询语句受限并不能展示想要的数据结构,并且节点分布不均,因此将图数据库中的部分数据通过查询语句写入到redis中,查询之后通过处理缓存数据,进行首页的初始化,这样能够满足首页的加载速度,但是发现一个问题,在数据处理阶段,也就是将数据处理为前端所需的形式时,由于多层嵌套,导致做了几个N方基本负责度的粑粑代码,因此感觉速度还是受影响,今天将对Redis的存储结构进行优化,并尝试模糊查询方法。
之前遗留的问题:
之前的数据处理为如下格式
[{"company_name":[{"product_name":["cvenumber1","cvenumber1","cvenumber1"]}]}}]
由于无论是公司还是产品还是漏洞编号都是通过list嵌套的方式存储,这样的好处是可以方便的使用limit参数返回想要的数据数量,如 product_name[list][:limit]就能获得如50条数据中的30条,但是在数据处理过程中,多层嵌套拿到内层参数需要大量的for循环,这样对于程序的运行时十分耗时的,随着数据量的增加,之前的数据处理代码如下:
def formatAllNodes(self, dataLine, level, limit):
nodes = []
edges = []
if level == 2:
for line in eval(dataLine)[:limit]:
for k, v in line.items():
if k != "product_number":
company_name = k
childrenNum = line["product_number"]
node = {
"id": company_name,
"name": company_name,
"level": 0,
"childrenNum": childrenNum,
"tag": company_name
}
nodes.append(node)
product_list = line[company_name][0]
for item in (product_list[:limit]):
for product_name, cve_list in (item.items()):
if product_name != "cve_number":
node = {
"id": product_name,
"name": product_name,
"level": 2,
"isLeaf": True,
"tags": [
company_name
]
}
nodes.append(node)
edge = {
"source": company_name,
"target": product_name
}
edges.append(edge)
resDic = {"nodes":nodes,"edges":edges}
return resDic
elif level == 3:
for line in eval(dataLine)[:limit]:
for k, v in line.items():
if k != "product_number":
company_name = k
childrenNum = line["product_number"]
node = {
"id": company_name,
"name": company_name,
"level": 0,
"childrenNum": childrenNum,
"tag": company_name
}
nodes.append(node)
product_list = line[company_name][0]
for item in (product_list[:limit]):
for product_name, cve_list in (item.items()):
if product_name != "cve_number":
node = {
"id": product_name,
"name": product_name,
"level": 1,
"childrenNum": item["cve_number"],
"tag": product_name,
"isLeaf": False,
"tags": [
company_name
]
}
nodes.append(node)
edge = {
"source": company_name,
"target": product_name
}
edges.append(edge)
for cvenumber in cve_list[:limit]:
node = {
"id": cvenumber,
"name": cvenumber,
"level": 2,
"isLeaf": True,
"tags": [
company_name
]
}
nodes.append(node)
edge = {
"source": company_name,
"target": cvenumber
}
edges.append(edge)
resDic = {"nodes":nodes,"edges":edges}
return resDic
else:
return {"nodes":[],"edges":[]}
4-5层的for循环明显不利于查询过程中的响应速度,因此首先从redis的存储结构入手。
def makeData():
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):
conpany_name = (str(item.data().get("n")).split("name: '")[1].rstrip("'})"))
print(index,conpany_name)
product_number = int(item.data().get("count(r)"))
# 漏洞列表存储公司名称以及公司所包含的产品数量
vulnerability.append({"conpany_name": conpany_name, "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:
cvenode_number = int(item.data().get("count(t)"))
product_name = (str(item.data().get("m")).split("name: '")[1].rstrip("'})"))
product_list_all.append({"product_name":product_name, "cvenode_number":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)
redisUtils().setKey(product_name, str(cve_list_all))
redisUtils().setKey(conpany_name, str(product_list_all))
redisUtils().setKey("vulnerability", str(vulnerability))
重新更新redis的存储结构,其中vulnerability保存了top50公司以及对应的产品数量,对于不同的公司,使用公司名称为键值,保存对应的产品和产品所包含的漏洞数量,第三部分则是产品名称作为键,而漏洞列表作为值,这样查询过程中,将for循环遍历的时间改为redis的查询时间,用于优化查询以及页面加载速度。
查询使用
redisUtils().getKey("vulnerability")
进行查询。
结果示例:
[{'conpany_name': 'intel', 'product_number': 6789}, {'conpany_name': 'hp', 'product_number': 5860}, {'conpany_name': 'cisco', 'product_number': 4020}, {'conpany_name': 'lenovo', 'product_number': 2800}, {'conpany_name': 'siemens', 'product_number': 2176}, {'conpany_name': 'dell', 'product_number': 2106}, {'conpany_name': 'qualcomm', 'product_number': 1881}, {'conpany_name': 'debian_9', 'product_number': 1356}, {'conpany_name': 'schneider-electric', 'product_number': 1291}, {'conpany_name': 'debian_10', 'product_number': 1227}, {'conpany_name': 'huawei', 'product_number': 1200}, {'conpany_name': 'debian_11', 'product_number': 979}, {'conpany_name': 'mitsubishielectric', 'product_number': 928}, {'conpany_name': 'netgear', 'product_number': 907}, {'conpany_name': 'debian_sid', 'product_number': 905}, {'conpany_name': 'supermicro', 'product_number': 888}, {'conpany_name': 'axis', 'product_number': 798}, {'conpany_name': 'lexmark', 'product_number': 766}, {'conpany_name': 'opensuse_Leap_15.1', 'product_number': 715}, {'conpany_name': 'amd', 'product_number': 680}, {'conpany_name': 'debian_12', 'product_number': 676}, {'conpany_name': 'ubuntu_18.04.5_lts', 'product_number': 667}, {'conpany_name': 'fedora_29', 'product_number': 636}, {'conpany_name': 'ibm', 'product_number': 629}, {'conpany_name': 'opensuse_Leap_15.2', 'product_number': 622}, {'conpany_name': 'fedora_27', 'product_number': 618}, {'conpany_name': 'samsung', 'product_number': 608}, {'conpany_name': 'oracle_8', 'product_number': 587}, {'conpany_name': 'phoenixcontact', 'product_number': 564}, {'conpany_name': 'fedora_28', 'product_number': 563}, {'conpany_name': 'fedora_31', 'product_number': 561}, {'conpany_name': 'ubuntu_18.04', 'product_number': 547}, {'conpany_name': 'opensuse_Leap_15.0', 'product_number': 542}, {'conpany_name': 'debian_8', 'product_number': 541}, {'conpany_name': 'fedora_EPEL_7', 'product_number': 540}, {'conpany_name': 'ubuntu_16.04.7_lts', 'product_number': 539}, {'conpany_name': 'hikvision', 'product_number': 532}, {'conpany_name': 'alpine_edge', 'product_number': 531}, {'conpany_name': 'opensuse_Leap_42.3', 'product_number': 531}, {'conpany_name': 'jenkins', 'product_number': 527}, {'conpany_name': 'fedora_32', 'product_number': 527}, {'conpany_name': 'asus', 'product_number': 519}, {'conpany_name': 'fedora_30', 'product_number': 507}, {'conpany_name': 'fedora_33', 'product_number': 492}, {'conpany_name': 'ubuntu_16.04', 'product_number': 482}, {'conpany_name': 'alpine_3.16', 'product_number': 467}, {'conpany_name': 'alpine_3.13', 'product_number': 459}, {'conpany_name': 'tp-link', 'product_number': 457}, {'conpany_name': 'ubuntu_18.10', 'product_number': 455}, {'conpany_name': 'alpine_3.15', 'product_number': 447}]
根据公司名称查询:
print(redisUtils().getKey("intel"))
结果如下:
[{'product_name': 'core_i7-8665u', 'cvenode_number': 59}, {'product_name': 'graphics_driver', 'cvenode_number': 58}, {'product_name': 'core_i7-8565u', 'cvenode_number': 58}, {'product_name': 'core_i7-8706g', 'cvenode_number': 58}, {'product_name': 'core_i7-8650u', 'cvenode_number': 58}, {'product_name': 'core_i7-8700', 'cvenode_number': 57}, {'product_name': 'core_i7-8700t', 'cvenode_number': 57}, {'product_name': 'core_i7-8700k', 'cvenode_number': 57}, {'product_name': 'core_i7-8500y', 'cvenode_number': 57}, {'product_name': 'core_i7-8750h', 'cvenode_number': 56}, {'product_name': 'core_i7-8709g', 'cvenode_number': 56}, {'product_name': 'core_i7-8550u', 'cvenode_number': 56}, {'product_name': 'core_i7-8809g', 'cvenode_number': 56}, {'product_name': 'core_i7-8850h', 'cvenode_number': 56}, {'product_name': 'core_i7-8705g', 'cvenode_number': 56}, {'product_name': 'core_i7-8700b', 'cvenode_number': 55}, {'product_name': 'xeon_gold_5220s', 'cvenode_number': 54}, {'product_name': 'xeon_gold_6230', 'cvenode_number': 54}, {'product_name': 'xeon_gold_6222v', 'cvenode_number': 54}, {'product_name': 'core_i7-8559u', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5222', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5218b', 'cvenode_number': 54}, {'product_name': 'xeon_bronze_3204', 'cvenode_number': 54}, {'product_name': 'xeon_gold_6226', 'cvenode_number': 54}, {'product_name': 'xeon_gold_6238l', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5215', 'cvenode_number': 54}, {'product_name': 'xeon_silver_4208', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5218n', 'cvenode_number': 54}, {'product_name': 'xeon_silver_4214', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5218', 'cvenode_number': 54}, {'product_name': 'xeon_silver_4215', 'cvenode_number': 54}, {'product_name': 'xeon_silver_4214y', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5217', 'cvenode_number': 54}, {'product_name': 'xeon_silver_4216', 'cvenode_number': 54}, {'product_name': 'xeon_gold_6238', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5220', 'cvenode_number': 54}, {'product_name': 'xeon_silver_4210', 'cvenode_number': 54}, {'product_name': 'xeon_silver_4209t', 'cvenode_number': 54}, {'product_name': 'xeon_gold_5215l', 'cvenode_number': 54}, {'product_name': 'core_i7-7600u', 'cvenode_number': 53}, {'product_name': 'core_i7-7700t', 'cvenode_number': 53}, {'product_name': 'core_i7-7820hq', 'cvenode_number': 53}, {'product_name': 'core_i7-7920hq', 'cvenode_number': 53}, {'product_name': 'xeon_gold_5218t', 'cvenode_number': 53}, {'product_name': 'core_i7-7660u', 'cvenode_number': 53}, {'product_name': 'xeon_gold_5220t', 'cvenode_number': 53}, {'product_name': 'core_i7-10710u', 'cvenode_number': 52}, {'product_name': 'core_i7-10510y', 'cvenode_number': 52}, {'product_name': 'core_i7-7500u', 'cvenode_number': 52}, {'product_name': 'core_i7-7567u', 'cvenode_number': 52}]
根据产品查询,结果如下:
['CVE-2022-29901', 'CVE-2022-21180', 'CVE-2022-0005', 'CVE-2022-0002', 'CVE-2022-0001', 'CVE-2021-33150', 'CVE-2021-33124', 'CVE-2021-33107', 'CVE-2021-0158', 'CVE-2021-0157', 'CVE-2021-0156', 'CVE-2021-0127', 'CVE-2021-0125', 'CVE-2021-0124', 'CVE-2021-0119', 'CVE-2021-0118', 'CVE-2021-0117', 'CVE-2021-0116', 'CVE-2021-0115', 'CVE-2021-0111', 'CVE-2021-0107', 'CVE-2021-0103', 'CVE-2021-0099', 'CVE-2021-0095', 'CVE-2021-0093', 'CVE-2021-0092', 'CVE-2021-0091', 'CVE-2020-8703', 'CVE-2020-8700', 'CVE-2020-8695', 'CVE-2020-8694', 'CVE-2020-8670', 'CVE-2020-24507', 'CVE-2020-24506', 'CVE-2020-24486', 'CVE-2020-24457', 'CVE-2020-12360', 'CVE-2020-12359', 'CVE-2020-12358', 'CVE-2020-12357', 'CVE-2020-0593', 'CVE-2020-0551', 'CVE-2020-0549', 'CVE-2020-0548', 'CVE-2020-0543', 'CVE-2020-0529', 'CVE-2020-0528', 'CVE-2019-14615', 'CVE-2019-14607', 'CVE-2019-11157']
数据处理程序优化:
def formatAllNodes(self, dataLine, level, limit):
nodes = []
edges = []
if level == 2:
for line in eval(dataLine)[:limit]:
company_name = line.get("conpany_name")
product_number = line.get("product_number")
node = {
"id": company_name,
"name": company_name,
"level": 0,
"childrenNum": product_number,
"tag": company_name
}
nodes.append(node)
for line in eval(self.r.getKey(company_name))[:limit]:
product_name = line.get("product_name")
node = {
"id": product_name,
"name": product_name,
"level": 1,
"isLeaf": True,
"tags": [
company_name
]
}
nodes.append(node)
edge = {
"source": company_name,
"target": product_name
}
edges.append(edge)
nodes.append(node)
resDic = {"nodes":nodes,"edges":edges}
return resDic
elif level == 3:
for line in eval(dataLine)[:limit]:
company_name = line.get("conpany_name")
product_number = line.get("product_number")
node = {
"id": company_name,
"name": company_name,
"level": 0,
"childrenNum": product_number,
"tag": company_name
}
nodes.append(node)
for line in eval(self.r.getKey(company_name))[:limit]:
product_name = line.get("product_name")
node = {
"id": product_name,
"name": product_name,
"level": 1,
"childrenNum": line.get("cvenode_number"),
"tag": product_name,
"isLeaf": False,
"tags": [
company_name
]
}
nodes.append(node)
edge = {
"source": company_name,
"target": product_number
}
edges.append(edge)
nodes.append(node)
for line in eval(self.r.getKey(product_name))[:limit]:
cvenumber = line
node = {
"id": cvenumber,
"name": cvenumber,
"level": 2,
"isLeaf": True,
"tags": [
product_name
]
}
nodes.append(node)
edge = {
"source": product_name,
"target": cvenumber
}
edges.append(edge)
resDic = {"nodes":nodes,"edges":edges}
return resDic
查询速度比之前有所优化
查询1000节点时间能控制在100-300ms,暂时可以接受