Spark - repartição () vs coalescência ()


254

De acordo com o Learning Spark

Lembre-se de que o reparticionamento dos dados é uma operação bastante cara. O Spark também possui uma versão otimizada do repartition()chamado coalesce()que permite evitar a movimentação de dados, mas apenas se você estiver diminuindo o número de partições RDD.

Uma diferença que recebo é que, com repartition()o número de partições, é possível aumentar / diminuir, mas com coalesce()o número de partições, só é possível diminuir.

Se as partições estão espalhadas por várias máquinas e coalesce()são executadas, como evitar a movimentação de dados?

Respostas:


354

Evita uma reprodução aleatória completa . Se for sabido que o número está diminuindo, o executor poderá manter os dados com segurança no número mínimo de partições, apenas movendo os dados dos nós extras para os nós que mantivemos.

Então, seria algo como isto:

Node 1 = 1,2,3
Node 2 = 4,5,6
Node 3 = 7,8,9
Node 4 = 10,11,12

Em seguida, coalescedesça para 2 partições:

Node 1 = 1,2,3 + (10,11,12)
Node 3 = 7,8,9 + (4,5,6)

Observe que o Nó 1 e o Nó 3 não exigiram a movimentação dos dados originais.


115
Obrigado pela resposta. A documentação deveria ter dito melhor em minimize data movementvez de avoiding data movement.
Praveen Sripati

12
Existe algum caso em que repartitiondeve ser usado em vez de coalesce?
Niemand

21
@ Niemand Acho que a documentação atual cobre isso muito bem: github.com/apache/spark/blob/… Lembre-se de que tudo o que repartitionfaz é chamar coalescecom o shuffleparâmetro definido como true. Deixe-me saber se isso ajuda.
perfil completo de Justin Pihony

2
É possível reduzir o número de arquivos de partição existentes? Não tenho hdfs, mas problema com muitos arquivos.

2
a repartição será estatisticamente mais lenta, pois não sabe que está encolhendo ... embora talvez eles possam otimizar isso. Internamente ele só chama coalesce com uma shuffle = truebandeira
Justin Pihony

171

A resposta de Justin é incrível e essa resposta entra em mais profundidade.

O repartitionalgoritmo faz uma shuffle completa e cria novas partições com dados distribuídos uniformemente. Vamos criar um DataFrame com os números de 1 a 12.

val x = (1 to 12).toList
val numbersDf = x.toDF("number")

numbersDf contém 4 partições na minha máquina.

numbersDf.rdd.partitions.size // => 4

Aqui está como os dados são divididos nas partições:

Partition 00000: 1, 2, 3
Partition 00001: 4, 5, 6
Partition 00002: 7, 8, 9
Partition 00003: 10, 11, 12

Vamos fazer um shuffle completo com o repartitionmétodo e obter esses dados em dois nós.

val numbersDfR = numbersDf.repartition(2)

Aqui está como os numbersDfRdados são particionados na minha máquina:

Partition A: 1, 3, 4, 6, 7, 9, 10, 12
Partition B: 2, 5, 8, 11

O repartitionmétodo cria novas partições e distribui uniformemente os dados nas novas partições (a distribuição de dados é mais uniforme para conjuntos de dados maiores).

Diferença entre coalesceerepartition

coalesceusa partições existentes para minimizar a quantidade de dados que são embaralhados. repartitioncria novas partições e faz uma reprodução aleatória completa. coalesceresulta em partições com diferentes quantidades de dados (às vezes partições com tamanhos muito diferentes) e repartitionresulta em partições de tamanho aproximadamente igual.

É coalesceou repartitionmais rápido?

coalescepode executar mais rápido que repartition, mas partições de tamanhos desiguais geralmente são mais lentas para trabalhar com partições de tamanhos iguais. Você geralmente precisará reparticionar conjuntos de dados após filtrar um grande conjunto de dados. Descobri repartitionser mais rápido no geral, porque o Spark foi desenvolvido para funcionar com partições de tamanhos iguais.

NB: Curiosamente, observei que a repartição pode aumentar o tamanho dos dados no disco . Certifique-se de executar testes ao usar a repartição / coalescência em grandes conjuntos de dados.

Leia esta postagem no blog se desejar obter mais detalhes.

Quando você usar coalescência e reparticionamento na prática


