计算机系统应用教程网站

网站首页 > 技术文章 正文

(十四) JanusGraph 索引管理 索引图英文翻译

btikc 2024-10-26 08:41:20 技术文章 11 ℃ 0 评论

图数据的特性注定了对于大多数的检索场景,需要从点出发或者从边出发去检索,同时顶点和边中的属性,用于有效的过滤数据,提升在一张大图中高效检索数据的诉求。

基本使用场景

(图索引是整个图上的全局索引结构,它允许在足够选择性的条件下通过顶点或边的属性有效地检索顶点或边。)常见的使用场景例如:

g.V().has('name', 'hercules')
g.E().has('reason', textContains('loves'))

(这是是参考之前使用Java链接JanusGraph那章中使用的例子) 9. Java连接JanusGraph


第一个查询要求所有名称为"hercules"的顶点。

第二个要求所有边,其中属性“reason”包含“loves”这个词。

索引就是为了解决诸如此类的问题的,如果没有图索引来回答这些查询,则需要对图中的所有顶点或边进行全面扫描,以找到与给定条件匹配的顶点或边,这对于大型图来说是非常低效和不可行的。

图索引类型

JanusGraph区分了两种类型的图索引:复合索引和混合索引。复合索引非常快速高效,但仅限于对先前定义的特定属性键组合进行相等查找。混合索引可用于对索引键的任何组合进行查找,并支持多个条件谓词(不过这取决于后端使用的搜索引擎具体支持哪些类型的索引

组合索引(Composite Index):

组合索引通过一个或者一组属性组成的固定的属性组合进行等值检索,这些属性必须是预定义且是固定顺序的。 组合索引支持唯一约束,设置为unique,则该属性组合必须保证唯一性。 排序在内存中,成本高。

混合索引(Mixed Index)

使用混合索引遍历顶点或边的时候,可以使用任何预先添加的属性key的组合。混合索引比组合索引更灵活,支持更多的条件谓词,而组合索引只支持相等判断查询。混合索引比组合索引查询速度要慢。和组合索引不同,混合索引需要配置索引后端,如elasticsearch, solr, luncen。混合索引支持全文检索、范围查询、地理位置查询等,并且不支持唯一性检查。排序效率高。其实这种类型生产使用程度更高,往往搭建部署JanusGraph的时候,都会配置后端的搜索引擎。

索引操作

上面提到的两种方式的索引都通过如下方法创建:

JanusGraphManagement.buildIndex(String, Class)

其中第一个参数定义索引的名称,第二个参数指定要索引的元素类型(例如Vertex.Class)方法返回图索引的一个构造器

索引生命周期及操作

在具体操作索引之前需了解下索引的生命周期:

我们的目标是从索引开始,通过一系列action,最终使索引进入ENABLED状态。
一个索引只能是处于以下几个状态中的一种:

状态

说明

INSTALLED

索引已安装在系统中,但尚未向群集中的所有实例注册

REGISTERED

索引已向群集中的所有实例注册,但尚未启用

ENABLED

索引已启用并正在使用

DISABLED

索引不在使用

常用基本操作

// 打开数据库
graph = JanusGraphFactory.open('xxxx')

// 获取遍历句柄
g = graph.traversal()

// 开启事务
mgmt = graph.openManagement()

// 查看进行中的事务
graph.getOpenTransactions()


// 回滚当前正在进行的事务
graph.tx().rollback()

// 回滚多条事务
for(i=0;i<size;i++) {graph.getOpenTransactions().getAt(0).rollback()}  //size替换为事务的数量

  
// 打印Schema
mgmt.printSchema()
// 打印边标签
mgmt.printEdgeLabels()
// 打印顶点标签
mgmt.printVertexLabels()
// 打印属性键
mgmt.printPropertyKeys()
// 打印索引
mgmt.printIndexes()

幽灵实例

janusgraph在导入数据前或者导入数据后都可以创建索引,对于导入数据前创建索引一般不易有问题,在导入数据后创建索引比较复杂且容易出错,导致索引无法生效。创建索引之前,确定JanusGraph没有其它事务正在运行很重要。创建索引前先检查是否存在幽灵实例,有就关掉,最好也不要先写入数据,否则会很痛苦。。

# 查看
gremelin > mgmt = graph.openManagement()
gremelin > mgmt.getOpenInstances()
gremelin > mgmt.commit()

关闭掉相关幽灵实例:

gremelin > mgmt = graph.openManagement()
gremelin > ids = mgmt.getOpenInstances()
gremelin > for(String id : ids){if(!id.contains("(")){mgmt.forceCloseInstance(id)}}
gremelin > mgmt.commit()

使用以下代码回滚未提交的事务:

gremelin > mgmt = graph.openManagement()
gremelin > all_graphtx = graph.getOpenTransactions();
gremelin > for(i=0; i < all_graphtx.size(); i++) { graph.getOpenTransactions().getAt(0).rollback() };


索引操作

  1. REGISTER_INDEX:注册索引,安装索引后必须注册;
  2. REINDEX:重新索引,创建索引时,已存在数据;
  3. ENABLE_INDEX:启用索引,必须先注册索引后,才能启用索引;
  4. DISABLE_INDEX:停用索引;
  5. REMOVE_INDEX:删除索引,但是只能删除组合索引composite index。

获取相关属性

gremelin > mgmt = graph.openManagement()
# 获取属性
gremelin > tid =  mgmt.getPropertyKey("tid")
gremelin > date = mgmt.getPropertyKey("date")
gremelin > event_id = mgmt.getPropertyKey("event_id")
gremelin > client = mgmt.getPropertyKey("client")

创建索引

# 创建组合索引,indexOnly(vertexLabel)表示只在顶点上创建了索引
# 使用时需要用hasLable('')指定顶点lable信息,要不然不会命中索引
mgmt.buildIndex("composite_index_tid",Vertex.class).addKey(tid).buildCompositeIndex()
mgmt.commit()
# 创建混合索引'search'是索引后端名称,与配置文件中配置的名称对应index.search.backend=elasticsearch
mgmt.buildIndex("mixed_index_date",Vertex.class).addKey(date).indexOnly(vertexLabel).buildMixedIndex("search")
mgmt.buildIndex("mixed_index_date_event_id",Vertex.class).addKey(date).addKey(event_id).indexOnly(vertexLabel).buildMixedIndex("search")
mgmt.buildIndex("mixed_index_date_event_id_client",Vertex.class).addKey(date).addKey(client).addKey(event_id).indexOnly(vertexLabel).buildMixedIndex("search")
mgmt.commit()

注册索引

对于索引上已原有数据的情况,接下来,我们需要将索引的状态从INSTALLED更新到REGISTERED状态

# 更新索引状态到 REGISTER
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex("index_tid"),SchemaAction.REGISTER_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date'),SchemaAction.REGISTER_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date_event_id'),SchemaAction.REGISTER_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex("mixed_index_date_event_id_client"),SchemaAction.REGISTER_INDEX).get()
mgmt.commit()

