(Почему) нам нужно вызвать кеш или сохранить на RDD

Когда резервный распределенный набор данных (RDD) создается из текстового файла или коллекции (или из другого RDD), нужно ли нам явно указывать "кеш" или "упорствовать", чтобы хранить данные RDD в памяти? Или данные по RDD, хранящиеся распределенным способом в памяти по умолчанию?

val textFile = sc.textFile("/user/emp.txt")

В соответствии с моим пониманием, после вышеупомянутого шага, textFile является RDD и доступен во всех/некоторых из памяти node.

Если да, почему нам нужно вызывать "кеш" или "упорствовать" на текстовом RDD, то?

Ответ 1

Большинство операций RDD ленивы. Подумайте о RDD как о описании серии операций. RDD - это не данные. Итак, эта строка:

val textFile = sc.textFile("/user/emp.txt")

Он ничего не делает. Он создает RDD, который говорит "нам нужно будет загрузить этот файл". На данный момент файл не загружается.

Операции RDD, которые требуют наблюдения за содержимым данных, не могут быть ленивыми. (Эти действия называются действиями.) Пример: RDD.count - указать количество строк в файле, файл должен быть прочитан. Поэтому, если вы пишете textFile.count, в этот момент файл будет считан, строки будут подсчитаны, и счетчик будет возвращен.

Что делать, если вы снова вызываете textFile.count? То же самое: файл будет считан и подсчитан снова. Ничего не хранится. RDD - это не данные.

Итак, что делает RDD.cache? Если вы добавите textFile.cache к вышеуказанному коду:

val textFile = sc.textFile("/user/emp.txt")
textFile.cache

Он ничего не делает. RDD.cache также является ленивой операцией. Файл все еще не читается. Но теперь RDD говорит "прочитайте этот файл, а затем кешируйте содержимое". Если вы затем запустите textFile.count в первый раз, файл будет загружен, кэширован и подсчитан. Если вы вызовете textFile.count второй раз, операция будет использовать кеш. Он просто берет данные из кеша и подсчитывает строки.

Поведение кэша зависит от доступной памяти. Если файл не помещается в память, например, textFile.count вернется к обычному поведению и перечитает файл.

Ответ 2

Я думаю, что вопрос лучше сформулировать так:

Когда нам нужно вызывать кеш или сохраняться на RDD?

Процессы искры ленивы, т.е. ничего не произойдет, пока это не понадобится. Чтобы быстро ответить на вопрос, после выпуска val textFile = sc.textFile("/user/emp.txt") ничего не происходит с данными, создается только HadoopRDD, используя файл в качестве источника.

Скажем, мы немного преобразуем эти данные:

val wordsRDD = textFile.flatMap(line => line.split("\\W"))

Снова ничего не происходит с данными. Теперь есть новый RDD wordsRDD, который содержит ссылку на testFile и функцию, которая будет применяться при необходимости.

Только когда действие вызывается на RDD, например wordsRDD.count, будет выполняться цепочка RDD, называемая lineage. То есть данные, разбитые на разделы, будут загружены исполнителями Spark-кластеров, будет применена функция flatMap, и результат будет вычислен.

В линейном родословном, как и в этом примере, cache() не требуется. Данные будут загружены исполнителям, все преобразования будут применены и, наконец, будет вычислен count, все в памяти - если данные вписываются в память.

cache полезен, когда линия RDD разветвляется. Предположим, вы хотите отфильтровать слова предыдущего примера в счет для положительных и отрицательных слов. Вы можете сделать это следующим образом:

val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()

Здесь каждая ветвь вызывает перезагрузку данных. Добавление явного выражения cache гарантирует, что обработка, выполненная ранее, будет сохранена и повторно использована. Работа будет выглядеть следующим образом:

val textFile = sc.textFile("/user/emp.txt")
val wordsRDD = textFile.flatMap(line => line.split("\\W"))
wordsRDD.cache()
val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()

По этой причине cache называется "разбить линию", поскольку он создает контрольную точку, которая может быть повторно использована для дальнейшей обработки.

Правило большого пальца: используйте cache, когда линия вашего RDD разветвляется или когда RDD используется несколько раз, как в цикле.

Ответ 3

Нужно ли нам называть "кеш" или "упорствовать" явно для хранения данных RDD в памяти?

Да, только при необходимости.

Данные по RDD, хранящиеся распределенным образом в памяти по умолчанию?

Нет!

И вот почему:

  • Spark поддерживает два типа общих переменных: широковещательные переменные, которые могут использоваться для кэширования значения в памяти на всех узлах и аккумуляторов, которые являются только "добавленными" переменными, такими как счетчики и суммы.

  • RDD поддерживают два типа операций: преобразования, которые создают новый набор данных из существующего, и действия, которые возвращают значение программе драйвера после выполнения вычисления в наборе данных. Например, map - это преобразование, которое передает каждый элемент набора данных через функцию и возвращает новый RDD, представляющий результаты. С другой стороны, сокращение - это действие, которое агрегирует все элементы RDD с использованием некоторой функции и возвращает конечный результат программе драйвера (хотя есть также параллельное сокращениеByKey, которое возвращает распределенный набор данных).

  • Все преобразования в Spark ленивы, поскольку они не сразу вычисляют их результаты. Вместо этого они просто запоминают преобразования, применяемые к некоторому базовому набору данных (например, к файлу). Преобразования вычисляются только тогда, когда действие требует, чтобы результат возвращался в программу драйвера. Эта конструкция позволяет Spark работать более эффективно - например, мы можем понять, что набор данных, созданный с помощью карты, будет использоваться в сокращении и возвращать только результат сокращения в драйвер, а не более крупный сопоставленный набор данных.

  • По умолчанию каждый преобразованный RDD может быть пересчитан каждый раз, когда вы запускаете на нем действие. Однако вы также можете сохранить RDD в памяти с использованием метода persist (или cache), и в этом случае Spark будет поддерживать элементы вокруг кластера для более быстрого доступа при следующем запросе. также поддерживает постоянные RDD на диске или реплицируется через несколько узлов.

Подробнее см. Руководство по программированию Spark.

Ответ 4

Добавление другой причины для добавления (или временного добавления) вызова метода cache.

для проблем с отладочной памятью

с методом cache, искра даст информацию для отладки относительно размера RDD. поэтому в искровом интегрированном пользовательском интерфейсе вы получите информацию о потреблении памяти RDD. и это оказалось очень полезным для диагностики проблем памяти.

Ответ 5

Ниже приведены три ситуации, в которых вы должны кэшировать свои RDD:

с использованием RDD много раз

выполнение нескольких действий на одном и том же RDD

для длинных цепочек (или очень дорогих) преобразований