实时计算图TigerGraph之GSQL体验(二)

上一篇文章简介了TigerGraph的安装上手和基本的benchmark 以及可能的问题. 这一篇来单独说说他的核心查询和图算法实现的基础GSQL.

0x00.简介

GSQL从使用上来说, 其实算一个图灵完整的程序语言了, 简单说就是在CURD上类似SQL语法, 但是实现复杂的自定义的查询的时候类似编程语言, 可以快速的自定义函数/算法来实现复杂的逻辑和分析, 这点上篇文章中的查询某个顶点的kstep (K步邻居)已经见识到了.

然后GraphStudio一般是业务或展示的时候使用, 很多高级功能和特别的解析都只能在gsql上进行, 所以一般上手TigerGraph之后 ,肯定会大量使用gsql了, 虽然目前gsql的测试很麻烦..

更新: 官方后续更新说会添加无需编译的方式? 具体时间/效果未知

0x01.CURD语xv法

1.Schema的CURD

虽然GSQL花了很大的篇幅介绍图计算的算法, 但是我们最常用的当然还是基本的CURD(增删改查), 因为前端页面上只能直接写GSQL的函数, 还不能直接写快捷的SQL语法查询. (当然可以包装一下?) ,首先gsql 进入交互模式.

注意以下事项:

  1. 每个顶点必须指定PRIMARY_ID 字段, 且只允许unit和string类型 (最新2.2版开始, 只推荐用string)

  2. 前端页面目前不能在有数据的时候修改Schema, 所以理论上都是gsql内修改Schema ,推荐参考官方文档.

    1.4更新: 2.2企业版⽀持在有数据的时候修改schema, 但是单机开发版本不⽀持. 同时通过这种⽅式修改schema可能导致性能下降,不建议在⽣产环境使⽤, 如必要则应尽量减少次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#1.新增,分顶点/边/图来说
#1.1 新增顶点, 第一个是主键必须,这里选unit(0~2^64-1)
CREATE VERTEX v_name (PRIMARY_ID vid uint, v_prop1 string, v_prop2 int, v_prop3 string, v_prop1 string)
#1.2新增边,from v1 to v2,边两头必须指定,边方向也必须指定directed/undirected
CREATE UNDIRECTED EDGE e_name (FROM person, TO person, connect_day DATETIME)
#1.3新增图,把创建的顶点和边包进去.
CREATE GRAPH g_name (v_name, e_name)
#1.4新增导入任务(并发读取,m1,m2代表节点1,2)
CREATE LOADING JOB job_name FOR GRAPH g_name {
DEFINE FILENAME fname;
DEFINE FILENAME fname2;
LOAD fname TO VERTEX v_name VALUES($0,$1,$2,....,$n) WHERE to_int($1 > 50); #有n列就赋n个参数,后面where可以筛选数据
LOAD fname2 TO EDGE e_name VALUES($0,$1,$2,....,$n) USING SEPARATOR="\t", HEADER="false", EOL="\n";
}
#实际调用,如果数据步子loadingdata文件夹下,则需要指定路径.
RUN LOADING JOB job_name USING fname="m1:/data01/vertex/person/vTest.csv", fname2="m2:/data01/likes/eTest.csv"

#2.查询图Schema信息
#2.1查询当前的所有顶点/边/图大概信息
ls #最常用
gadmin status graph -v #可以看到集群各机器的顶点/边数量和占用

#2.3查询导入任务的状态信息
show loading status job_id |ALL

#3.修改Schema,安全考虑这里必须先建立一个job,一个job内可以写多行操作
#注意创建job操作分为两种,默认为local,还有一种是global模式.
use graph g_name
use global
#新增顶点/边
create global schema_change job add_ele {
add vertex v_name (PRIMARY_ID vid uint, v_prop datetime,);
add vertex v_name2 (PRIMARY_ID vid uint, v_prop2 float,);
add directed edge e_name (from v_name, to v_name2 ,e_prop int);
}

