跳转到主要内容

配置 HStore 分布式后端

1 概述

hstore 是 HugeGraph 的分布式存储后端。图使用该后端时,HugeGraph-Server 本地磁盘上不保存任何图数据, 数据由另外两个进程负责:

  • HugeGraph-PD(Placement Driver)保存集群元数据:已注册的 Store 列表、每个图的分区布局、分区到 Store 的映射关系、图的 Schema 以及 Schema 的 id 计数器。
  • HugeGraph-Store 保存实际的键值数据,并通过 Raft 在多个 Store 节点之间复制。

Server 进程内嵌了 PD 客户端和 Store 客户端。每次读写时,它先向 PD 查询该 key 属于哪个分区、当前哪个 Store 节点是这个分区的 leader,然后把请求直接发给这个 Store 节点。

服务端的适配层是 hugegraph-hstore 模块,它以后端名 hstore 注册,驱动版本为 1.13

选择 hstore 影响的不只是数据写到哪里,Server 还会根据后端类型切换下列行为:

方面使用 hstore使用本地后端
Schema 存储通过 PD 元数据驱动读写 SchemaSchema 保存在 m store 中
Schema id通过 PD 客户端由 PD 分配由 schema store 分配
System store没有独立的 system store,系统数据写入 graph store独立的 s store
任务调度器distributedlocal
权限管理器StandardAuthManagerV2StandardAuthManager
后端版本校验读取 graph store读取 system store
init-store.sh跳过该图,元数据由 PD 和 Store 负责创建本地 store

2 前置条件

hstore 不能独立工作。在 Server 打开 hstore 图之前,PD 集群和至少一个 Store 节点必须已经运行, 并且启动顺序如下:

  1. PD,先启动以便组成 Raft 组。
  2. Store,通过 gRPC 向 PD 注册。gRPC 地址出现在 PD 自身 pd.initial-store-list 中的 Store 会直接进入 Up 状态;不在该列表中、并且 PD 从未见过它处于 UpOffline 的 Store 会注册为 Pending, 需要先激活才能提供数据服务。
  3. Server,随后从 PD 读回 Store 列表。

服务端需要关注的默认端口:

进程gRPC 端口REST 端口
PD86868620
Store85008520

服务端的 pd.peers 指向 PD 的 gRPC 端口,而不是 REST 端口。

另外两个进程的安装与配置方式,参见 安装/构建 HugeGraph-PD安装/构建 HugeGraph-Store

3 选择 hstore 后端

3.1 图配置文件

在图的属性文件(例如 conf/graphs/hugegraph.properties)中设置后端:

backend=hstore
serializer=binary
store=hugegraph
pd.peers=127.0.0.1:8686

关于这四个配置项:

  • backend=hstore 选择该适配层。自 1.7.0 起允许的取值为 memoryrocksdbhbasehstore。 发行包中做校验的位置对该值不区分大小写。
  • serializer=binary 是必需的。注册 hstore 后端时只注册了配置空间和存储 provider,并没有注册自己的 序列化器,适配层就是按二进制序列化器编写的。serializer 的内置默认值是 text,因此必须显式写出该项。
  • store=hugegraph 是 PD 看到的图名中的命名空间部分。Server 以 <graphspace>/<store> 打开 provider, 每个底层 store 再追加自己的后缀,因此 PD 中每个 store 对应一个图条目:图数据是 DEFAULT/hugegraph/g,schema store 位是 DEFAULT/hugegraph/mgraphspace 默认为 DEFAULTgm 是固定的。
  • pd.peers 是以逗号分隔的 PD gRPC 地址列表。适配层从配置中读取该项,而不是从 rest-server.properties 中读取,图级别的元数据连接也使用同一个值。

如果图配置文件中没有 pd.peers,那么在加载图时,只要 usePD 为 true 或者后端是 hstore, Server 会把 rest-server.properties 中的值复制到图配置里。不过在图配置文件中显式写出该项更清晰。

3.2 rest-server.properties

# use pd
usePD=true
pd.peers=127.0.0.1:8686

usePD=true 让 Server 在启动时从 PD 加载元数据。在这条路径上,它会把元数据管理器连接到 PD,创建内置的 admin 账号和默认图空间,加载图空间与服务,创建内部的系统图(后端固定为 hstore),并加载 PD 中保存的 图配置。

它和图级别的 backend=hstore 是两个独立的开关:一个图可以使用 hstoreusePD 保持默认的 false, 此时 Server 不会走基于 PD 的元数据路径。发行包自带的测试启动脚本在后端为 hstore 时会设置该项。

3.3 发行包中的模板文件

发行包在 conf/graphs/hstore.properties.template 中提供了一份该后端的现成图配置文件。它与 hugegraph.properties 的差别是:把 backend 设为 hstore、不注释 pd.peers=127.0.0.1:8686、 并且不包含内存管理配置段。

hstore 的 Docker 镜像会自动套用这份模板:它删除 conf/graphs/hugegraph.properties,再把模板重命名过去, 因此容器启动时就已经选好了 hstore 后端。

本地构建的发行包默认编译了 hstore provider。rocksdb-only 这个 Maven profile 会把编译进去的后端列表 收窄为只有 rocksdb,用这种方式构建出来的发行包会以 Unsupported backend type 拒绝 backend=hstore

4 hstore 配置项

hstore 配置空间中只有下面两个配置项,它们写在图的属性文件里。

配置项默认值说明
hstore.partition_count0分区数量,PD 依据该值控制分区(Number of partitions)。
hstore.shard_count0副本数量,PD 依据该值控制分区副本(Number of copies)。

4.1 hstore.partition_count