8
Ótima resposta @Powers, mas os dados da Partição A e B não estão distorcidos? Como é distribuído uniformemente?
precisa saber é o seguinte

Além disso, qual é a melhor maneira de obter o tamanho da partição sem obter erro OOM. eu usordd.glom().map(len).collect() mas dá muitos erros de OOM.
precisa saber é o seguinte

8
@anwartheravian - A Partição A e a Partição B são de tamanhos diferentes porque o repartitionalgoritmo não distribui dados igualmente para conjuntos de dados muito pequenos. Eu costumava repartitionorganizar 5 milhões de registros em 13 partições e cada arquivo tinha entre 89,3 MB e 89,6 MB - isso é quase igual!
Poderes

1
@Power parece melhor responder com detalhes.
Verde

1
Isso explica a diferença muito melhor. Obrigado!
Abhi

22

Um ponto adicional a ser observado aqui é que, como o princípio básico do Spark RDD é a imutabilidade. A repartição ou coalescência criará um novo RDD. O RDD base continuará a existir com o número original de partições. Caso o caso de uso exija a persistência do RDD no cache, o mesmo deverá ser feito no recém-criado RDD.

scala> pairMrkt.repartition(10)
res16: org.apache.spark.rdd.RDD[(String, Array[String])] =MapPartitionsRDD[11] at repartition at <console>:26

scala> res16.partitions.length
res17: Int = 10

scala>  pairMrkt.partitions.length
res20: Int = 2

Agradável! isso é crítico e, pelo menos para esse experiente especialista em escala, não é óbvio - ou seja, nem a repartição nem a tentativa de coalescência de modificar os dados, exatamente como eles são distribuídos pelos nós
doug

1
@Harikrishnan, então, se eu entendi as outras respostas corretamente, de acordo com elas, no caso de coalescência, o Spark usa partições existentes; no entanto, como o RDD é imutável, você pode descrever como a Coalesce faz uso de partições existentes? De acordo com meu entendimento, pensei que o Spark anexasse novas partições às partições existentes em coalescência.
Explorer

Mas se o RDD "antigo" não for mais usado, como é conhecido pelo gráfico de execução, ele será apagado da memória se não persistir, não é?
Markus

15

repartition - é recomendável usá-lo enquanto aumenta o número de partições, pois envolve a baralhamento de todos os dados.

coalesce- é recomendável usá-lo enquanto reduz o número de partições. Por exemplo, se você possui 3 partições e deseja reduzi-las para 2, coalesceos dados da 3ª partição serão movidos para as partições 1 e 2. As partições 1 e 2 permanecerão no mesmo contêiner. Por outro lado, repartitionembaralha os dados em todas as partições, portanto, o uso da rede entre os executores será alto e afetará o desempenho.

coalesceexecuta melhor do que repartitionao reduzir o número de partições.


Explicação útil.
Narendra Maru

11

O que se segue dos códigos e documentos de código coalesce(n)é o mesmo coalesce(n, shuffle = false)e repartition(n)o mesmo quecoalesce(n, shuffle = true)

Assim, ambos coalescee repartitionpodem ser usados ​​para aumentar o número de partições

Com shuffle = true, você pode realmente unir-se a um número maior de partições. Isso é útil se você tiver um pequeno número de partições, digamos 100, potencialmente com algumas partições sendo anormalmente grandes.

Outra observação importante a ser destacada é que, se você diminuir drasticamente o número de partições, deverá considerar usar a versão aleatória de coalesce(o mesmo repartitionnesse caso). Isso permitirá que seus cálculos sejam executados em paralelo nas partições pai (várias tarefas).

No entanto, se você estiver fazendo uma coalizão drástica, por exemplo, para numPartitions = 1, isso poderá resultar em seu cálculo em menos nós do que você deseja (por exemplo, um nó no caso de numPartitions = 1). Para evitar isso, você pode passar shuffle = true. Isso adicionará uma etapa de reprodução aleatória, mas significa que as partições upstream atuais serão executadas em paralelo (independentemente do particionamento atual).

Consulte também a resposta relacionada aqui


10

Todas as respostas estão adicionando um grande conhecimento a essa pergunta muito frequente.

Então, seguindo a tradição da linha do tempo dessa pergunta, aqui estão meus 2 centavos.

Eu achei a repartição mais rápida que unir , em casos muito específicos.

