(Чому) нам потрібно викликати кеш або зберігати RDD


171

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

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

Як я розумію, після вищевказаного кроку textFile є RDD і доступний у всій / деякій пам'яті вузла.

Якщо так, то чому нам потрібно викликати "кеш" або "зберігати" у textFile RDD тоді?

Відповіді:


300

Більшість операцій 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перейде до звичної поведінки і перечитає файл.


4
Привіт, Даніель, - якщо ви викликаєте кеш, це означає, що RDD не перезавантажується з джерела (наприклад, текстовий файл) - як ви можете бути впевнені, що дані з текстового файлу є останніми після його кешування? (чи виявляє це іскра чи це ручна операція розгортатись () періодично, щоб гарантувати, що вихідні дані будуть перераховані пізніше у родовому?)
andrew.butkus

також - якщо вам потрібно періодично скасовувати, - якщо у вас є rdd, який є кешованим, залежно від іншого RDD, який є кешованим, ви повинні розгорнути обидва RDD, щоб побачити відновлені результати?
andrew.butkus

21
Іскра просто припускає, що файл ніколи не зміниться. Він зчитує файл у довільний момент часу і може перечитати його частини за необхідності пізніше. (Наприклад, якщо частина даних була витіснена з кешу.) Тож вам краще зберегти файли незмінними! Просто створіть новий файл з новим іменем, коли у вас є нові дані, а потім завантажте його як новий RDD. Якщо ви постійно отримуєте нові дані, загляньте в Іскрову трансляцію.
Даніель Дарабос

10
Так. RDD незмінні, тому кожен RDD припускає, що його залежності також непорушні. Іскровий потік дозволяє налаштувати такі дерева, які працюють на потоці змін. Але ще простішим рішенням є побудова дерева у функції, яка приймає ім'я файлу як його параметр. Тоді просто зателефонуйте до функції нового файлу та пуфа, ви отримали нове дерево обчислень.
Даніель Дарабос

1
@Humoyun: На вкладці "Зберігання" Spark UI ви бачите, скільки всіх керованих RDD кешується. Дані можуть бути настільки великими, що лише 40% їх вміщується в загальній пам'яті, яку ви маєте для кешування. Одним із варіантів у цьому випадку є використання perisistта вибір параметра зберігання, що дозволяє розсипати дані кешу на диск.
Даніель Дарабос

197

Я думаю, що питання краще сформулювати так:

Коли нам потрібно викликати кеш або зберегти RDD?

Іскрові процеси ледачі, тобто нічого не відбудеться, поки цього не потрібно. Для швидкої відповіді на запитання після val textFile = sc.textFile("/user/emp.txt")видачі даних нічого не відбувається, лише a 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 використовується кілька разів, як у циклі.


1
Дивовижно. Дякую. Ще одне пов'язане питання. Коли ми кешуємо чи зберігаємо, дані зберігатимуться у пам’яті виконавця чи робочого вузла. Якщо це пам'ять виконавця, як Spark визначає, у якого виконавця є дані.
Рамана

1
@RamanaUppala використовується пам'ять виконавця. Частка пам'яті виконавця, що використовується для кешування, контролюється конфігурацією spark.storage.memoryFraction. Що стосується того, у якого виконавця є які дані, RDD буде відслідковувати свої розділи, які розповсюджуються на виконавців.
maasg

5
@maasg Виправте мене, якщо я помиляюся, але ні я, cacheні persist можу зламати рід .
нуль323

Де б зберігалися словаRDD, якщо у вищенаведеному прикладі у нас не було .cache ()?
sun_dare

що робити, якщо перед двома підрахунками ми об'єднаємо дві гілки назад до одного rdd і підрахуємо? в цьому випадку кеш корисний?
Xiawei Zhang

30

Чи потрібно явно викликати "кеш" або "зберігати", щоб зберегти дані RDD в пам'яті?

Так, лише за потреби.

Дані RDD, які зберігаються розподіленим способом у пам'яті за замовчуванням?

Немає!

І ось чому:

  • Spark підтримує два типи загальних змінних: трансляції змінних, які можна використовувати для кешування значень у пам'яті на всіх вузлах, та акумулятори, що є змінними, до яких лише додано, таких як лічильники та суми.

  • RDD підтримують два типи операцій: перетворення, які створюють новий набір даних із наявного, та дії, які повертають значення драйверній програмі після запуску обчислення на наборі даних. Наприклад, map - це перетворення, яке передає кожен елемент набору даних через функцію і повертає новий RDD, що представляє результати. З іншого боку, Redu - це дія, яка агрегує всі елементи RDD за допомогою певної функції та повертає кінцевий результат програмі драйверів (хоча існує також паралельне reduByKey, яке повертає розподілений набір даних).

  • Всі перетворення в Спарці ліниві, оскільки вони не підраховують свої результати одразу. Натомість вони просто запам'ятовують перетворення, застосовані до деякого базового набору даних (наприклад, до файлу). Перетворення обчислюються лише тоді, коли дія вимагає повернення результату в програму драйверів. Ця конструкція дозволяє Spark працювати більш ефективно - наприклад, ми можемо зрозуміти, що набір даних, створений за допомогою карти, буде використовуватися для зменшення і повертати тільки результат зменшення для драйвера, а не для більшого зіставленого набору даних.

  • За замовчуванням кожен перетворений RDD може бути перерахований щоразу, коли виконується дія на ньому. Однак ви також можете зберегти RDD в пам'яті, використовуючи метод persist (або кеш), і в цьому випадку Spark буде зберігати елементи навколо кластера для набагато швидшого доступу наступного разу, коли ви його запитуєте. Існує також підтримка збережених RDD на диску або тиражуються через декілька вузлів.

Для отримання більш детальної інформації перегляньте посібник із програмування Spark .


1
Це не відповіло на моє запитання.
Рамана

Що на це не відповідає?
Еліаса

1
коли дані RDD зберігаються у пам'яті за замовчуванням, чому нам потрібно викликати кеш або зберегти?
Рамана

За замовчуванням RDD не зберігаються в пам'яті, тому зберігання RDD робить Spark швидше перетворення на кластері
eliasah

2
Це гарна відповідь, я не знаю, чому це було знято. Це відповідь зверху вниз, що пояснює, як працюють RDD з концепцій високого рівня. Я додав ще одну відповідь, яка йде знизу вгору: починаючи з "що робить цей рядок". Можливо, легше наслідувати когось, що тільки починається зі Спарком.
Даніель Дарабос

11

Нижче наведено три ситуації, в які слід кешувати RDD:

використовуючи RDD багато разів

виконання декількох дій на одній RDD

для довгих ланцюгів (або дуже дорогих) перетворень


7

Додавання ще однієї причини для додавання (або тимчасового додавання) cacheвиклику методу.

для налагодження пам'яті

за допомогою cacheметоду, spark надасть інформацію про налагодження щодо розміру RDD. тому в інтегрованому інтерфейсі іскри ви отримаєте інформацію про споживання пам'яті RDD. і це виявилося дуже корисним для діагностики проблем із пам'яттю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.