每个 graph store 第一次被打开时,Server 会把这个数字连同图名一起发给 PD。取负值会在此处被拒绝, 报错信息为 The value of hstore.partition_count cannot be less than 0.

PD 对该值的处理方式:

  • 0,也就是默认值,表示交给 PD 决定。对图数据 store,PD 使用自身集群级别的分区总数,该总数由 pd.initial-store-list 中的条目数量、partition.store-max-shard-countpartition.default-shard-count 推算得出;对 /m/s store 固定使用 1
  • 取值在 1 到该总数之间时,按原值使用。
  • 取值大于该总数时,会被下调到该总数。

该数字在 store 首次向 PD 注册时生效,之后再修改属性文件不会让已有的图重新分区。

4.2 hstore.shard_count

hstore.shard_count 声明在 hstore 配置空间中,属性文件里也接受该项,但当前版本服务端没有任何代码读取它: 适配层读取的只有 hstore.partition_count 一项。实际生效的副本数由 PD 的配置决定,即 PD application.yml 中的 partition.default-shard-count

5 只在 hstore 模式下生效的其他配置项

下列配置项位于公共的 rest-server.properties 和图属性文件中,但只有在使用 PD 和 hstore 后端时才生效, 或者才会改变行为。source 列给出该配置项在 HugeGraph master 分支上的声明位置(文件与行号)。

配置项文件默认值在 hstore 模式下的作用source
pd.peersrest-server.properties127.0.0.1:8686用于元数据、服务发现和系统图的 PD 地址ServerOptions.java:195-201
pd.peers{graph}.properties127.0.0.1:8686后端适配层自身使用的 PD 地址CoreOptions.java:649-654
usePDrest-server.propertiesfalseServer 启动时是否从 PD 加载元数据ServerOptions.java:390-396
clusterrest-server.propertieshg-test集群名,作为所有 PD 元数据 key 的前缀ServerOptions.java:187-193
init_store.enabledrest-server.propertiestruePD/Store 部署下应设为 false,元数据已由存储侧负责ServerOptions.java:371-380
graph.load_from_local_configrest-server.propertiesfalse启动时是否在 PD 中的图配置之外,额外扫描 conf/graphsServerOptions.java:355-361
auth.graph_storerest-server.propertieshugegraph保存权限数据的图,关闭 init-store 时会校验它使用 hstore 后端ServerOptions.java:591-598
graphspace{graph}.propertiesDEFAULTPD 看到的图名的第一段CoreOptions.java:679-685

init-store.sh 从不初始化 hstore 图。在开启的路径上,它扫描 conf/graphs 并跳过后端为 hstore 的每一个 图。如果用 init_store.enabled=false 整体关闭这一步,它会改为校验 admin 账号仍然能在 PD 启动路径上被创建: usePD 必须为 true、权限图必须存在于本地配置中且后端为 hstoreauth.admin_pa 必须显式设置为非空值。 否则启动会直接失败,而不是使用公开的默认密码创建账号。

6 Server 如何通过 PD 发现 Store

适配层在进程中第一次打开 hstore 图时,一次性构建这些客户端:

  1. pd.peers 构建 PD 客户端配置,带上 PD 的鉴权凭据,并开启客户端侧的分区缓存。
  2. 创建进程级的 PD 客户端。
  3. 用该 PD 客户端创建进程级的 Store 客户端。

创建 Store 客户端时,会把一个基于 PD 的分区器同时注册为 Store 客户端节点管理器的 node provider、 partitioner 和 notifier。路由逻辑全部在这个分区器中:

  • 单点和前缀请求:向 PD 查询拥有该 key 的分区,取该分区的 leader 副本,把请求发到对应的 store id。
  • 按 code 的范围扫描:按 code 逐个遍历分区直到覆盖整个范围,每个分区产生一个目标 Store。
  • 全图扫描:向 PD 查询该图的活跃 Store,并向全部 Store 扇出请求。
  • Store 地址解析:通过 PD 把 store id 解析成主机和端口。
  • 缓存失效:当某个 Store 返回分区 leader 已迁移时,notifier 会更新 PD 客户端缓存中的分区 leader 并使过期的分区条目失效,之后的请求就会跟随新的 leader。

由于 Store 列表来自 PD 而不是配置文件,增删 Store 节点只需要针对同一个 PD 集群启动或停止它, 服务端不需要改任何配置。

7 后端能力

hstore 并不支持本地后端的所有查询形式。对用户可见的差异如下:

特性是否支持
按 key 前缀扫描支持
按 key 范围扫描支持
带范围条件的查询支持
带 order by 的查询支持
分页查询支持
OLAP 属性支持
Task 和 Server 顶点支持
Scan token不支持
按名称查询 Schema不支持
按 label 查询不支持
in 条件的查询不支持
contains 的查询不支持
contains key 的查询不支持
按输入 id 顺序排序不支持
按 label 删除边不支持
更新顶点属性不支持
更新边属性不支持
事务不支持
Number 类型不支持
聚合属性不支持
TTL不支持

不支持按输入 id 顺序排序,是因为多节点批量扫描会按 Store 对输入 key 分组,从而丢失全局顺序; 不支持更新顶点和边属性,是因为属性被存放在单个 cell 中。

8 验证

Server 启动后,后端指标接口会返回 PD 当前认为处于活跃状态的 Store 数量:

curl http://localhost:8080/metrics/backend

响应中的 nodes 就是 PD 返回的活跃 Store 数量。nodes0 说明 Server 连上了 PD,但 PD 中没有状态为 Up 的 Store,通常是 Store 节点还没注册,或者因为不在 PD 的 pd.initial-store-list 中而注册成了 Pending