本文首发于 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 出发的方式去获取图数据:

  1. #### 必须 Nebula Graph 索引存在的查询
  2. # query 0 纯属性条件出发查询
  3. LOOKUP ON tag1 WHERE col1 > 1 AND col2 == "foo" \
  4. YIELD tag1.col1 as col1, tag1.col3 as col3;
  5. # query 1 纯属性条件出发查询
  6. MATCH (v:player { name: 'Tim Duncan' })-->(v2:player) \
  7. RETURN v2.player.name AS Name;

上边这两个纯属性条件出发查询就是字面意思的”根据指定的属性条件获取点或者边本身“ ,反面的例子则是给定了点的 ID。参考以下例子:

  1. #### 不基于索引的查询
  2. # query 2, 从给定的点做的游走查询 vertex VID: "player100"
  3. GO FROM "player100" OVER follow REVERSELY \
  4. YIELD src(edge) AS id | \
  5. GO FROM $-.id OVER serve \
  6. WHERE properties($^).age > 20 \
  7. YIELD properties($^).name AS FriendOf, properties($$).name AS Team;
  8. # query 3, 从给定的点做的游走查询 vertex VID: "player101" 或者 "player102"
  9. MATCH (v:player { name: 'Tim Duncan' })--(v2) \
  10. WHERE id(v2) IN ["player101", "player102"] \
  11. 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)不需要索引:

  1. MATCH (v:player { name: 'Tim Duncan' })-->(v2:player) \
  2. 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 。
  • 索引有左匹配的限制
    • 符合查询的顺序要仔细设计
    • 有时候需要使用全文索引 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,并且常常可以借助管道符在此之上做拓展查询,参考下面例子:

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

也可以是 MATCH,这里边 v 是通过索引得到的,而 v2 则是在数据(非索引)部分拓展查询获得的。

  1. MATCH (v:player{name:"Tim Duncan"})--(v2:player) \
  2. 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 小助手会拉你进群~~

关注公众号

全方位讲解 Nebula Graph 索引原理和使用的更多相关文章

  1. 分布式图数据库 Nebula Graph 的 Index 实践

    导读 索引是数据库系统中不可或缺的一个功能,数据库索引好比是书的目录,能加快数据库的查询速度,其实质是数据库管理系统中一个排序的数据结构.不同的数据库系统有不同的排序结构,目前常见的索引实现类型如 B ...

  2. Nebula Graph 在微众银行数据治理业务的实践

    本文为微众银行大数据平台:周可在 nMeetup 深圳场的演讲这里文字稿,演讲视频参见:B站 自我介绍下,我是微众银行大数据平台的工程师:周可,今天给大家分享一下 Nebula Graph 在微众银行 ...

  3. 解析 Nebula Graph 子图设计及实践

    本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实践. 前言 在先前的 Query Engine 源码解析中,我们介绍了 2.0 ...

  4. MySQL 深入浅出数据库索引原理(转)

    本文转自:https://www.cnblogs.com/aspwebchh/p/6652855.html 前段时间,公司一个新上线的网站出现页面响应速度缓慢的问题, 一位负责这个项目的但并不是搞技术 ...

  5. 【Important】数据库索引原理

    为什么要给表加上主键? 为什么加索引后会使查询变快? 为什么加索引后会使写入.修改.删除变慢? 什么情况下要同时在两个字段上建索引? 想理解索引原理必须清楚一种数据结构(平衡树非二叉)也就是b tre ...

  6. 索引原理 B tree

    数据库原理之-索引 背景介绍: 用数据库的时候经常有几个疑问: 1:为啥通过加索引就能提升数据的查询料率? 2:为啥加多了索引会导致增删改的效率变低? 3:为啥有的人能用好有的人用不好? 这些问题我们 ...

  7. mysql索引原理及优化(一)

    什么是索引 索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-tree的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表 ...

  8. MySQL 索引原理以及慢查询优化

    本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree ...

  9. 图数据库 Nebula Graph TTL 特性

    导读 身处在现在这个大数据时代,我们处理的数据量需以 TB.PB, 甚至 EB 来计算,怎么处理庞大的数据集是从事数据库领域人员的共同问题.解决这个问题的核心在于,数据库中存储的数据是否都是有效的.有 ...

随机推荐

  1. 【C# IO 操作 】Big-endian 和 Little-endian 详解

    首先,认识字节(Byte),计算机中Byte意思为"字节",8个二进制位构成1个"字节(Byte)",即1Byte=8bit,字节是计算机处理数据的基本单位.所 ...

  2. Hadoop权威指南 - 学习笔记

    初识Hadoop.关于MapReduce Hadoop宏观介绍 相对于其他系统的优势 关系型数据库管理系统 为什么不能用配有大量硬盘的数据库进行大规模分析?为什么需要Hadoop? 因为计算机硬盘的发 ...

  3. LINUX服务器常用命令

    转至:https://my.oschina.net/7shell/blog/70508 常用命令 查看所有80端口的连接数 1. netstat -nat|grep -i "80" ...

  4. node热加载

    node可以通过require热加载文件,这里先提一下require的加载方式: 当我们第一次使用require加载模块时require会把被加载文件的绝对路径作为key存放在require的cach ...

  5. PyTorch深度学习实践——多分类问题

    多分类问题 目录 多分类问题 Softmax 在Minist数据集上实现多分类问题 作业 课程来源:PyTorch深度学习实践--河北工业大学 <PyTorch深度学习实践>完结合集_哔哩 ...

  6. java内存区域模型和详解

    一,概述 java虚拟机运行时数据区模型图: 主要包括:程序计数器,java虚拟机栈,本地方法栈,java 堆,方法区(元空间). 其中堆和方法区由所有线程共享的数据区:程序计数器,java虚拟机栈, ...

  7. linux定时任务 - crontab定时任务

    crontab 定时任务命令 linux 系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另 外, 由于使用者 ...

  8. rsyn实现服务器源码同步

    近期技术总监提出,要建立预生产环境,代码实现灰度发布.需要多台服务器源码保持一致. 实施步骤 1.安装rsyn服务端并添加环境变量. 2.安装客户端并配置环境变量. 3.更改配置文件并开放防火墙端口. ...

  9. JVM知识梳理

    JDK 是什么? JDK 是用于支持 Java 程序开发的最小环境. Java 程序设计语言 Java 虚拟机 Java API类库 JRE 是什么? JRE 是支持 Java 程序运行的标准环境. ...

  10. Kubernetes 使用kubeadm创建集群

    镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 实践环境 CentOS-7-x86_64-DVD-1810 Docker 19.03.9 Kubernetes version: v1.20.5 ...