#修改已有的顶点/边属性 (比如新增/删除),注意新增默认是在最后append.暂时不可以自定义选择插入位置
create global schema_change job del_rep {
alter vertex domain add attribute( pnew uint);
alter vertex domain drop attribute(pold, ,prank_sn);
}
#执行修改任务
RUN job job_name


#4.删除(大数据量时-->慎用)
#4.1 删图: 先删边,再删顶点,最后删图
drop edge ename
drop vertex vname
drop graph gname
#4.n清空所有(慎用,操作不可逆)
drop all

注意, 如果创建了多个图, 那么目前在前端界面, 图的Schema状态是只读的, 可能是考虑误改? 所以如需要可以直接修改Schema, 建议单图/或者gsql修改了.

2.数据的CURD

核心也是在新增 & 查询 .

1
2
3
4
5
6
7
#1.新增数据

#2.查看数据

#3.修改数据

#4.删除数据

0x02. 内置SQL查询

这里的内置SQL查询可能很容易误解, 我之前对gsql的定义是 :

  1. 简单的查询类似SQL的语法, 比如select * from Stu where name = "jin" .
  2. 复杂的查询类似Kstep, PageRank,以及各种复杂逻辑,用类似C的编程方式 (for/while循环等)

但是实际上随着使用深入, 发现还是不太一样, 上面说的1类查询 , 其实是一个单独的内置查询API, 并不能在真正的GSQL中完全复用, 所以你会发现你在Query函数 里写1中查询 很多是语法报错的…

那么先来看看GSQL到底有哪些内置的查询呢? 参考文档

  1. 查询具体的顶点/边数目

    1
    2
    3
    4
    5
    6
    #查询某个顶点和边的总数目(很有用)
    select count() from Person #查Person顶点的数目

    #如果我要查某条边呢?注意不能直接写边的名字,而要指定出入的顶点类型
    select count() from Person-(likes)->Dog #查likes边的数目
    select count() from Person-(ANY)->ANY #查Person的所有边数目.(这里它甚至会查不属于自己的边)
  2. 最简单的单点查询 (注意primary_id是通指主键, 而不是说字段名)

    1
    2
    3
    4
    5
    6
    7
    8
    #查询某个顶点(很有用)
    select * from Person where primary_id=="APTX4869" #查Person顶点的主键为xx的元素
    select * from Person where age < 25 #查Person顶点的主键为xx的元素
    select name,sex from Person where name == "whoami" #查Person顶点的名字是whoami的age

    #查某条边的信息.同样也需要指定至少一个
    select * from Person-(likes)->Dog where from_id == "APTX4869"
    select * from Person-(ANY)->ANY where from_id == "APTX4869" #Any可能会用的很多.

    这里需要注意的是-()-> 表示的是遍历的方向, 所以即使是双向边也需要指定, 并且至少指定一个源顶点以及它的主键,

  3. 好像没有了? 只有这么几个内置查询么? 后续待确认…

0x03.编写gsql文件

类似mysql的sql文件 , 当你在生产环境建立一个复杂图模型, 并且有许多自定义化的操作的时候, 你肯定不可能每次在命令行里敲命令了, 会固化为一个文件直接去执行, 所以熟悉了基本的语法之后, 之后都是用gsql xx.gsql 文件的方式去执行. 那么先配置一下vim的gsql高亮, 以便后续查看.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#1.载vim文件到服务器
wget https://raw.githubusercontent.com/tigergraph/ecosys/master/editor/vim/gsql.vim
#删掉19行的echo信息 echom "gsql syntax highlighting code.", 不然每次打开gsql文件都会提示回车进入..
vim gsql.vim

#2.echo有显示直接跳到第三步
echo $VIM
#(可选)没显示,就自己去找一下系统路径,然后修改一下后面的.vimrc文件第二行的$VIM
/usr/share/vim/vim74/ #Unix下很可能在这里,注意vim后的数字可能不一样.自行修改. 然后
cp gsql.vim /usr/share/vim/vim74/syntax/

