图谱结构:
其中query标签的结构:
a_[a|b|c|d|e|f]标签的结构
b_[a|b|c|d|e|f]标签的结构
数据规模:
类别总数:100,每个类别名称数:100,每个类别名称序号总数:10
数据生成脚本:
import pymysql from itertools import product mydb = pymysql.connect( host="localhost", user="", passwd="", database="test_graph" ) classes, names = 100, 100 mycursor = mydb.cursor(pymysql.cursors.DictCursor) mydb.begin() mycursor.execute('delete from query;') mycursor.execute('delete from entity;') mycursor.execute('delete from relation;') mycursor.execute('alter table query auto_increment=1') mycursor.execute('alter table entity auto_increment=1') mycursor.execute('alter table relation auto_increment=1') for i in range(classes): mycursor.execute(f'insert into query(class) values({i})') for i, k, name in product(range(classes), 'abcdef', range(names)): mycursor.execute(f"insert into entity(class, `table`, `name`) values({i},'a_{k}', 'name_{name}')") for i, k, name, index in product(range(classes), 'abcdef', range(names), range(10)): mycursor.execute(f"insert into entity(class, `table`, `name`, `index`) values({i},'b_{k}', 'name_{name}', {index})") print('-----------------------------------') for k in 'abcdef': mycursor.execute(f"insert into relation(h, h_table, r, t, t_table) select query.id, 'query', 'query', entity.id, 'a_{k}'" f" from query, entity where query.class = entity.class and entity.table = 'a_{k}'") for k in 'abcdef': mycursor.execute(f"insert into relation(h, h_table, r, t, t_table) select ea.id, 'a_{k}', 'ab', eb.id, 'b_{k}' " f" from entity ea, entity eb where ea.name = eb.name and ea.class = eb.class and ea.table = 'a_{k}' and eb.table = 'b_{k}'") mydb.commit() mycursor.close()
优化方法:
1、简化返回接口
一次不要返回太多内容和太多字段
2、接口分离
查询较慢的部分(例如聚合运算统计数量)可以单独分离成一个接口以提高整体的体验
3、关键字段引入索引
g.V().hasLabel("label").values("name").fold(). order(Scope.local). index(). unfold(). order(). by(__.tail(Scope.local, 1))
4、并行处理
import asyncio import time def r1(a, b): time.sleep(a) return 1 + b def r2(a): time.sleep(a) return 2 + a async def run(loop): task1 = loop.run_in_executor(None, r1, 1, 4) task2 = loop.run_in_executor(None, r2, 2) return await task1 + await task2 if __name__ == "__main__": loop = asyncio.new_event_loop() print(loop.run_until_complete(run(loop)))
5、使用查询节点,尽量使用边的关系,避免使用标签前缀匹配
例如下面语句的执行时间为0.25s
g.V().filter{it.get().label().startsWith('a')}.has('class','59') .has('name', 'name_5').valueMap('id', 'class', 'name').limit(10)
优化后的如下语句执行时间为0.04s
g.V().hasLabel('query').has('class','59').outE().inV() .has('name', 'name_5').valueMap('id', 'class', 'name').limit(10)
6、使用边的关系和结果聚集
对于查询class=59,找出所有name的标签的index
方法一:
第一步:找出class对应的所有name,时间0.03s
g.V().hasLabel('query').has('class', '59').outE().inV() .outE.valueMap('class', 'name').dedup('class', 'name')
第二步:找出每个name的index,每条语句执行时间为0.62s
g.V().filter{it.get().label().startsWith('b')}.has('class','59') .has('name', 'name_5').valueMap('index').dedup('index')
或者执行下面语句,每条语句执行时间为0.04s
g.V().hasLabel('query').has('class', '59') .outE().inV().has('name', 'name_5').outE().inV() .valueMap('index').dedup('index')
使用并发访问最短可到0.04秒,但是又100个名称,需要执行101次请求
方法二:使用聚集的方法,如下,时间0.08s
g.V().hasLabel('query').has('class', '59').outE() .inV().as('class', 'name', 'indexes') .project('class', 'name', 'indexes').by('class').by('name') .by(__.outE().inV().valueMap('index').fold())
方法三:使用group语句,时间为0.27s,当组数过多时性能有所降低
g.V().hasLabel('query').has('class', '59').outE().inV().outE().inV() .group().by('name').by(valueMap('index').fold()).unfold()
参考资料:
Python:协程中 Task 和 Future 的理解及使用 - 简书
Gremlin中文文档
Gremlin -- 常用查询用法 - 云+社区 - 腾讯云
深入学习Gremlin(16):结果聚集与展开 - 程序员大本营
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)