跳到内容

重新索引

图索引以顶点为中心的索引描述了如何构建图全局和以顶点为中心的索引以提高查询性能。如果索引键或标签是在同一管理事务中新定义的,则这些索引将立即可用。在这种情况下,无需重新索引图,可以跳过此部分。如果索引键和标签在索引构建之前就已存在,则需要重新索引整个图,以确保索引包含以前添加的元素。本节描述重新索引过程。

警告

重新索引是一个手动过程,包含多个步骤。必须仔细按照正确的顺序执行这些步骤,以避免索引不一致。

概述

JanusGraph 可以在定义索引后立即开始写入增量索引更新。但是,在索引完成并可用之前,JanusGraph 还必须对与新索引模式类型相关联的所有现有图元素进行一次性读取。一旦此重新索引作业完成,索引就完全填充并准备好使用。然后必须启用索引才能在查询处理期间使用。

重新索引前

重新索引过程的起点是索引的构建。有关全局图和以顶点为中心的索引的完整讨论,请参阅提高性能的索引。请注意,全局图索引通过其名称唯一标识。以顶点为中心的索引通过其名称以及定义索引的边标签或属性键的组合唯一标识——后者的名称在本节中称为索引类型,并且仅适用于以顶点为中心的索引。

针对现有模式元素构建新索引后,建议等待几分钟,以便将索引通知给集群。记下索引名称(以及以顶点为中心的索引的索引类型),因为重新索引时需要此信息。

准备重新索引

重新索引作业有两种执行框架可供选择

  • MapReduce
  • JanusGraphManagement

MapReduce 上的重新索引支持大型、水平分布的数据库。JanusGraphManagement 上的重新索引会生成一个单机 OLAP 作业。这旨在方便和快速处理足以由一台机器处理的小型数据库。

重新索引需要

  • 索引名称(字符串 — 用户在构建新索引时提供给 JanusGraph)
  • 索引类型(字符串 — 定义以顶点为中心的索引的边标签或属性键的名称)。这仅适用于以顶点为中心的索引 - 全局图索引留空。

在 MapReduce 上执行重新索引作业

在 MapReduce 上生成和运行重新索引作业的推荐方法是通过 MapReduceIndexManagement 类。以下是使用此类别运行重新索引作业的粗略步骤

  • 打开 JanusGraph 实例
  • 将图实例传入 MapReduceIndexManagement 的构造函数
  • MapReduceIndexManagement 实例上调用 updateIndex(<index>, SchemaAction.REINDEX)
  • 如果索引尚未启用,请通过 JanusGraphManagement 启用它

此类实现了一个 updateIndex 方法,该方法仅支持其 SchemaAction 参数的 REINDEXDISCARD_INDEX 操作。该类使用类路径上的 Hadoop 配置和 jar 启动 Hadoop MapReduce 作业。支持 Hadoop 1 和 2。此类从传递给其构造函数的 JanusGraph 实例获取有关索引和存储后端(例如 Cassandra 分区器)的元数据。

graph = JanusGraphFactory.open(...)
mgmt = graph.openManagement()
mr = new MapReduceIndexManagement(graph)
mr.updateIndex(mgmt.getRelationIndex(mgmt.getRelationType("battled"), "battlesByTime"), SchemaAction.REINDEX).get()
mgmt.commit()

MapReduce 上的重新索引示例

以下 Gremlin 片段概述了 MapReduce 重新索引过程的所有步骤,这是一个自包含的示例,使用最小的虚拟数据针对 Cassandra 存储后端。

// Open a graph
graph = JanusGraphFactory.open("conf/janusgraph-cql-es.properties")
g = graph.traversal()

// Define a property
mgmt = graph.openManagement()
desc = mgmt.makePropertyKey("desc").dataType(String.class).make()
mgmt.commit()

// Insert some data
graph.addVertex("desc", "foo bar")
graph.addVertex("desc", "foo baz")
graph.tx().commit()

// Run a query -- note the planner warning recommending the use of an index
g.V().has("desc", containsText("baz"))

// Create an index
mgmt = graph.openManagement()

desc = mgmt.getPropertyKey("desc")
mixedIndex = mgmt.buildIndex("mixedExample", Vertex.class).addKey(desc).buildMixedIndex("search")
mgmt.commit()

// Rollback or commit transactions on the graph which predate the index definition
graph.tx().rollback()

// Block until the SchemaStatus transitions from INSTALLED to REGISTERED
report = ManagementSystem.awaitGraphIndexStatus(graph, "mixedExample").call()

// Run a JanusGraph-Hadoop job to reindex
mgmt = graph.openManagement()
mr = new MapReduceIndexManagement(graph)
mr.updateIndex(mgmt.getGraphIndex("mixedExample"), SchemaAction.REINDEX).get()

// Enable the index
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex("mixedExample"), SchemaAction.ENABLE_INDEX).get()
mgmt.commit()

// Block until the SchemaStatus is ENABLED
mgmt = graph.openManagement()
report = ManagementSystem.awaitGraphIndexStatus(graph, "mixedExample").status(SchemaStatus.ENABLED).call()
mgmt.rollback()

