Статьи

Распределенные графовые вычисления с Gremlin

script-Ступенно в Фавна Gremlin позволяет выполнение произвольного сценария Gremlin против всех вершин в графе Faunus. Эта простая идея имеет интересные последствия для распределенных графовых вычислений на основе Gremlin. Например, можно оценить скрипт Gremlin на каждой вершине в исходном графе (например, Titan) параллельно, сохраняя локальность данных / процессов. В этом разделе будут обсуждаться следующие два варианта использования.

  • Глобальные мутации графа : параллельное обновление вершин / ребер в кластере Титана при произвольном вычислении.
  • Алгоритмы глобального графа : распространять информацию на произвольные глубины в кластере Титан, чтобы вычислить некоторый алгоритм в параллельной манере.

script-Ступенно требует , чтобы скрипт EXIST Gremlin в HDFS и имеет следующие определения методов:

  • setup(String... args): вызывается один раз за маппер во время Map.setup()
  • map(FaunusVertex v, String... args): вызывается для каждого ключа / значения Map.map()с тем, vчтобы бытьFaunusVertex
  • cleanup(String... args): вызывается один раз за маппер во время Map.cleanup()

Наконец, script-ступенно имеет метод подписи: FaunusPipeline.script(String scriptUri, String... args).

Глобальные мутации графа

Один из способов сделать глобальные мутации графа с Faunus — это использовать средство, InputFormatкоторое считывает граф из базы данных графа (например, Titan и / или Rexster ), а затем мутировать представление Faunus этого графа в HDFS на различных этапах Gremlin / Faunus. Наконец, удалите исходный граф в базе данных и загрузите новый мутированный граф Фаунуса, используя соответствующий OutputFormat. Проблема этого метода заключается в том, что он требует очистки и повторной загрузки базы данных графов, что для производственных систем 24 × 7 не является разумным требованием.

Другим способом параллельного обновления графа является использование script-step для обеспечения в реальном времени распределенных массовых обновлений исходного графа в самой базе данных графа. Простой пример объясняет идею. Предположим, что набор данных Graph of the Gods (см. Визуализацию ) в Titan / Cassandra (или Titan / HBase) совмещен с узлами данных Hadoop и трекерами задач. Далее предположим, что вызывается следующий скрипт Gremlin / Groovy FathersNames.groovy(распространяется с Faunus at data/FathersNames.groovy). Этот скрипт добавит новое свойство к каждой вызванной вершине fathersName. Значением этого свойства является имя отца вершины (если у вершины есть father). В качестве побочного эффекта от Faunus Stringуказывается имя добавленного отца.

// FathersName.groovy

def g

// create a Titan database connection that exists for the life of the mapper
def setup(args) {
    conf = new org.apache.commons.configuration.BaseConfiguration()
    conf.setProperty('storage.backend', args[0])
    conf.setProperty('storage.hostname', 'localhost') // co-located Hadoop ensures local Titan machine has vertex
    g = com.thinkaurelius.titan.core.TitanFactory.open(conf)
}

// process each Faunus vertex
//  - find the corresponding Titan vertex as it shares the same id with the Faunus vertex
//  - update the Titan vertex given some Gremlin traversal over Titan
def map(v, args) {
    u = g.v(v.id) // the Faunus vertex id is the same as the original Titan vertex id
    pipe = u.out('father').name
    if (pipe.hasNext()) u.fathersName = pipe.next();
    u.name + "'s father's name is " + u.fathersName
}

// close the Titan database connection
def cleanup(args) {
    g.shutdown()
}

Поместите этот FathersName.groovyфайл в HDFS, используя Gremlin.

gremlin> hdfs.copyFromLocal('data/FathersNames.groovy', 'FathersNames.groovy')
==>null

С этим файлом в HDFS можно выполнить следующий обход Gremlin / Faunus. Для всех вершин полубога и бога в графе, добавьте имя отца вершины, если оно существует.

g.V.has('type','demigod','god').script('FathersName.groovy','cassandrathrift')

Graph богов в Titan служит вкладом в работу Faunus. NoOpOutputFormatиспользуется, поскольку нет необходимости писать график, поскольку оригинальное представление графа Titan изменяется.

gremlin> g = FaunusFactory.open('bin/titan-cassandra-input.properties')                
==>faunusgraph[titancassandrainputformat->graphsonoutputformat]
gremlin> g.setGraphOutputFormat(NoOpOutputFormat.class)                                
==>null
gremlin> g.V.has('type','demigod','god').script('FathersName.groovy','cassandrathrift')
13/03/06 18:21:43 INFO mapreduce.FaunusCompiler: Compiled to 1 MapReduce job(s)
13/03/06 18:21:43 INFO mapreduce.FaunusCompiler: Executing job 1 out of 1: MapSequence[com.thinkaurelius.faunus.mapreduce.transform.VerticesMap.Map, com.thinkaurelius.faunus.mapreduce.filter.PropertyFilterMap.Map, com.thinkaurelius.faunus.mapreduce.util.ScriptMap.Map]
13/03/06 18:21:43 INFO mapreduce.FaunusCompiler: Job data location: output/job-0
13/03/06 18:21:43 WARN mapred.JobClient: Use GenericOptionsParser for parsing the arguments. Applications should implement Tool for the same.
13/03/06 18:21:44 INFO mapred.JobClient: Running job: job_201303061253_0077
13/03/06 18:21:45 INFO mapred.JobClient:  map 0% reduce 0%
13/03/06 18:21:53 INFO mapred.JobClient:  map 50% reduce 0%
...
==>hercules's father's name is jupiter
==>pluto's father's name is null
==>jupiter's father's name is saturn
==>neptune's father's name is null

Глядя на исходный граф в Титане, у тех вершин, которые имеют отцов, появилось новое fathersNameсвойство.

gremlin> g.V.transform("{it.name + ' ' + it.fathersName}")             
...
13/03/06 18:25:40 INFO mapred.JobClient: Running job: job_201303061253_0078
13/03/06 18:25:41 INFO mapred.JobClient:  map 0% reduce 0%
...
==>tartarus null
==>alcmene null
==>sea null
==>hydra null
==>hercules jupiter
==>cerberus null
==>pluto null
==>saturn null
==>sky null
==>jupiter saturn
==>neptune null
==>nemean null

В приведенном выше примере Фаунус выполняет функцию фильтрации вершин, подлежащих обработке, до полубогов и богов, а затем предоставляет сценарию идентификатор вершины Титана, который в конечном итоге будет вычислен. Одна из важных идей, которую следует извлечь из этого примера, заключается в том, что, хотя Фаунус связан с вершиной (шаг может действовать только на вершине и ее падающих ребрах), при scriptналичии соединения с Титаном можно совершать произвольные прогулки произвольной глубины на один шаг и, таким образом, позволяет более выразительные объемные / глобальные вычисления на Титане.

Типичные случаи использования массовых мутаций описаны ниже:

  • «Нам нужно преобразовать все Stringдаты в Longдаты».
  • «Для всех вершин людей давайте добавим новое ребро с именем grandfather, которое вычисляется по father* fatherребрам».
  • «Нам нужно удалить всех пользователей и все их соответствующие данные (закачки, папки, данные о дружбе и т. Д.), Которые не вошли в систему за последние 2 года».

Наконец, обратите внимание, что localhostиспользуется в качестве storage.hostnameтитана. Если кластер Hadoop находится в одном месте с кластером Cassandra (или HBase), то localhostэто местоположение FaunusVertexи TitanVertex. Таким образом, вычисления существуют вдоль данных в кластере.