#3.创建/修改当前用户下的".vimrc"文件, 添加如下几行.
vim ~/.vimrc
au BufRead,BufNewFile *.gsql set filetype=gsql
au! Syntax gsql source $VIM/syntax/gsql.vim #绝对地址则如:au! Syntax gsql source /usr/share/vim/vim74/syntax/gsql.vim
syntax on

配置好后就开始编写一套gsql 代码了, 整个gsql大概分为三个大类:

  • Schema的增删改查
  • 数据的增删改查
  • 图算法的设计

依次顺序来介绍, 首先定义一个图的结构, 参考官方的图例 , 创建一个schema.gsql ,以下是一些常用参考文档:

  • 3种方式导入Set/List数据 (个人推荐使用第三种split() 函数,比较通用)
  • 通过函数过滤部分数据 ()
  • 自定义过滤数据(待补)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#注意清空所有,慎重.
DROP ALL
CREATE VERTEX company (PRIMARY_ID companyId string, id string, company_name string default "unknown")
CREATE VERTEX persons (PRIMARY_ID pId string, id string, profileId string default "0", registrationDate uint default 0)
CREATE VERTEX skill (PRIMARY_ID skillId uint, id string)
CREATE UNDIRECTED EDGE person_work_company (FROM persons, TO company, positionId double, industryId float, startTime int)
CREATE UNDIRECTED EDGE person_person (FROM persons, TO persons, connect_time int)
CREATE UNDIRECTED EDGE all_to_skill (FROM *, TO skill)
CREATE UNDIRECTED EDGE all_to_company (FROM *, TO company)
CREATE GRAPH work_graph(*) # 这个*太重要了....之前我都是手动输入的..

#我还不确定export schema g_name具体作用.
EXPORT SCHEMA work_graph

USE GRAPH work_graph
#注意,sys.data_root是系统特有属性,不可用随便替换名字...
SET sys.data_root="./"
CREATE LOADING JOB load_data FOR GRAPH work_graph {
LOAD "$sys.data_root/small_companies"
TO VERTEX company VALUES ($0, $0, $2)
USING HEADER="false", SEPARATOR=",", QUOTE="double";
LOAD "$sys.data_root/small_persons"
TO VERTEX persons VALUES ($0, $0, $1, $2)
WHERE to_int($2) >= 0
USING HEADER="false", SEPARATOR=",", QUOTE="double";
# Example of flattening a multi-valued field
LOAD "$sys.data_root/small_persons"
TO temp_table member_skill_table (memberID, skillID)
VALUES ($0, flatten($3, "|", 1))
USING HEADER="false", SEPARATOR=",", QUOTE="double";
LOAD temp_table member_skill_table
TO VERTEX skill VALUES ($"skillID", $"skillID");

LOAD "$sys.data_root/small_person_company"doge
TO EDGE person_work_company VALUES($0, $1, $2, $3, $4)
WHERE to_int($4) >= 0
USING HEADER="false", SEPARATOR=",", QUOTE="double";
LOAD "$sys.data_root/small_person_person"
TO EDGE person_person VALUES($0, $1, $2)
LOAD "$sys.data_root/small_person_person"
TO EDGE person_person VALUES($0, $1, $2)
WHERE to_int($2) >= 0
USING HEADER="false", SEPARATOR=",", QUOTE="double";

#接下来的例子展示了如何去从一个数据源映射多个Schema的边和顶点关系.注意对比
LOAD "$sys.data_root/small_all_to_all"
TO EDGE all_to_skill VALUES ($2 company, $3 skill)
WHERE $0 == "s" AND $1 == "c",
TO EDGE all_to_skill VALUES ($2 persons, $3 skill)
WHERE $0 == "s" AND $1 == "m",
TO EDGE all_to_skill VALUES ($2 skill, $3 skill)
WHERE $0 == "s" AND $1 == "s",
TO EDGE all_to_company VALUES ($2 company, $3 company)
WHERE $0 == "c" AND $1 == "c",
TO EDGE all_to_company VALUES ($2 persons, $3 company)
WHERE $0 == "c" AND $1 == "m",
TO EDGE all_to_company VALUES ($2 skill, $3 company)
WHERE $0 == "c" AND $1 == "s"
USING HEADER="false", SEPARATOR=",", QUOTE="double";
}
#最后运行
RUN LOADING JOB load_data

