关于数据库:全方位讲解-Nebula-Graph-索引原理和使用

56次阅读

共计 4932 个字符,预计需要花费 13 分钟才能阅读完成。

本文首发于 Nebula Graph Community 公众号

index not found?找不到索引?为什么我要创立 Nebula Graph 索引?什么时候要用到 Nebula Graph 原生索引?针对社区常见问题,本文旨在一文带大家搞清索引应用问题。

Nebula Graph 的索引其实和传统的关系型数据库中的索引很像,然而又有一些容易让人纳闷的区别。刚开始理解 Nebula 的同学会纳闷:

  • 不分明 Nebula Graph 图数据库中的索引到的是什么概念;
  • 什么时候应该应用 Nebula Graph 索引;
  • Nebula Graph 索引会影响写入性能吗?影响水平如何?

在这篇文章里,咱们就把这些问题一一解决,不便大家更好地应用 Nebula Graph。

到底 Nebula Graph 索引是什么

简略来说,Nebula Graph 索引是 用来且只用来针对纯属性条件登程查问场景的性能,它具备以下个性:

  • 图游走(walk)查问中的属性条件过滤不须要它
  • 纯属性条件登程查问(注:非采样状况)必须创立索引

纯属性条件登程查问

咱们晓得在传统关系型数据库中,索引是对表数据的一个或多个针对特定 重排序的正本,它用来 减速特定列过滤条件的读查问 并带来了额定的数据写入。简略来说,索引能起到减速的作用,但查问应用索引并非是必要的。

在 Nebula Graph 图数据库里,索引则是对 点、边特定属性数据 重排序的正本,用来提供 纯属性条件登程查问

以如下边的查问为例,该语句实现了从指定点边属性条件,而非点的 ID 登程的形式去获取图数据:

#### 必须 Nebula Graph 索引存在的查问

# query 0 纯属性条件登程查问
LOOKUP ON tag1 WHERE col1 > 1 AND col2 == "foo" \
    YIELD tag1.col1 as col1, tag1.col3 as col3;

# query 1 纯属性条件登程查问
MATCH (v:player { name: 'Tim Duncan'})-->(v2:player) \
        RETURN v2.player.name AS Name;

上边这两个纯属性条件登程查问就是字面意思的”依据指定的属性条件获取点或者边自身“,背面的例子则是给定了点的 ID。参考以下例子:

#### 不基于索引的查问

# query 2, 从给定的点做的游走查问 vertex VID: "player100"

GO FROM "player100" OVER follow REVERSELY \
        YIELD src(edge) AS id | \
    GO FROM $-.id OVER serve \
        WHERE properties($^).age > 20 \
        YIELD properties($^).name AS FriendOf, properties($$).name AS Team;

# query 3, 从给定的点做的游走查问 vertex VID: "player101" 或者 "player102"

MATCH (v:player { name: 'Tim Duncan'})--(v2) \
        WHERE id(v2) IN ["player101", "player102"] \
        RETURN v2.player.name AS Name;

咱们认真看前边的 query 1query 3,只管语句中条件都有针对 tag 为 player 的过滤条件 {name: 'Tim Duncan'},但一个须要依赖索引实现,一个不须要索引。具体的起因在这里:

  • query 3之中不须要索引,因为:

    • 它会绕过 (v:player { name: 'Tim Duncan'}) 这种未给定 VID 的终点,从 v2 这样给定了 VID ["player101", "player102"] 的终点向外扩大,下一步再通过 GetNeighbors() 取得边的另一端的点,而后 GetVertices() 失去下一跳的 v,依据 v.player.name 过滤掉不要的数据;
  • query 1 则不同,它因为没有任何给定的终点 VID:

    • 只能从属性条件 {name: 'Tim Duncan'} 动手,在依照 name 排序的索引数据中先找到合乎的点:IndexScan() 失去 v
    • 而后再从 v 做 GetNeighbors() 取得边的另一端 的 v2,在通过 GetVertices() 去取得下一跳 v2 中的数据;

其实,这里的要害就是在于是查问是否存在给定的顶点 ID(Vertex ID),下边两个查问的执行打算里更清晰地比拟了他们的区别:


图注:query 1 的执行打算(须要索引);

图注:query 3 的执行打算(不须要索引);

为什么纯属性条件登程查问里必须要索引呢?

因为 Nebula Graph 在存储数据的时候,它的构造是面向分布式与关联关系设计的,相似表构造数据库中无索引的全扫描条件搜寻实际上更加低廉,所以设计上被无意禁止了。

但,如果你不谋求全副数据,只有采样一部分,3.0 里之后是反对不强制索引 LIMIT <n> 的状况的,如下查问(有 LIMIT)不须要索引:

MATCH (v:player { name: 'Tim Duncan'})-->(v2:player) \
    RETURN v2.player.name AS Name LIMIT 3;

为什么只有纯属性条件登程查问须要索引

在这里,咱们比拟一下失常的图查问 graph-queries 和纯属性条件登程查问 pure-prop-condition queries 实现形式:

  • graph-queries,如 query 2query 3 是沿着边一路找到特定门路条件的扩大游走;
  • pure-prop-condition queries,如 query 0query 1 是只通过特定属性条件(或者是无限度条件)找到满足的点、边;

而在 Nebula Graph 里,graph-queries 在扩大的时候,图的原始数据曾经依照 VID(点和边都是)排序过了,或者说在数据里曾经索引过了,这个排序带来间断存储(物理上邻接)使得扩大游走自身就是优化过能疾速返回后果。

总结:索引是什么,索引不是什么?

索引是什么?

  • Nebula Graph 索引是为了从给定属性条件查点、边的一份属性数据的排序,它用写入的代价使得这种读查问模式成为可能。

