全方位讲解 Nebula Graph 索引原理和使用

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 1 和 query 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 2和query 3是沿着边一路找到特定路径条件的扩展游走; - pure-prop-condition queries,如
query 0和query 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,并且常常可以借助管道符在此之上做拓展查询,参考下面例子:
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:
- 只有复合左匹配的被复合索引的属性真子集的过滤条件才能被只支持
- 比如,index_a:
(isRisky: bool, age: int),和 index_b:(age: int, isRisky: bool)在查询WHERE n.user.age > 18这个语句时,只有index_b复合最左匹配,能满足这个查询。
- 比如,index_a:
- 针对一些从属性作为查询的起点,找点、边的情况,原生索引是不能满足全文搜索的匹配场景的。这时候,我们应该考虑使用 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 索引原理和使用的更多相关文章
- 分布式图数据库 Nebula Graph 的 Index 实践
导读 索引是数据库系统中不可或缺的一个功能,数据库索引好比是书的目录,能加快数据库的查询速度,其实质是数据库管理系统中一个排序的数据结构.不同的数据库系统有不同的排序结构,目前常见的索引实现类型如 B ...
- Nebula Graph 在微众银行数据治理业务的实践
本文为微众银行大数据平台:周可在 nMeetup 深圳场的演讲这里文字稿,演讲视频参见:B站 自我介绍下,我是微众银行大数据平台的工程师:周可,今天给大家分享一下 Nebula Graph 在微众银行 ...
- 解析 Nebula Graph 子图设计及实践
本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实践. 前言 在先前的 Query Engine 源码解析中,我们介绍了 2.0 ...
- MySQL 深入浅出数据库索引原理(转)
本文转自:https://www.cnblogs.com/aspwebchh/p/6652855.html 前段时间,公司一个新上线的网站出现页面响应速度缓慢的问题, 一位负责这个项目的但并不是搞技术 ...
- 【Important】数据库索引原理
为什么要给表加上主键? 为什么加索引后会使查询变快? 为什么加索引后会使写入.修改.删除变慢? 什么情况下要同时在两个字段上建索引? 想理解索引原理必须清楚一种数据结构(平衡树非二叉)也就是b tre ...
- 索引原理 B tree
数据库原理之-索引 背景介绍: 用数据库的时候经常有几个疑问: 1:为啥通过加索引就能提升数据的查询料率? 2:为啥加多了索引会导致增删改的效率变低? 3:为啥有的人能用好有的人用不好? 这些问题我们 ...
- mysql索引原理及优化(一)
什么是索引 索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-tree的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表 ...
- MySQL 索引原理以及慢查询优化
本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree ...
- 图数据库 Nebula Graph TTL 特性
导读 身处在现在这个大数据时代,我们处理的数据量需以 TB.PB, 甚至 EB 来计算,怎么处理庞大的数据集是从事数据库领域人员的共同问题.解决这个问题的核心在于,数据库中存储的数据是否都是有效的.有 ...
随机推荐
- 【windows 操作系统】线程句柄HANDLE与线程ID的关系
什么是句柄 句柄是一种指向指针的指针.我们知道,所谓指针是一种内存地址.应用程序启动后,组成这个程序的各对象是住留在内存的.如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访 ...
- AcWing 207. 球形空间产生器
传送门 思路: 设球心坐标为(x1,x2,...,xn),有 ,由此我们可以列出N+1个二次方程,我们可以对前后两个方程做差,来得到N个一次方程,同时可以消掉常数C,第i个方程即 那么我们就可以直接采 ...
- Python:绘图添加中文标题
(20条消息) Python绘图如何显示中文标题_wulei_1107103372的博客-CSDN博客_python画图中文标题 plt.rcParams['font.sans-serif'] = [ ...
- 爬虫之Scrapy框架介绍及基础用法
今日内容概要 爬虫框架之Scrapy 利用该框架爬取博客园 并发编程 今日内容详细 爬虫框架Scrapy 1.什么是框架? 框架类似于房子的结构,框架会提前帮你创建好所有的文件和内部环境 你只需要往对 ...
- 01_c语言再学习_基础部分(1)
目录: 1.编译基础 2.c语言关键字 3.c语言数据类型 4.二进制/8进制/16进制 5.计算机内存数值存储方式:sizeof/原码/反码/补码 6.c语言中的字符和字符串 7.c语言中的数组和字 ...
- vue项目npm run dev报错events.js:160 throw er; // Unhandled 'error' event listen EADDRINUSE :::8002
出错情况,如下图: 报错原因: listen EADDRINUSE :::8002 意思是当前8002端口被占用 解决办法: 一:简单粗暴:关掉可能影响的相关程序,重新执行启动. 二: 1.Win+R ...
- Eureka单机&集群配置
目录 Eureka是什么 自我保护机制 版本选择 服务搭建 创建项目 导入GAV坐标 application启动类添加注解 配置yml 启动项目 集群配置 修改上面的yml 打jar包到另外一台电脑O ...
- tensorflow源码解析之common_runtime-executor-上
目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...
- linux中文件颜色,蓝色,白色等各自代表的含义
linux中文件颜色,蓝色,白色等各自代表的含义 绿色文件---------- 可执行文件,可执行的程序 红色文件-----------压缩文件或者包文件 蓝色文件----------目录 白色文件- ...
- Kotlin笔记小结(For Java Developer)
这篇文章为kotlin学习记录,主要针对的是自己的知识盲区,不适用于新手. 文中所有demo均来自于kotlin官网 类型 整形 Type Size (bits) Min value Max valu ...