0x04.累加器(Accumulator)

Accumulator 应该算是gsql的核心了, 但是这个部分的确不是一两话说的清的…我也不知道还有没有机会补上这个部分. 建议大家还是根据需要自己先看吧. 多看一下官方示例里的参考. 但是注意

0x05. 问题

GSQl虽然功能非常强大, 但是初上手使用你可能会发现一些尴尬的问题: 你想实现一个在业务看来很简单的功能需求, 但是可能得把整个gsql文档翻个底朝天…然后还是没有找到想要的答案. 那么如何避免整个问题呢 :)

先说一些可能很常用的点, 如果你测试的时候也遇到了, 可以参考:

  1. 因为内置SQL查询和gsql-query 实际不兼容, 所以切勿把SQL的传统写法带入了gsql, 不然可能会有许多的问题…

  2. primary_id 这个字段目前不能直接在gsql里查询 , 可能是因为前者是用GSE 直接映射的, 后者是完全依靠GPE计算, 然后因为官方没有这样关联, 所以你不可使用 v.id / v.primary_id (假设id是主键的名字) 来进行查询. (那如何查询主键比如selcet * from Stu where sid == 1 呢) .一种是用内置SQL查询, 另外一种是把id字段冗余一列…当做属性查询 (但是个人认为这个方案应该很不科学.. 速度上也会相差许多.)

    更新: 官方回复在3.0版本会支持, 并支持属性索引, 发布时间未知.

  3. 目前属性没有索引,实测来看可能遍历时间会很有点长(至少是每个属性几秒, 而且顶点数据量还不算大.). 请设计Schema的时候注意拆分热点属性, 也可等以后官方有了属性索引功能再说

  4. 主键字段不要使用可能过长随机的字符串, 会造成可能的严重的内存/存储膨胀. 如果遇到这个问题, 目前可参考的思路是把所有长串做一次hash处理, 然后之后再转换回去.

0xn. 其他

1.ASCII编码参考

如果使用REST的方式从HDFS读取数据, 那么以下是ASCII码常用参考:

  • 9是tab \t

  • 10 是换行 \n (注意换行和回车是相近的,不用os上表现不一样.) , URL编码中%0A 是换行符

  • 13 是回车 \r

  • 44是 ,

  • 47是 /

    rest-api导入参考, 详细参数说明

常用命令 : gadmin config-apply && gadmin restart -f

2.限制各模块资源使用

1
2
#第一个命令会进入runtime文件,直接编辑保存即可. 比如限制GSE模块15G内存使用 --> GSE:"ProcMaxGB=15"
gadmin --configure runtime && gadmin config-apply && gadmin restart gse -fy

怎么限制CPU或者磁盘IO的使用还不清楚…(磁盘估计限制不了.)

1.4补充: 设置查询的timeout可以间接控制CPU使⽤率 (官方回复, 我还没有确认)


参考资料:

  1. Tigergraph-Gsql入门-zhihu
  2. 图数据库实战入门 —— 一个简单的电影推荐系统实现-zhihu
  3. gsql官方文档之定义Schema&加载数据(一)
  4. gsql官方文档之查询(二)
  5. gsql官方实例demo(三)
  6. (更新)图计算算法库文档
  7. (12月更新)gsql中文文档版本[更新ing]