启用索引

将索引从REGISTERED更新为ENABLE状态。如果该索引字段已经存有数据,则需要重做索引,如果是新的字段,则直接应用,这两种操作都能使索引更新为ENABLE状态。

# 更新索引状态到 ENABLE
m = graph.openManagement()
m.updateIndex(m.getGraphIndex('index_tid'), SchemaAction.ENABLE_INDEX).get() 
m.updateIndex(m.getGraphIndex('mixed_index_date'), SchemaAction.ENABLE_INDEX).get() 
m.updateIndex(m.getGraphIndex('mixed_index_date_event_id'), SchemaAction.ENABLE_INDEX).get() 
m.updateIndex(m.getGraphIndex('mixed_index_date_event_id_client'), SchemaAction.ENABLE_INDEX).get() 
m.commit() 


# 等待索引生效,这步可能也会超时报错,所以创建索引前尽量不要写数据在里面
ManagementSystem.awaitGraphIndexStatus(graph, 'index_tid').status(SchemaStatus.ENABLED).call()
ManagementSystem.awaitGraphIndexStatus(graph, 'mixed_index_date').status(SchemaStatus.ENABLED).call()
ManagementSystem.awaitGraphIndexStatus(graph, 'mixed_index_date_event_id').status(SchemaStatus.ENABLED).call()
ManagementSystem.awaitGraphIndexStatus(graph, 'mixed_index_date_event_id_client').status(SchemaStatus.ENABLED).call()

# 最后查看索引状态,已经从INSTALLED 变成 REGISTERED 最后变成ENABLED就可用了
m = graph.openManagement()
m.printSchema()


冻结索引

对已有的索引,冻结操作可以使索引不再有效。值得注意的是,冻结的步骤是不可反向的,即冻结状态无法再还原为创建/注册/可用。

mgmt = graph.openManagement()
//复合索引
mgmt.updateIndex(mgmt.getGraphIndex('index_tid'), SchemaAction.DISABLE_INDEX).get()
//混合索引
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date'), SchemaAction.DISABLE_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date'), SchemaAction.DISABLE_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date_event_id'), SchemaAction.DISABLE_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date_event_id_client'), SchemaAction.DISABLE_INDEX).get()

mgmt.commit()



移除索引

移除和冻结一样,也是不可逆的操作。值得注意的是,对于不同类型的索引要求的移除操作不同。复合索引和中心顶点索引只需通过JanusGraph进行移除操作即可,但混合索引需要使用索引后端进行移除。

删除索引操作删除了与索引相关联的所有内容,除了其模式定义和DISABLED状态。 该索引的架构存根即使在删除后仍然保留,尽管其存储占用空间可以忽略不计并且是固定的。


