图数据库:Nebula

default

安装

Docker Desktop 插件

Docker Desktop 中搜索 Nubula,点击到 Extensions,找到 nebulagraph-docker-ext(名称可能不一样),安装等待完成即可。

Docker Compose安装

轻量版 docker-compose.yml 文件如下:

  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
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
version: '3.4'
services:
  metad0:
    image: docker.io/vesoft/nebula-metad:nightly
    environment:
      USER: root
    command:
      - --meta_server_addrs=metad0:9559
      - --local_ip=metad0
      - --ws_ip=metad0
      - --port=9559
      - --ws_http_port=19559
      - --data_path=/data/meta
      # - --log_dir=/logs
      # log to stderr not file
      - --logtostderr=true
      - --redirect_stdout=false
      # log to stderr not file
      - --v=0
      - --minloglevel=0
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://metad0:19559/status"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s
    ports:
      - 9559:9559
      - 19559:19559
      - 19560
    volumes:
      - ./data/meta0:/data/meta
      # - ./logs/meta0:/logs
    networks:
      - nebula-net
    restart: on-failure
    cap_add:
      - SYS_PTRACE

  storaged0:
    image: docker.io/vesoft/nebula-storaged:nightly
    environment:
      USER: root
    command:
      - --meta_server_addrs=metad0:9559
      - --local_ip=storaged0
      - --ws_ip=storaged0
      - --port=9779
      - --ws_http_port=19779
      - --data_path=/data/storage
      # - --log_dir=/logs
      # log to stderr not file
      - --logtostderr=true
      - --redirect_stdout=false
      # log to stderr not file
      - --v=0
      - --minloglevel=0
    depends_on:
      - metad0
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://storaged0:19779/status"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s
    ports:
      - 9779:9779
      - 19779:19779
      - 19780
    volumes:
      - ./data/storage0:/data/storage
      # - ./logs/storage0:/logs
    networks:
      - nebula-net
    restart: on-failure
    cap_add:
      - SYS_PTRACE

  graphd:
    image: docker.io/vesoft/nebula-graphd:nightly
    environment:
      USER: root
    command:
      - --meta_server_addrs=metad0:9559
      - --port=9669
      - --local_ip=graphd
      - --ws_ip=graphd
      - --ws_http_port=19669
      # - --log_dir=/logs
      # log to stderr not file
      - --logtostderr=true
      - --redirect_stdout=false
      # log to stderr not file
      - --v=0
      - --minloglevel=0
    depends_on:
      - storaged0
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://graphd:19669/status"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s
    ports:
      - 9669:9669
      - 19669:19669
      - 19670
    # volumes:
    #   - ./logs/graph:/logs
    networks:
      - nebula-net
    restart: on-failure
    cap_add:
      - SYS_PTRACE

  storage-activator:
    # This is just a script to activate storaged for the first time run by calling nebula-console
    # Refer to https://docs.nebula-graph.io/master/4.deployment-and-installation/manage-storage-host/#activate-storaged
    # If you like to call console via docker, run:

    # docker run --rm -ti --network host vesoft/nebula-console:nightly -addr 127.0.0.1 -port 9669 -u root -p nebula

    image: docker.io/vesoft/nebula-console:nightly
    entrypoint: ""
    environment:
      ACTIVATOR_RETRY: ${ACTIVATOR_RETRY:-30}
    command: 
      - sh
      - -c
      - |
        for i in `seq 1 $$ACTIVATOR_RETRY`; do
          nebula-console -addr graphd -port 9669 -u root -p nebula -e 'ADD HOSTS "storaged0":9779' 1>/dev/null 2>/dev/null;
          if [[ $$? == 0 ]]; then
            echo "✔️ Storage activated successfully.";
            break;
          else
            output=$$(nebula-console -addr graphd -port 9669 -u root -p nebula -e 'ADD HOSTS "storaged0":9779' 2>&1);
            if echo "$$output" | grep -q "Existed"; then
              echo "✔️ Storage already activated, Exiting...";
              break;
            fi
          fi;
          if [[ $$i -lt $$ACTIVATOR_RETRY ]]; then
            echo "⏳ Attempting to activate storaged, attempt $$i/$$ACTIVATOR_RETRY... It's normal to take some attempts before storaged is ready. Please wait.";
          else
            echo "❌ Failed to activate storaged after $$ACTIVATOR_RETRY attempts. Please check MetaD, StorageD logs. Or restart the storage-activator service to continue retry.";
            echo "ℹ️ Error during storage activation:"
            echo "=============================================================="
            echo "$$output"
            echo "=============================================================="
            break;
          fi;
          sleep 5;
        done && tail -f /dev/null;        

    depends_on:
      - graphd
    networks:
      - nebula-net