索引不是什么?

  • Nebula Graph 索引 不是用来减速个别图查问的:从一个点开始向外拓展的查问(即便是过滤属性条件的)不会依赖原生索引,因为 Nebula 数据本身的存储就是面向这种查问优化、排序的。

一些 Nebula Graph 索引的设计细节

为了更好了解索引的限度、代价、能力,咱们来解释更多他的细节

  • Nebula Graph 索引是在本地(不是离开、中心化)和点数据被一起存储、分片的。
  • 它只反对左匹配

    • 因为底层是 RocksDB Prefix Scan;
  • 性能代价:

    • 写入时候的门路:不只是多一分数据,为了保障一致性,还有低廉的读操作;
    • 读门路:基于规定的优化抉择索引,fan-out 到所有 StorageD;

这些信息可在我的集体网站的 #手绘图和视频#(链接:https://www.siwei.io/sketch-notes/)里能够看到,参考下图:

因为左匹配的设计,在简单查问场景,比方:针对纯属性条件登程查问里波及到通配、REGEXP,Nebula Graph 提供了全文索引的性能,它是利用 Raft Listener 去异步将数据写到内部 Elasticsearch 集群之中,并在查问的时候去查 ES 去做到的,具体全文索引应用参见文档:https://docs.nebula-graph.com.cn/3.0.0/4.deployment-and-installation/6.deploy-text-based-index/2.deploy-es/。

在这个手绘图中,咱们还能够看出

  • Write path

    • 写入索引数据是同步操作的;
  • Read path

    • 这部分画了一个 RBO 的例子,查问里的规定假如 col2 相等匹配排在右边的状况下,性能优于 col1 的大小比拟匹配,所以抉择了第二个索引;
    • 选好了索引之后,扫描索引的申请被 fan-out 到存储节点上,这其中有些过滤条件比方 TopN 是能够下推的;

论断:

  • 因为写入的代价,只有必须用索引的时候采纳,如果采样查问能满足读的要求,能够不创立索引而用 LIMIT <n>。
  • 索引有左匹配的限度

    • 合乎查问的程序要认真设计
    • 有时候须要应用全文索引 full-text index。

索引的应用

具体要参考 Nebula 官网的索引文档:https://docs.nebula-graph.io/3.0.0/3.ngql-guide/14.native-index-statements/ 一些要点是:

第一点,在 Tag 或者 EdgeType 上针对想要被条件反查点边的属性创立索引,应用 CREATE INDEX 语句;

第二点,创立索引之后的索引局部数据会同步写入,然而如果创立索引之前曾经有的点边数据对应的索引是须要明确指定去创立的,这是一个异步的 job,须要执行语句 REBUILD INDEX

第三点,触发了异步的 REBUILD INDEX 之后,可用语句 SHOW INDEX STATUS 查问状态:

第四点,利用到索引的查问能够是 LOOKUP,并且经常能够借助管道符在此之上做拓展查问,参考上面例子:

LOOKUP ON player \
    WHERE player.name == "Kobe Bryant"\
    YIELD id(vertex) AS VertexID, properties(vertex).name AS name |\
    GO FROM $-.VertexID OVER serve \
    YIELD $-.name, properties(edge).start_year, properties(edge).end_year, properties($$).name;

也能够是 MATCH,这里边 v 是通过索引失去的,而 v2 则是在数据(非索引)局部拓展查问取得的。

MATCH (v:player{name:"Tim Duncan"})--(v2:player) \
    RETURN v2.player.name AS Name;

第五点,复合索引的能力与限度。了解原生索引的匹配是左匹配能让咱们晓得对于超过一个属性的索引:复合索引,并且能帮忙咱们了解它的能力有限度,这里说几个论断:

  • 咱们创立针对多个属性的复合索引是程序无关的

    • 比方,咱们创立一个双属性复合索引 index_a: (isRisky: bool, age: int),和 index_b: (age: int, isRisky: bool) 在依据 WHERE n.user.isRisky == true AND n.user.age > 18 筛选条件进行查问时,index_a 因为左匹配一个相等的短字段,显然效率更高。
  • 只有复合左匹配的被复合索引的属性真子集的过滤条件能力被只反对

    • 比方,index_a: (isRisky: bool, age: int),和 index_b: (age: int, isRisky: bool) 在查问 WHERE n.user.age > 18 这个语句时,只有 index_b 复合最左匹配,能满足这个查问。
  • 针对一些从属性作为查问的终点,找点、边的状况,原生索引是不能满足全文搜寻的匹配场景的。这时候,咱们应该思考应用 Nebula 全文索引,它是 Nebula 社区反对的开箱即用的外置 Elasticsearch,通过配置,创立了全文索引的数据会通过 Raft listener 异步更新到 Elastic 集群中,全文索引的查问入口也是 LOOKUP,具体的信息请参考文档:https://docs.nebula-graph.com.cn/3.0.1/4.deployment-and-installation/6.deploy-text-based-index/2.deploy-es/。

回顾

  • Nebula Graph 索引在只提供属性条件状况下通过对属性的排序正本扫描查点、边;
  • Nebula Graph 索引 不是 用来图拓展查问的;
  • Nebula Graph 索引是左匹配,不是用来做含糊全文搜寻的;
  • Nebula Graph 索引在写入时候有性能代价;
  • 记得如果创立 Nebula Graph 索引之前曾经有相应点边上的数据,要重建索引;

Happy Graphing!


交换图数据库技术?退出 Nebula 交换群请先填写下你的 Nebula 名片,Nebula 小助手会拉你进群~~

关注公众号

正文完
 0