个人实践:JanusGraph禁用索引置为DISABLED状态,但在通过JanusGraph查询这个索引索引仍然会存在。即使通过ElasticSearch删除了索引数据,但JanusGraph中这个索引仍然是可以查到的,但状态是DISABLED。


gremelin > mgmt = graph.openManagement()
# 获取属性
gremelin > tid =  mgmt.getPropertyKey("tid")
gremelin > date = mgmt.getPropertyKey("date")
gremelin > event_id = mgmt.getPropertyKey("event_id")
gremelin > client = mgmt.getPropertyKey("client")

# 创建索引,indexOnly(vertexLabel)表示只在顶点上创建了索引,使用时需要用hasLable('')指定顶点lable信息,要不然不会命中索引
mgmt.buildIndex("composite_index_tid",Vertex.class).addKey(tid).buildCompositeIndex()

# 创建混合索引'search'是索引后端名称,与配置文件中配置的名称对应index.search.backend=elasticsearch
mgmt.buildIndex("mixed_index_date",Vertex.class).addKey(date).indexOnly(vertexLabel).buildMixedIndex("search")
mgmt.buildIndex("mixed_index_date_event_id",Vertex.class).addKey(date).addKey(event_id).indexOnly(vertexLabel).buildMixedIndex("search")
mgmt.buildIndex("mixed_index_date_event_id_client",Vertex.class).addKey(date).addKey(client).addKey(event_id).indexOnly(vertexLabel).buildMixedIndex("search")
mgmt.commit()

# 更新索引状态到 REGISTER
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex("index_tid"),SchemaAction.REGISTER_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date'),SchemaAction.REGISTER_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date_event_id'),SchemaAction.REGISTER_INDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex("mixed_index_date_event_id_client"),SchemaAction.REGISTER_INDEX).get()
mgmt.commit()

# 等待索引生效,这步可能会超时报错,多等一会,或者隔段时间试一次
ManagementSystem.awaitGraphIndexStatus(graph,"index_tid").status(SchemaStatus.REGISTERED).call()
ManagementSystem.awaitGraphIndexStatus(graph,"mixed_index_date").status(SchemaStatus.REGISTERED).call()
ManagementSystem.awaitGraphIndexStatus(graph,"mixed_index_date_event_id").status(SchemaStatus.REGISTERED).call()
ManagementSystem.awaitGraphIndexStatus(graph,"mixed_index_date_event_id_client").status(SchemaStatus.REGISTERED).call()

# 更新索引状态到 ENABLE
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex('index_tid'), SchemaAction.ENABLE_INDEX).get() 
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date'), SchemaAction.ENABLE_INDEX).get() 
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date_event_id'), SchemaAction.ENABLE_INDEX).get() 
mgmt.updateIndex(mgmt.getGraphIndex('mixed_index_date_event_id_client'), SchemaAction.ENABLE_INDEX).get() 
mgmt.commit() 

# 等待索引生效,这步可能也会超时报错,所以创建索引前尽量不要写数据在里面
ManagementSystem.awaitGraphIndexStatus(graph, 'index_tid').status(SchemaStatus.ENABLED).call()
ManagementSystem.awaitGraphIndexStatus(graph, 'mixed_index_date').status(SchemaStatus.ENABLED).call()
ManagementSystem.awaitGraphIndexStatus(graph, 'mixed_index_date_event_id').status(SchemaStatus.ENABLED).call()
ManagementSystem.awaitGraphIndexStatus(graph, 'mixed_index_date_event_id_client').status(SchemaStatus.ENABLED).call()

# 最后查看索引状态,已经从INSTALLED 变成 REGISTERED 最后变成ENABLED就可用了
mgmt = graph.openManagement()
mgmt.printSchema()

顶点中心索引(Vertex-centric index)

Vertex-centric index(顶点中心索引)是为每个vertex建立的本地索引结构,在大型graph中,每个vertex有数千条Edge,在这些vertex中遍历效率将会非常低(需要在内存中过滤符合要求的Edge)。Vertex-centric index可以通过使用本地索引结构加速遍历效率,组合索引只支持最左匹配原则。

// 开始事务
mgmt = graph.openManagement()
// 获取属性键
hahaProperty = mgmt.getPropertyKey('haha') 
// 获取边标签
follow = mgmt.getEdgeLabel('follow')

// 创建中心顶点索引
// 参数依次序是 边、索引、方向,属性
mgmt.buildEdgeIndex(follow, 'follow_vci', Direction.BOTH, Order.desc, hahaProperty)
// 提交事务
mgmt.commit()


// 注册
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getRelationIndex(follow, "follow_vci"),SchemaAction.REGISTER_INDEX).get()
// 提交事务
mgmt.commit()



// 注册
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getRelationIndex(follow, "follow_vci"),SchemaAction.ENABLE_INDEX).get()
// 提交事务
mgmt.commit()

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表