// Run a query -- JanusGraph will use the new index, no planner warning
g.V().has("desc", containsText("baz"))

// Concerned that JanusGraph could have read cache in that last query, instead of relying on the index?
// Start a new instance to rule out cache hits.  Now we're definitely using the index.
graph.close()
graph = JanusGraphFactory.open("conf/janusgraph-cql-es.properties")
g.V().has("desc", containsText("baz"))

在 ManagementSystem 上执行重新索引作业

要在 ManagementSystem 上运行重新索引作业,请调用带 SchemaAction.REINDEX 参数的 ManagementSystem.updateIndex。例如

m = graph.openManagement()
i = m.getGraphIndex('indexName')
m.updateIndex(i, SchemaAction.REINDEX).get()
m.commit()

ManagementSystem 使用本地线程池并行运行重新索引作业。默认情况下,并发级别等于可用处理器的数量。如果要更改并发级别,可以添加如下参数

// only use one thread to run reindexing
m.updateIndex(i, SchemaAction.REINDEX, 1).get()

ManagementSystem 示例

以下将一些示例数据加载到 BerkeleyDB 支持的 JanusGraph 数据库中,事后定义索引,使用 ManagementSystem 重新索引,最后启用和使用索引

import org.janusgraph.graphdb.database.management.ManagementSystem

// Load some data from a file without any predefined schema
graph = JanusGraphFactory.open('conf/janusgraph-berkeleyje.properties')
g = graph.traversal()
m = graph.openManagement()
m.makePropertyKey('name').dataType(String.class).cardinality(Cardinality.LIST).make()
m.makePropertyKey('lang').dataType(String.class).cardinality(Cardinality.LIST).make()
m.makePropertyKey('age').dataType(Integer.class).cardinality(Cardinality.LIST).make()
m.commit()
graph.io(IoCore.gryo()).readGraph('data/tinkerpop-modern.gio')
graph.tx().commit()

// Run a query -- note the planner warning recommending the use of an index
g.V().has('name', 'lop')
graph.tx().rollback()

// Create an index
m = graph.openManagement()
m.buildIndex('names', Vertex.class).addKey(m.getPropertyKey('name')).buildCompositeIndex()
m.commit()
graph.tx().commit()

// Block until the SchemaStatus transitions from INSTALLED to REGISTERED
ManagementSystem.awaitGraphIndexStatus(graph, 'names').status(SchemaStatus.REGISTERED).call()

// Reindex using JanusGraphManagement
m = graph.openManagement()
i = m.getGraphIndex('names')
m.updateIndex(i, SchemaAction.REINDEX)
m.commit()

// Wait for the index to be enabled
ManagementSystem.awaitGraphIndexStatus(graph, 'names').status(SchemaStatus.ENABLED).call()

// Run a query -- JanusGraph will use the new index, no planner warning
g.V().has('name', 'lop')
graph.tx().rollback()

// Concerned that JanusGraph could have read cache in that last query, instead of relying on the index?
// Start a new instance to rule out cache hits.  Now we're definitely using the index.
graph.close()
graph = JanusGraphFactory.open("conf/janusgraph-berkeleyje.properties")
g = graph.traversal()
g.V().has('name', 'lop')

常见问题

启动作业时出现 IllegalArgumentException

在索引构建后不久启动重新索引作业时,作业可能会因以下异常之一而失败

The index mixedExample is in an invalid state and cannot be indexed.
The following index keys have invalid status: desc has status INSTALLED
(status must be one of [REGISTERED, ENABLED, DISABLED])

The index mixedExample is in an invalid state and cannot be indexed.
The index has status INSTALLED, but one of [REGISTERED, ENABLED, DISABLED] is required

构建索引时,其存在会广播到集群中的所有其他 JanusGraph 实例。这些实例必须确认索引的存在才能开始重新索引过程。确认可能需要一段时间才能到达,具体取决于集群的大小和连接速度。因此,在构建索引之后和开始重新索引过程之前,应该等待几分钟。

请注意,由于 JanusGraph 实例故障,确认可能会失败。换句话说,集群可能会无限期地等待失败实例的确认。在这种情况下,用户必须手动将失败的实例从集群注册表中删除,如故障与恢复中所述。恢复集群状态后,必须通过在管理系统中再次手动注册索引来重新启动确认过程。

mgmt = graph.openManagement()
rindex = mgmt.getRelationIndex(mgmt.getRelationType("battled"),"battlesByTime")
mgmt.updateIndex(rindex, SchemaAction.REGISTER_INDEX).get()
gindex = mgmt.getGraphIndex("byName")
mgmt.updateIndex(gindex, SchemaAction.REGISTER_INDEX).get()
mgmt.commit()

等待几分钟以等待确认到达后,重新索引作业应该成功启动。

找不到索引

重新索引作业中的此异常表明给定名称的索引不存在或名称未正确指定。重新索引全局图索引时,应仅指定构建索引时定义的索引名称。重新索引全局图索引时,除了定义以顶点为中心的索引的边标签或属性键的名称外,还必须提供索引名称。