No meu aplicativo, quando o número de arquivos que estimamos é menor que o limite certo, a repartição funciona mais rapidamente.

Aqui está o que eu quero dizer

if(numFiles > 20)
    df.coalesce(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)
else
    df.repartition(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)

No snippet acima, se meus arquivos tivessem menos de 20 anos, a coalescência levaria uma eternidade para terminar, enquanto a repartição era muito mais rápida e, portanto, o código acima.

Obviamente, esse número (20) dependerá do número de trabalhadores e da quantidade de dados.

Espero que ajude.


6

Repartição : embaralhe os dados em um NOVO número de partições.

Por exemplo. O quadro de dados inicial é particionado em 200 partições.

df.repartition(500): Os dados serão embaralhados de 200 partições para novas 500 partições.

Agrupar : embaralhe os dados para o número existente de partições.

df.coalesce(5): Os dados serão embaralhados das 195 partições restantes para as 5 partições existentes.


3

Gostaria de acrescentar à resposta de Justin e Power que -

repartitionirá ignorar as partições existentes e criar novas. Então você pode usá-lo para corrigir a inclinação dos dados. Você pode mencionar chaves de partição para definir a distribuição. A inclinação dos dados é um dos maiores problemas no espaço de problemas do 'big data'.

coalescefuncionará com as partições existentes e embaralhará um subconjunto delas. Ele não pode corrigir tanto a inclinação dos dados quanto o repartitionfaz. Portanto, mesmo que seja menos caro, pode não ser o que você precisa.


3

A todas as ótimas respostas, gostaria de acrescentar que essa repartitioné a melhor opção para tirar proveito da paralelização de dados. Enquantocoalesce uma opção barata para reduzir as partições, é muito útil ao gravar dados no HDFS ou em algum outro coletor para aproveitar grandes gravações.

Eu achei isso útil ao escrever dados no formato parquet para obter o máximo proveito.


2

Para alguém que teve problemas ao gerar um único arquivo csv do PySpark (AWS EMR) como saída e salvá-lo no s3, o uso da repartição ajudou. A razão é que a coalescência não pode fazer um baralhamento completo, mas a repartição pode. Essencialmente, você pode aumentar ou diminuir o número de partições usando a repartição, mas só pode diminuir o número de partições (mas não 1) usando a coalescência. Aqui está o código para quem está tentando escrever um csv do AWS EMR para o s3:

df.repartition(1).write.format('csv')\
.option("path", "s3a://my.bucket.name/location")\
.save(header = 'true')

0

De uma maneira simples COALESCE: - é apenas para diminui o número de partições, sem embaralhamento de dados, apenas comprime as partições

REPARTIÇÃO: - é para aumentar e diminuir o número de partições, mas o embaralhamento ocorre

Exemplo:-

val rdd = sc.textFile("path",7)
rdd.repartition(10)
rdd.repartition(2)

Ambos funcionam bem

Mas geralmente fazemos essas duas coisas quando precisamos ver a saída em um cluster, seguimos com isso.


9
Também haverá movimentação de dados com o Coalese.
sun_dare

0

Mas você também deve se certificar de que os dados que estão chegando aos nós de coalescência devem estar altamente configurados, se você estiver lidando com dados enormes. Como todos os dados serão carregados nesses nós, pode levar à exceção da memória. Embora a reparação seja cara, prefiro usá-la. Uma vez que embaralha e distribui os dados igualmente.

Seja prudente ao escolher entre coalescer e reparticionar.


0

O repartitionalgoritmo faz uma mistura completa dos dados e cria partições de tamanho igual. coalescecombina partições existentes para evitar uma reprodução aleatória completa.

O Coalesce funciona bem para obter um RDD com muitas partições e combinar partições em um único nó de trabalho para produzir um RDD final com menos partições.

Repartitionreorganizará os dados em seu RDD para produzir o número final de partições solicitadas. O particionamento dos DataFrames parece um detalhe de implementação de baixo nível que deve ser gerenciado pela estrutura, mas não é. Ao filtrar DataFrames grandes em outros menores, você quase sempre deve reparticionar os dados. Você provavelmente estará filtrando DataFrames grandes em outros menores com frequência, para se acostumar ao reparticionamento.

Leia esta postagem no blog se desejar obter mais detalhes.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.