1. 图
当我们在查看路线图,研究组织结构图,或者使用 Facebook、LinkedIn、Twitter 等社交网络的时候,就是在使用图。图,是一种几乎无处不在、用来思考现实世界场景的方法,因为图能够抽象出这些场景要表现的项(item)和关系,从而能够快速、高效地处理数据中的连接。
举个例子,从家里出发去超市。
在地图上,城市通常用圆来表示,连接这些城市的路则用线来表示。在组织结构图中,一个圆通常代表一个人,并通常带有相关的头衔,将这些人连接在一起的线则表示雇佣关系。在社交网络中,人们通过加好友或关注的方式来相互连接
- 图: 顶点与边的集合
- 顶点:图中零条、一条或多条边经过的点,也称为节点或实体。
- 边:图中两个顶点之间的关系,有时也称为关系、链接或连接。
2. 图数据库
图数据库是一种数据存储引擎,将包含顶点和边的基本图结构与持久化技术和遍历(查询)语言相结合,以创建针对高度关联数据的存储和快速检索进行优化的数据库。
与其他数据库技术不同,图数据库建立在这样的概念上:实体之间的关系与数据中的实体同等重要,甚至比后者更加重要。由于实体和关系得到了同等对待,因此图数据库可以用于更准确、更容易地表示和推理现实世界中的关系,特别是与其他数据库技术相比。
与传统数据库相比,图数据库不使用 sql。
个人理解,仅参考: 如果说只是存储顶点和边,那么传统数据库也能存储。但是在某些场景下,传统数据库查询效率会比图数据库繁琐。
递归查询:
递归查询会连续执行多次,反复调用自己,直到满足某种终止条件。关系数据库不能很好地处理递归操作(尤其是无边界的递归操作),不论在语法还是性能方面都很吃力。这通常会导致需要编写和维护复杂的查询语句,或者/以及对数据进行过度的反规范化,而这只是为了及时返回结果而已。
假设有如下关系图:
在关系型数据库中
CREATE TABLE org_chart (
employee_id SMALLINT NOT NULL,
manager_employee_id SMALLINT NULL,
employee_name VARCHAR(20) NOT NULL
)
然后使用递归函数来查询这些数据,以找出用户的管理层次结构.
WITH RECURSIVE org AS (
SELECT employee_id,
manager_employee_id,
employee_name,
1 AS level
FROM org_chart
UNION
SELECT e.employee_id,
e.manager_employee_id,
e.employee_name,
m.level + 1 AS level
FROM org_chart AS e
INNER JOIN org AS m ON e.manager_employee_id = m.employee_id
)
SELECT employee_id, manager_employee_id, employee_name
FROM org
ORDER BY level ASC;
这类 sql 片段编写调试复杂,且性能不好.
改为由图数据库编写(使用 gremlin 语言):
g.V(). repeat( out('works_for')).path().next()
由此能见,使用图数据库能更简单解决'关联' 这种问题.
路径问题:
上图中的路径问题在图数据库中仅仅需要
g.V('TFGB_').repeat(out() ).until(hasId('_TGFB')).path().next()
即可完成下面问题
TFGB_ -运鹅过河-> FB_TG -空船返回-> TFB_G -运大麦过河-> F_TGB -运鹅返回->
TFG_B -运狐狸过河-> G_TBF -空船返回-> TG_FB -运鹅过河-> _TGFB
TFGB_ -运鹅过河-> FB_TG -空船返回-> TFB_G -运狐狸过河-> B_TFG -运鹅返回->
TGB_F -运大麦过河-> G_TBF -空船返回-> TG_FB -运狐狸过河-> _TGFB
像是路由、依赖关系管理、社交网络分析,数据分析 等这些问题,基本可通过关系型数据库进行解决.
3. gremlin 语法
顶点(Vertex)
边(Edge)
语法以 g 开头,区分大小写.
V():查询顶点,一般作为图查询的第 1 步
E():查询边,一般作为图查询的第 1 步
id(),
properties(''),
valueMap(),
value()
outE(),inE(),bothE(), 点的出边/入边/所有边
bothV(),inV(),outV() 所有边 / 边的入点/ 边的出点/
repeat()循环 loop() 循环次数
until() 循环的结束条件
emit() 收集 里面可加条件
simplepath()/path()
limit()/count()
has('xx')/hasNot('xx') 属性判断
.property('aa','bb') 新增属性/修改属性,
eq、neq 、lt、lte、gt、gte、inside、outside、between、within、without、between,max、min
startingWith、containing、filter
阿里 GDB 参考文档:
https://help.aliyun.com/product/102714.html
gremlin 语法参考文档:
https://zhuanlan.zhihu.com/p/558737366
https://zhuanlan.zhihu.com/p/469781456
4.性能优化
1.设置查询范围
图数据库 GDB 的查询分析引擎程序能够根据数据输入提供适合需求的查询方式。当查询数据量大时,您可以指定查找数据的范围(特定的 label,查询属性的起始范围、终止范围、迭代次数等)。
示例:
- 推荐方案(将过滤属性限制在 10~30 内)
g.V().hasLabel("person").has("age",P.gt(10).and(lt(30))).limit(5)
- 普通方案
g.V().has("age",P.gt(10))
2.查询最短路径
- 图数据库 GDB 采用 DFS(深度优先搜索)策略实现最短路径,当您的业务中图数据的连通规模非常大时,您可以在进行最短路径查询时添加限制条件。
示例:
- 推荐方案(限制了最短路径的最大深度)
g.V($startV).repeat(both().simplePath()).until(hasId($endV).or().loops().is(gt($depth))).hasId($endV).path()
- 普通方案
g.V($startV).repeat(both().simplePath()).until(hasId($endV))
- 当您的业务中需要将数据按照权重进行最短路径查询,您可以按照边的权重进行排序。
示例:
g.V(fromVertexId)
.repeat(outE().inV().simplePath())
.until(hasId(toVertexId).or().loops().is(gt(deepLimit)))
.hasId(toVertexId).path().as(‘p’)
.map(unfold().coalesce(values(‘weight’),constant(0.0)).sum())
.as(‘cost’).select(‘cost’,’p’).order().by(select(“cost”),Order.incr)
3.规避超级顶点
图数据库 GDB 具有自动索引机制和强大的统计分析引擎,为您提供适合的执行优化。即使如此,也建议您在使用图数据库 GDB 时通过规则规避业务中的超级顶点。
4.查询修改的合并语句
如果您的业务需要在使用 Gremlin 查询的同时修改 property,您可以使用查询修改的合并语句。
示例:
g.V("test").property("nums", union(values("nums"), constant(1)).sum())
5.查询超时分析
如果您的业务场景比较复杂,容易出现超时的情况,您可以使用 profile()语句,对查询中每一步的执行性能进行分析。通过性能分析,您可以对查询执行中涉及到的数据分布情况有明确了解(例如超级顶点、随机查询大量数据、属性过滤等比较耗时的数据)。
查询请求示例:
g.V().out().limit(10).profile()
返回数据示例:
==>Traversal Metrics
Step Count Traverses Time (ms) % Dur
=============================================================================================================
GraphDbGraphStep(vertex,[]) 29 29 1.657 77.43
VertexStep(OUT,vertex) 11 11 0.410 19.18
RangeGlobalStep(0,10) 10 10 0.072 3.39
>TOTAL - - 2.140 -
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于