networks:
  nebula-net:
1
2
# 切换到 docker-compose.yml 文件目录,执行以下命令
docker-compose up -d

入门教程

安装 Nebula 后,自带有 NebulaGraph Studio 的 Web 客户端.

  • url:http://ip:17001
  • Host:IP地址
  • 账号:root
  • 密码:nebula

Nebula 结构

  • Schema,等于 Space
    • Tag
      • Properties
    • Edge
      • Properties
    • Index

最上层为 Schema,相当于数据库。表分为 Tag 和 Edge。其中,Tag 用于描述实体对象,Edge 用于记录对象关系。Index 中包含针对 Tag 和 Edge 所建立的索引。

初始化示例

创建 Schema, Tag, Edge, Index,在 NebulaGraph Studio 中的 Console 模块可以执行 NGQL 脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Create Space 
CREATE SPACE `demo_basketballplayer` (partition_num = 10, replica_factor = 1, charset = utf8, collate = utf8_bin, vid_type = FIXED_STRING(32));
:sleep 20;
# 切换当前空间
USE `demo_basketballplayer`;

# Create Tag: 
CREATE TAG `player` ( `name` string NULL, `age` int64 NULL) ttl_duration = 0, ttl_col = "";
CREATE TAG `team` ( `name` string NULL) ttl_duration = 0, ttl_col = "";

# Create Edge: 
CREATE EDGE `follow` ( `degree` int64 NULL) ttl_duration = 0, ttl_col = "";
CREATE EDGE `serve` ( `start_year` int64 NULL, `end_year` int64 NULL) ttl_duration = 0, ttl_col = "";
:sleep 20;

# Create Index: 
CREATE TAG INDEX `player_index_0` ON `player` ();
CREATE TAG INDEX `player_index_1` ON `player` ( `name`(20));
CREATE EDGE INDEX `follow_index_0` ON `follow` ();
CREATE EDGE INDEX `serve_index_0` ON `serve` ();

插入数据

 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
