Імп. ПОРАДА :
Кожен раз, коли у вас є ініціалізація важкої ваги, яку слід робити один раз для багатьох RDD
елементів, а не один раз на RDD
елемент, і якщо ця ініціалізація, наприклад створення об'єктів із сторонньої бібліотеки, не може бути серіалізована (щоб Spark міг передати її через кластер до робочі вузли), використовувати mapPartitions()
замість
map()
. mapPartitions()
передбачає, що ініціалізація повинна бути виконана один раз для кожного робочого завдання / потоку / розділу замість одного разу на RDD
елемент даних, наприклад: див. нижче.
val newRd = myRdd.mapPartitions(partition => {
val connection = new DbConnection /*creates a db connection per partition*/
val newPartition = partition.map(record => {
readMatchingFromDB(record, connection)
}).toList // consumes the iterator, thus calls readMatchingFromDB
connection.close() // close dbconnection here
newPartition.iterator // create a new iterator
})
Q2. чи flatMap
поводиться як карта чи як mapPartitions
?
Так. дивіться, будь ласка, приклад 2 flatmap
.. її пояснення.
Q1. Яка різниця між RDD map
таmapPartitions
map
працює функція, що використовується на рівні елемента, в той час як
mapPartitions
виконує функцію на рівні розділу.
Приклад Сценарій : якщо в конкретномуRDD
розділіу нас є 100K елементів,тоді ми знімемо функцію, яку використовує трансформація відображення 100K разів, коли ми використовуємоmap
.
І навпаки, якщо ми використовуємо, mapPartitions
то ми будемо викликати певну функцію лише один раз, але ми передамо всі записи в 100 К і отримаємо всі відповіді за один виклик функції.
Збільшується продуктивність, оскільки map
працює над певною функцією стільки разів, особливо якщо функція робить що-небудь дороге, що не потрібно було б робити, якщо ми передамо всі елементи одразу (у випадку mappartitions
).
карта
Застосовує функцію перетворення до кожного елемента RDD і повертає результат як новий RDD.
Варіанти лістингу
визначити карту [U: ClassTag] (f: T => U): RDD [U]
Приклад:
val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
val b = a.map(_.length)
val c = a.zip(b)
c.collect
res0: Array[(String, Int)] = Array((dog,3), (salmon,6), (salmon,6), (rat,3), (elephant,8))
mapPartitions
Це спеціалізована карта, яка викликається лише один раз для кожного розділу. Весь вміст відповідних розділів доступний у вигляді послідовного потоку значень через вхідний аргумент (Iterarator [T]). Спеціальна функція повинна повертати ще один ітератор [U]. Об'єднані ітератори результатів автоматично перетворюються на новий RDD. Зверніть увагу, що кортежі (3,4) та (6,7) відсутні в наступному результаті через обраний нами розділ.
preservesPartitioning
вказує, чи зберігає функція введення частку, яка повинна бути, false
якщо це не пара RDD і функція введення не змінює клавіші.
Варіанти лістингу
def mapPartitions [U: ClassTag] (f: Ітератор [T] => Ітератор [U], зберігаєПереділення: Булева = хибна): RDD [U]
Приклад 1
val a = sc.parallelize(1 to 9, 3)
def myfunc[T](iter: Iterator[T]) : Iterator[(T, T)] = {
var res = List[(T, T)]()
var pre = iter.next
while (iter.hasNext)
{
val cur = iter.next;
res .::= (pre, cur)
pre = cur;
}
res.iterator
}
a.mapPartitions(myfunc).collect
res0: Array[(Int, Int)] = Array((2,3), (1,2), (5,6), (4,5), (8,9), (7,8))
Приклад 2
val x = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9,10), 3)
def myfunc(iter: Iterator[Int]) : Iterator[Int] = {
var res = List[Int]()
while (iter.hasNext) {
val cur = iter.next;
res = res ::: List.fill(scala.util.Random.nextInt(10))(cur)
}
res.iterator
}
x.mapPartitions(myfunc).collect
// some of the number are not outputted at all. This is because the random number generated for it is zero.
res8: Array[Int] = Array(1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 7, 7, 7, 9, 9, 10)
Вищеописану програму також можна записати за допомогою flatMap наступним чином.
Приклад 2 з використанням плоскої карти
val x = sc.parallelize(1 to 10, 3)
x.flatMap(List.fill(scala.util.Random.nextInt(10))(_)).collect
res1: Array[Int] = Array(1, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10)
Висновок:
mapPartitions
перетворення відбувається швидше, ніж map
оскільки воно викликає вашу функцію один раз / розділ, а не один раз / елемент ..
Подальше читання: foreach Vs foreachPartitions Коли користуватися Що?