insert vertex player(name,age) values "player100":("Tim Duncan", 42);
insert vertex player(name,age) values "player101":("Tony Parker", 36);
insert vertex player(name,age) values "player102":("LaMarcus Aldridge", 33);
insert vertex player(name,age) values "player103":("Rudy Gay", 32);
insert vertex player(name,age) values "player104":("Marco Belinelli", 32);
insert vertex player(name,age) values "player105":("Danny Green", 31);
insert vertex player(name,age) values "player106":("Kyle Anderson", 25);
insert vertex player(name,age) values "player107":("Aron Baynes", 32);
insert vertex player(name,age) values "player108":("Boris Diaw", 36);
insert vertex player(name,age) values "player109":("Tiago Splitter", 34);
insert vertex team(name) values "team200":("Warriors");
insert vertex team(name) values "team201":("Nuggets");
insert edge follow(degree) values "player100"->"player101":(95);
insert edge follow(degree) values "player101"->"player100":(95);
insert edge follow(degree) values "player101"->"player102":(90);
insert edge follow(degree) values "player102"->"player101":(75);
insert edge follow(degree) values "player102"->"player100":(75);
insert edge follow(degree) values "player103"->"player102":(70);
insert edge follow(degree) values "player104"->"player101":(50);
insert edge follow(degree) values "player104"->"player100":(55);
insert edge follow(degree) values "player104"->"player105":(60);
insert edge follow(degree) values "player105"->"player104":(83);
insert edge follow(degree) values "player105"->"player100":(70);
insert edge follow(degree) values "player107"->"player100":(80);
insert edge follow(degree) values "player108"->"player101":(80);
insert edge follow(degree) values "player108"->"player100":(80);
insert edge follow(degree) values "player109"->"player100":(80);
insert edge serve(start_year,end_year) values "player100"->"team200":(1997, 2016);
insert edge serve(start_year,end_year) values "player101"->"team200":(1999, 2018);
insert edge serve(start_year,end_year) values "player101"->"team201":(2018, 2019);
insert edge serve(start_year,end_year) values "player102"->"team200":(2006, 2015);
insert edge serve(start_year,end_year) values "player102"->"team201":(2015, 2019);
insert edge serve(start_year,end_year) values "player103"->"team200":(2006, 2013);
insert edge serve(start_year,end_year) values "player103"->"team201":(2013, 2013);
insert edge serve(start_year,end_year) values "player103"->"team200":(2013, 2017);
insert edge serve(start_year,end_year) values "player103"->"team201":(2017, 2019);
insert edge serve(start_year,end_year) values "player104"->"team200":(2007, 2009);
insert edge serve(start_year,end_year) values "player104"->"team201":(2009, 2010);
insert edge serve(start_year,end_year) values "player104"->"team200":(2012, 2013);
insert edge serve(start_year,end_year) values "player105"->"team200":(2009, 2010);
insert edge serve(start_year,end_year) values "player105"->"team201":(2010, 2018);
insert edge serve(start_year,end_year) values "player105"->"team200":(2018, 2019);
insert edge serve(start_year,end_year) values "player106"->"team200":(2014, 2018);
insert edge serve(start_year,end_year) values "player106"->"team201":(2018, 2019);
insert edge serve(start_year,end_year) values "player107"->"team200":(2013, 2015);
insert edge serve(start_year,end_year) values "player107"->"team201":(2015, 2017);
insert edge serve(start_year,end_year) values "player107"->"team200":(2017, 2019);
insert edge serve(start_year,end_year) values "player108"->"team200":(2003, 2005);
insert edge serve(start_year,end_year) values "player108"->"team201":(2005, 2008);
insert edge serve(start_year,end_year) values "player108"->"team200":(2008, 2012);
insert edge serve(start_year,end_year) values "player108"->"team201":(2012, 2016);
insert edge serve(start_year,end_year) values "player108"->"team200":(2016, 2017);
insert edge serve(start_year,end_year) values "player109"->"team200":(2010, 2015);
insert edge serve(start_year,end_year) values "player109"->"team201":(2015, 2017);
insert edge serve(start_year,end_year) values "player109"->"team200":(2017, 2017);

查询及可视化

在 NebulaGraph Studio 的 Console 模块,选择 Space,执行脚本,默认显示结果为 Table,切换到 Graph 显示图关系结果。

高级使用

Match

场景

  • 点模型:运动员、球队。
  • 边模型:运动员效力的球队,运动员关注的运动员。

需求

在这个场景中,我们的需求是:找到运动员"Rudy Gay"所关注的运动员所效力过的球队的所有成员。

图模型构建

从图的角度来看,我们的需求可以抽象成三次扩散的逻辑:

1.先从点(运动员"Rudy Gay")通过(运动员关注运动员)边找到所有点(被运动员"Rudy Gay"关注的运动员)。

2.再从点(被运动员"Rudy Gay"关注的运动员)通过(运动员曾效力过的球队)边找到所有点(所有被运动员"Rudy Gay"关注的运动员曾效力过的球队)。

3.最后从点(所有被运动员"Rudy Gay"关注的运动员曾效力过的球队)通过边(运动员曾效力过的球队)找到所有点(曾效力过球队的所有运动员)。

语句编写

1
2
3
4
5
6
7
8
MATCH (startPlayer:player)-[interest:follow]-(interestPlayer:player)
WHERE id(startPlayer) == "player103"
WITH interestPlayer
MATCH (interestPlayer:player)-[serve:serve]-(serveTeam:team)
WITH serveTeam
MATCH (serveTeam:team)-[server2:serve]-(teamPlayer:player)
WITH serveTeam, collect(teamPlayer) as allTeamPlayer
RETURN serveTeam, allTeamPlayer

TODO

参考

【1】基于 Docker - NebulaGraph Database 手册 (nebula-graph.com.cn)

【2】美团图数据库平台建设及业务实践 - 美团技术团队 (meituan.com)

【3】Nebula入门学习——day3 nGQL指南 - bonelee - 博客园 (cnblogs.com)

Licensed under CC BY-NC-SA 4.0
Gear(夕照)的博客。记录开发、生活,以及一些不足为道的思考……