<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: renanpyd</title>
    <description>The latest articles on DEV Community by renanpyd (@renanpyd).</description>
    <link>https://dev.to/renanpyd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F687401%2F52339473-a6e4-4292-b2f6-f7286506a222.jpeg</url>
      <title>DEV Community: renanpyd</title>
      <link>https://dev.to/renanpyd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/renanpyd"/>
    <language>en</language>
    <item>
      <title>Como aprendi Apache Spark — Parte 1: Por que o Spark mudou o processamento de Big Data</title>
      <dc:creator>renanpyd</dc:creator>
      <pubDate>Tue, 06 Oct 2026 06:26:49 +0000</pubDate>
      <link>https://dev.to/renanpyd/como-aprendi-apache-spark-parte-1-por-que-o-spark-mudou-o-processamento-de-big-data-51ei</link>
      <guid>https://dev.to/renanpyd/como-aprendi-apache-spark-parte-1-por-que-o-spark-mudou-o-processamento-de-big-data-51ei</guid>
      <description>&lt;p&gt;Hoje falamos naturalmente sobre Data Lakes, Lakehouses, processamento distribuído em cloud, Delta Lake, Apache Iceberg, pipelines em tempo real e plataformas como Databricks.&lt;/p&gt;

&lt;p&gt;Naquele período, porém, uma palavra dominava grande parte das discussões sobre processamento distribuído:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hadoop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mais especificamente, &lt;strong&gt;Hadoop MapReduce&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Entender esse contexto é importante porque Spark não surgiu simplesmente como "mais um framework de Big Data".&lt;/p&gt;

&lt;p&gt;Ele nasceu tentando resolver limitações reais do processamento distribuído da época.&lt;/p&gt;

&lt;p&gt;E foi justamente isso que chamou minha atenção.&lt;/p&gt;




&lt;h2&gt;
  
  
  Antes do Spark: o mundo era muito mais MapReduce
&lt;/h2&gt;

&lt;p&gt;Imagine que temos alguns terabytes de logs e precisamos descobrir quantas vezes cada palavra aparece nesses dados.&lt;/p&gt;

&lt;p&gt;Conceitualmente, podemos dividir o problema em duas etapas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAP
documento -&amp;gt; palavras -&amp;gt; (palavra, 1)

REDUCE
(palavra, 1), (palavra, 1), (palavra, 1)
                     ↓
              (palavra, 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa ideia é extremamente poderosa.&lt;/p&gt;

&lt;p&gt;Podemos dividir um dataset enorme entre diversas máquinas, processar partes dele paralelamente e depois combinar os resultados.&lt;/p&gt;

&lt;p&gt;O Hadoop transformou esse modelo em uma plataforma capaz de processar volumes enormes de dados utilizando clusters de máquinas.&lt;/p&gt;

&lt;p&gt;Mas existia um problema.&lt;/p&gt;

&lt;p&gt;Para determinados workloads, principalmente aqueles compostos por &lt;strong&gt;múltiplas etapas&lt;/strong&gt;, havia muita leitura e escrita de dados entre operações.&lt;/p&gt;

&lt;p&gt;Imagine um algoritmo com várias etapas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dados
  ↓
Job 1
  ↓
Disco
  ↓
Job 2
  ↓
Disco
  ↓
Job 3
  ↓
Resultado
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando comecei a estudar processamento distribuído, uma das primeiras coisas que percebi foi:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Em Big Data, mover dados frequentemente custa mais do que executar a própria transformação.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rede custa.&lt;/p&gt;

&lt;p&gt;Disco custa.&lt;/p&gt;

&lt;p&gt;Serialização custa.&lt;/p&gt;

&lt;p&gt;Shuffle custa.&lt;/p&gt;

&lt;p&gt;E essa observação continua extremamente relevante na Engenharia de Dados atual.&lt;/p&gt;




&lt;h1&gt;
  
  
  Então apareceu o Spark
&lt;/h1&gt;

&lt;p&gt;Apache Spark nasceu como um projeto de pesquisa no &lt;strong&gt;AMPLab da University of California, Berkeley&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Uma das ideias centrais era permitir que determinados conjuntos de dados fossem reutilizados eficientemente durante diferentes etapas de uma computação distribuída.&lt;/p&gt;

&lt;p&gt;Isso era especialmente interessante para workloads iterativos.&lt;/p&gt;

&lt;p&gt;Pense, por exemplo, em algoritmos que repetem operações sobre os mesmos dados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dataset
   ↓
Iteração 1
   ↓
Iteração 2
   ↓
Iteração 3
   ↓
Iteração 4
   ↓
Resultado
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Machine Learning é um ótimo exemplo.&lt;/p&gt;

&lt;p&gt;Muitos algoritmos precisam executar várias iterações sobre um mesmo conjunto de dados.&lt;/p&gt;

&lt;p&gt;Se cada etapa precisar reconstruir todo o estado intermediário utilizando armazenamento persistente, o custo pode crescer rapidamente.&lt;/p&gt;

&lt;p&gt;Spark propôs uma abstração muito interessante para esse problema.&lt;/p&gt;

&lt;p&gt;Os &lt;strong&gt;RDDs&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  RDD: Resilient Distributed Dataset
&lt;/h1&gt;

&lt;p&gt;RDD significa:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resilient Distributed Dataset&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em português, podemos pensar em algo como:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;conjunto de dados distribuído e resiliente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mas essa tradução não explica por que a ideia foi tão importante.&lt;/p&gt;

&lt;p&gt;Um RDD representa uma coleção de elementos distribuída pelas máquinas de um cluster e capaz de ser processada paralelamente.&lt;/p&gt;

&lt;p&gt;Visualmente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    RDD
                     │
          ┌──────────┼──────────┐
          │          │          │
          ▼          ▼          ▼
      Partição 1  Partição 2  Partição 3
          │          │          │
          ▼          ▼          ▼
      Executor A  Executor B  Executor C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso permite que diferentes partes do conjunto de dados sejam processadas simultaneamente.&lt;/p&gt;

&lt;p&gt;Mas a parte mais interessante está no &lt;strong&gt;Resilient&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Por que "Resilient"?
&lt;/h1&gt;

&lt;p&gt;Clusters falham.&lt;/p&gt;

&lt;p&gt;Máquinas podem parar.&lt;/p&gt;

&lt;p&gt;Executors podem morrer.&lt;/p&gt;

&lt;p&gt;Processos podem ser encerrados.&lt;/p&gt;

&lt;p&gt;Nós podem ficar indisponíveis.&lt;/p&gt;

&lt;p&gt;Uma plataforma de processamento distribuído precisa assumir que falhas vão acontecer.&lt;/p&gt;

&lt;p&gt;Spark consegue reconstruir partições perdidas utilizando informações sobre &lt;strong&gt;como aquele dataset foi produzido&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Essa cadeia de dependências é chamada de:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;lineage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Arquivo
   │
   ▼
textFile()
   │
   ▼
  RDD A
   │
   │ filter()
   ▼
  RDD B
   │
   │ map()
   ▼
  RDD C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spark conhece essa sequência de transformações.&lt;/p&gt;

&lt;p&gt;Se uma partição de &lt;code&gt;RDD C&lt;/code&gt; for perdida, ele não necessariamente precisa manter uma cópia completa de tudo.&lt;/p&gt;

&lt;p&gt;Em muitos casos, pode reconstruir a partição a partir do lineage.&lt;/p&gt;

&lt;p&gt;Essa ideia é fundamental para entender a arquitetura do Spark.&lt;/p&gt;




&lt;h1&gt;
  
  
  Um primeiro RDD em PySpark
&lt;/h1&gt;

&lt;p&gt;Vamos olhar um exemplo extremamente simples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;numeros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parallelize&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui criamos um RDD contendo cinco elementos.&lt;/p&gt;

&lt;p&gt;Agora podemos aplicar uma transformação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dobrados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numeros&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Intuitivamente, poderíamos imaginar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1, 2, 3, 4, 5]

       map(x * 2)

[2, 4, 6, 8, 10]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas existe um detalhe fundamental.&lt;/p&gt;

&lt;p&gt;Quando executamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dobrados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numeros&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spark &lt;strong&gt;não precisa calcular imediatamente o resultado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ele registra a transformação.&lt;/p&gt;

&lt;p&gt;Esse comportamento é chamado de:&lt;/p&gt;

&lt;h1&gt;
  
  
  Lazy Evaluation
&lt;/h1&gt;

&lt;p&gt;E esse é um dos conceitos mais importantes de toda a arquitetura Spark.&lt;/p&gt;




&lt;h1&gt;
  
  
  Spark não executa tudo imediatamente
&lt;/h1&gt;

&lt;p&gt;Considere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;numeros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parallelize&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;pares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numeros&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dobrados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos imaginar o plano:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parallelize
     │
     ▼
   filter
     │
     ▼
    map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Até aqui estamos descrevendo &lt;strong&gt;o que queremos fazer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Agora executamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resultado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dobrados&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultado&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resultado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[4, 8]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;collect()&lt;/code&gt; é uma &lt;strong&gt;ação&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;É nesse momento que Spark precisa efetivamente produzir o resultado.&lt;/p&gt;

&lt;p&gt;Essa separação nos leva a dois conceitos fundamentais.&lt;/p&gt;




&lt;h1&gt;
  
  
  Transformations vs Actions
&lt;/h1&gt;

&lt;p&gt;Grande parte da programação com Spark pode ser compreendida através dessa divisão.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transformations
&lt;/h2&gt;

&lt;p&gt;Transformations criam um novo dataset a partir de outro.&lt;/p&gt;

&lt;p&gt;Exemplos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Normalmente elas são avaliadas de forma &lt;strong&gt;lazy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Podemos imaginar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RDD
 │
 ├── filter()
 │
 ▼
RDD
 │
 ├── map()
 │
 ▼
RDD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Estamos construindo um plano de transformação.&lt;/p&gt;




&lt;h2&gt;
  
  
  Actions
&lt;/h2&gt;

&lt;p&gt;Actions solicitam algum resultado da computação.&lt;/p&gt;

&lt;p&gt;Exemplos clássicos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando uma Action é executada, Spark precisa avaliar as transformações necessárias para produzir o resultado.&lt;/p&gt;

&lt;p&gt;Por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resultado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parallelize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Temos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dados
  │
  ▼
filter()
  │
  ▼
map()
  │
  ▼
take(5)
  │
  ▼
Resultado
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse modelo permite ao Spark entender uma sequência de operações antes da execução.&lt;/p&gt;

&lt;p&gt;Mais tarde veremos como isso se relaciona com &lt;strong&gt;jobs, stages, tasks e DAGs&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  O clássico WordCount
&lt;/h1&gt;

&lt;p&gt;Nenhuma viagem pela história do processamento distribuído estaria completa sem ele.&lt;/p&gt;

&lt;p&gt;O famoso:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WordCount.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;linhas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dados.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linhas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;linha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;palavras&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;contagem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduceByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;resultado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contagem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceitualmente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Arquivo
   │
   ▼
Linhas
   │
   │ flatMap()
   ▼
Palavras
   │
   │ map()
   ▼
(palavra, 1)
   │
   │ reduceByKey()
   ▼
(palavra, quantidade)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse pequeno exemplo contém vários conceitos importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;leitura distribuída;&lt;/li&gt;
&lt;li&gt;transformação dos dados;&lt;/li&gt;
&lt;li&gt;criação de pares chave/valor;&lt;/li&gt;
&lt;li&gt;agregação;&lt;/li&gt;
&lt;li&gt;execução distribuída;&lt;/li&gt;
&lt;li&gt;movimentação de dados entre partições.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E existe uma palavra especialmente importante nessa lista:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;movimentação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porque &lt;code&gt;reduceByKey()&lt;/code&gt; pode exigir que registros com a mesma chave sejam reunidos.&lt;/p&gt;

&lt;p&gt;Isso nos leva a um dos assuntos mais importantes de performance em Spark:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;shuffle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Vamos dedicar um artigo específico a isso mais adiante.&lt;/p&gt;




&lt;h1&gt;
  
  
  Spark não é simplesmente "Hadoop mais rápido"
&lt;/h1&gt;

&lt;p&gt;Durante muito tempo tornou-se comum encontrar explicações como:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Spark é Hadoop em memória."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essa descrição é conveniente.&lt;/p&gt;

&lt;p&gt;Mas é incompleta.&lt;/p&gt;

&lt;p&gt;Spark não é apenas uma implementação mais rápida de MapReduce.&lt;/p&gt;

&lt;p&gt;Ele oferece um modelo de execução diferente e uma API capaz de representar pipelines de transformação muito mais naturalmente.&lt;/p&gt;

&lt;p&gt;Compare conceitualmente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MapReduce

Job
 ↓
Disco
 ↓
Job
 ↓
Disco
 ↓
Job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Spark

Transformação
     ↓
Transformação
     ↓
Transformação
     ↓
Action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa mudança no modelo de programação é tão importante quanto a questão de performance.&lt;/p&gt;




&lt;h1&gt;
  
  
  Spark também não mantém "tudo em memória"
&lt;/h1&gt;

&lt;p&gt;Outro erro que encontrei muitas vezes ao aprender Spark foi:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Spark coloca todos os dados na RAM."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Não.&lt;/p&gt;

&lt;p&gt;Spark &lt;strong&gt;pode&lt;/strong&gt; manter dados em memória quando isso é útil e possível, mas isso não significa que todo processamento acontece exclusivamente na memória.&lt;/p&gt;

&lt;p&gt;Dependendo da operação, configuração e volume de dados, Spark pode utilizar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memória;&lt;/li&gt;
&lt;li&gt;disco;&lt;/li&gt;
&lt;li&gt;rede;&lt;/li&gt;
&lt;li&gt;armazenamento externo;&lt;/li&gt;
&lt;li&gt;recomputação.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O modelo real é muito mais sofisticado.&lt;/p&gt;

&lt;p&gt;E entender isso evita várias decisões ruins de arquitetura.&lt;/p&gt;




&lt;h1&gt;
  
  
  O que continua relevante em 2026?
&lt;/h1&gt;

&lt;p&gt;Hoje, na maior parte dos pipelines modernos, provavelmente não começaremos escrevendo diretamente RDDs.&lt;/p&gt;

&lt;p&gt;É muito mais comum trabalharmos com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/dados/clientes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;e depois:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resultado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ativo&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;estado&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou utilizando SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;estado&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;quantidade&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clientes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ativo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;estado&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DataFrames e Spark SQL permitem que o Spark realize otimizações muito mais sofisticadas sobre as consultas.&lt;/p&gt;

&lt;p&gt;Então por que estudar RDD?&lt;/p&gt;

&lt;p&gt;Porque vários conceitos fundamentais continuam aparecendo por baixo das abstrações modernas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DataFrame / SQL
       │
       ▼
Plano lógico
       │
       ▼
Otimização
       │
       ▼
Plano físico
       │
       ▼
Stages
       │
       ▼
Tasks
       │
       ▼
Executors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando alguma coisa fica lenta, cara ou instável, conhecer apenas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;geralmente não é suficiente.&lt;/p&gt;

&lt;p&gt;Precisamos entender &lt;strong&gt;o que está acontecendo por baixo da API&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Uma lição que continua válida
&lt;/h1&gt;

&lt;p&gt;Uma das coisas mais importantes que aprendi estudando Spark foi que Engenharia de Dados distribuída não consiste apenas em escrever transformações.&lt;/p&gt;

&lt;p&gt;Precisamos pensar em:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dados
  ↓
Particionamento
  ↓
Transformações
  ↓
Dependências
  ↓
Shuffle
  ↓
Stages
  ↓
Tasks
  ↓
Executors
  ↓
CPU / Memória / Disco / Rede
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando começamos a enxergar um pipeline dessa forma, vários problemas deixam de parecer misteriosos.&lt;/p&gt;

&lt;p&gt;Um job lento deixa de ser simplesmente:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Spark está lento."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E passa a gerar perguntas melhores:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Houve shuffle?&lt;/p&gt;

&lt;p&gt;Existe data skew?&lt;/p&gt;

&lt;p&gt;As partições estão bem dimensionadas?&lt;/p&gt;

&lt;p&gt;Estamos movimentando dados desnecessariamente?&lt;/p&gt;

&lt;p&gt;Algum executor está sofrendo pressão de memória?&lt;/p&gt;

&lt;p&gt;O driver está recebendo dados demais?&lt;/p&gt;

&lt;p&gt;Estamos usando a abstração correta?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essas perguntas continuam extremamente atuais.&lt;/p&gt;




&lt;h1&gt;
  
  
  Daqui para frente
&lt;/h1&gt;

&lt;p&gt;Neste artigo vimos os fundamentos que me fizeram começar a olhar Spark de forma diferente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;o contexto do Hadoop MapReduce;&lt;/li&gt;
&lt;li&gt;processamento distribuído;&lt;/li&gt;
&lt;li&gt;RDDs;&lt;/li&gt;
&lt;li&gt;resiliência;&lt;/li&gt;
&lt;li&gt;lineage;&lt;/li&gt;
&lt;li&gt;transformations;&lt;/li&gt;
&lt;li&gt;actions;&lt;/li&gt;
&lt;li&gt;lazy evaluation;&lt;/li&gt;
&lt;li&gt;e a ideia de distribuir uma computação entre várias máquinas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora podemos entrar de verdade na arquitetura.&lt;/p&gt;

&lt;p&gt;No próximo artigo vamos responder uma pergunta fundamental:&lt;/p&gt;

&lt;h1&gt;
  
  
  Quando executo um programa Spark, quem realmente faz o trabalho?
&lt;/h1&gt;

&lt;p&gt;Vamos conhecer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Driver, SparkContext, Cluster Manager, Executors, Jobs, Stages e Tasks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E vamos montar o caminho completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Aplicação
    ↓
Driver
    ↓
Cluster Manager
    ↓
Executors
    ↓
Stages
    ↓
Tasks
    ↓
Dados
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entender esse fluxo muda completamente a maneira como diagnosticamos performance, memória e falhas em aplicações Spark.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Série: Como aprendi Apache Spark&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;← Parte 0: Como aprendi Apache Spark: revisitando uma jornada pela Engenharia de Dados&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Próximo:&lt;/strong&gt; Parte 2 — Por dentro da arquitetura do Apache Spark: Driver, Executors, Jobs, Stages e Tasks&lt;/p&gt;

</description>
      <category>spark</category>
      <category>dataengineering</category>
      <category>bigdata</category>
      <category>python</category>
    </item>
    <item>
      <title>Como aprendi Apache Spark: revisitando uma jornada pela Engenharia de Dados</title>
      <dc:creator>renanpyd</dc:creator>
      <pubDate>Tue, 06 Oct 2026 04:05:21 +0000</pubDate>
      <link>https://dev.to/renanpyd/como-aprendi-apache-spark-revisitando-uma-jornada-pela-engenharia-de-dados-l5g</link>
      <guid>https://dev.to/renanpyd/como-aprendi-apache-spark-revisitando-uma-jornada-pela-engenharia-de-dados-l5g</guid>
      <description>&lt;p&gt;Quando comecei a estudar Apache Spark, o ecossistema de Big Data era muito diferente do que conhecemos hoje.&lt;/p&gt;

&lt;p&gt;Hadoop e MapReduce dominavam boa parte das conversas sobre processamento distribuído. Trabalhar com grandes volumes de dados normalmente significava pensar em clusters, HDFS, processamento em lote e muitas operações de leitura e escrita em disco.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;&amp;gt; Foi nesse cenário que o Spark começou a chamar minha atenção.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;**E esta série nasce justamente de uma ideia: revisitar meus antigos materiais de estudo sobre Apache Spark e reconstruí-los com o olhar que tenho hoje como profissional de Engenharia de Dados e IA.&lt;/p&gt;

&lt;p&gt;Não quero simplesmente republicar anotações antigas.&lt;/p&gt;

&lt;p&gt;Quero entender o que envelheceu, o que continua extremamente relevante e, principalmente, quais conceitos atravessaram diferentes gerações do Spark e continuam presentes na engenharia de dados moderna.**&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Por que revisitar Apache Spark?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Algumas tecnologias mudam tanto que estudar suas versões antigas parece arqueologia.&lt;/p&gt;

&lt;p&gt;Spark é diferente.&lt;/p&gt;

&lt;p&gt;As APIs evoluíram. O ecossistema mudou. RDDs deixaram de ser a principal abstração utilizada em muitas aplicações. DataFrames, Spark SQL, Catalyst, Structured Streaming e diversas otimizações passaram a ocupar um papel central.&lt;/p&gt;

&lt;p&gt;Mas vários fundamentos continuam essenciais.&lt;/p&gt;

&lt;p&gt;Conceitos como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;processamento distribuído;&lt;/li&gt;
&lt;li&gt;particionamento;&lt;/li&gt;
&lt;li&gt;transformações e ações;&lt;/li&gt;
&lt;li&gt;lazy evaluation;&lt;/li&gt;
&lt;li&gt;lineage;&lt;/li&gt;
&lt;li&gt;DAGs;&lt;/li&gt;
&lt;li&gt;shuffle;&lt;/li&gt;
&lt;li&gt;execução distribuída;&lt;/li&gt;
&lt;li&gt;tolerância a falhas;&lt;/li&gt;
&lt;li&gt;serialização;&lt;/li&gt;
&lt;li&gt;gerenciamento de memória;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;continuam ajudando a explicar por que um job Spark funciona — ou por que ele fica terrivelmente lento.&lt;/p&gt;

&lt;p&gt;E é justamente aí que estudar os fundamentos continua tendo valor.&lt;/p&gt;

&lt;p&gt;Um aviso importante sobre esta série:&lt;br&gt;
    &lt;em&gt;&lt;strong&gt;Esta não será simplesmente uma reprodução dos meus materiais antigos.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Alguns deles utilizam versões antigas do Spark e APIs que foram substituídas ou evoluíram significativamente.&lt;/p&gt;

&lt;p&gt;Sempre que isso acontecer, vou separar três coisas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Como funcionava&lt;/li&gt;
&lt;li&gt;O comportamento ou API utilizada naquele momento da evolução do Spark.&lt;/li&gt;
&lt;li&gt;O conceito por trás&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A ideia de Engenharia de Dados que continua importante independentemente da versão.&lt;/p&gt;

&lt;p&gt;Como pensamos nisso hoje: A maneira moderna de resolver ou compreender o mesmo problema. Isso significa que eventualmente veremos código antigo. E isso é proposital.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;&amp;gt; Código legado também conta a história de uma tecnologia.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;De Hadoop MapReduce ao Spark&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uma das ideias que tornou Spark especialmente interessante foi reduzir a dependência de sucessivas operações de leitura e escrita em disco durante determinados tipos de processamento.&lt;/p&gt;

&lt;p&gt;Em workloads iterativos e análises interativas, isso fazia uma diferença enorme.&lt;/p&gt;

&lt;p&gt;Mas seria simplista resumir Spark a:&lt;/p&gt;

&lt;p&gt;"Spark é rápido porque processa tudo em memória."&lt;/p&gt;

&lt;p&gt;A arquitetura é muito mais interessante.&lt;/p&gt;

&lt;p&gt;Spark introduziu um modelo de computação distribuída baseado em abstrações capazes de manter informações sobre como os dados foram produzidos.&lt;/p&gt;

&lt;p&gt;Uma dessas abstrações tornou-se fundamental para entender os primeiros anos do projeto:&lt;/p&gt;

&lt;p&gt;RDD — Resilient Distributed Dataset&lt;/p&gt;

&lt;p&gt;Um RDD representa uma coleção distribuída de dados que pode ser processada paralelamente.&lt;/p&gt;

&lt;p&gt;Mas a parte realmente interessante não é apenas o fato de os dados estarem distribuídos.&lt;/p&gt;

&lt;p&gt;É a combinação de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;particionamento;&lt;/li&gt;
&lt;li&gt;imutabilidade;&lt;/li&gt;
&lt;li&gt;operações funcionais;&lt;/li&gt;
&lt;li&gt;execução lazy;&lt;/li&gt;
&lt;li&gt;lineage;&lt;/li&gt;
&lt;li&gt;recomputação;&lt;/li&gt;
&lt;li&gt;tolerância a falhas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essas ideias ajudaram a criar um modelo poderoso para computação distribuída.&lt;/p&gt;

&lt;p&gt;Em 2012, o trabalho sobre Resilient Distributed Datasets recebeu o prêmio de melhor artigo no NSDI.&lt;/p&gt;

&lt;p&gt;Anos depois, muitas das abstrações de mais alto nível utilizadas no Spark seriam construídas sobre fundamentos estabelecidos nesse período.&lt;/p&gt;

&lt;p&gt;E então vieram DataFrames, Spark SQL e muito mais&lt;/p&gt;

&lt;p&gt;A história não parou nos RDDs.&lt;/p&gt;

&lt;p&gt;O ecossistema evoluiu.&lt;/p&gt;

&lt;p&gt;Spark SQL tornou o processamento de dados estruturados muito mais acessível.&lt;/p&gt;

&lt;p&gt;DataFrames elevaram o nível de abstração.&lt;/p&gt;

&lt;p&gt;O Catalyst Optimizer passou a desempenhar um papel fundamental na otimização de consultas.&lt;/p&gt;

&lt;p&gt;O projeto Tungsten trouxe importantes avanços na execução e no gerenciamento de memória.&lt;/p&gt;

&lt;p&gt;Structured Streaming aproximou processamento batch e streaming sob um modelo mais uniforme.&lt;/p&gt;

&lt;p&gt;E Spark tornou-se uma das principais tecnologias da Engenharia de Dados moderna.&lt;/p&gt;

&lt;p&gt;É essa evolução que quero explorar ao longo desta série.&lt;/p&gt;

&lt;p&gt;O que vamos estudar&lt;/p&gt;

&lt;p&gt;Nos próximos artigos vamos passar por assuntos como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;arquitetura do Apache Spark;&lt;/li&gt;
&lt;li&gt;RDDs;&lt;/li&gt;
&lt;li&gt;transformations e actions;&lt;/li&gt;
&lt;li&gt;lazy evaluation;&lt;/li&gt;
&lt;li&gt;SparkContext;&lt;/li&gt;
&lt;li&gt;execução distribuída;&lt;/li&gt;
&lt;li&gt;Client Mode e Cluster Mode;&lt;/li&gt;
&lt;li&gt;particionamento;&lt;/li&gt;
&lt;li&gt;joins e shuffles;&lt;/li&gt;
&lt;li&gt;accumulators;&lt;/li&gt;
&lt;li&gt;broadcast variables;&lt;/li&gt;
&lt;li&gt;JSON;&lt;/li&gt;
&lt;li&gt;DataFrames;&lt;/li&gt;
&lt;li&gt;Spark SQL;&lt;/li&gt;
&lt;li&gt;schemas;&lt;/li&gt;
&lt;li&gt;fontes de dados;&lt;/li&gt;
&lt;li&gt;window functions;&lt;/li&gt;
&lt;li&gt;Spark Streaming;&lt;/li&gt;
&lt;li&gt;testes;&lt;/li&gt;
&lt;li&gt;gerenciamento de memória;&lt;/li&gt;
&lt;li&gt;memoryOverhead;&lt;/li&gt;
&lt;li&gt;otimização e troubleshooting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Também vamos comparar algumas APIs históricas com a forma como resolveríamos os mesmos problemas atualmente.&lt;/p&gt;

&lt;p&gt;O objetivo não é decorar Spark&lt;/p&gt;

&lt;p&gt;Depois de trabalhar com Engenharia de Dados por algum tempo, uma coisa fica cada vez mais clara:&lt;/p&gt;

&lt;p&gt;saber escrever código Spark não significa necessariamente saber Spark.&lt;/p&gt;

&lt;p&gt;É relativamente fácil escrever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;O mais importante é conseguir perguntar:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quantas partições existem?&lt;/li&gt;
&lt;li&gt;Haverá shuffle?&lt;/li&gt;
&lt;li&gt;Qual será o volume movimentado pela rede?&lt;/li&gt;
&lt;li&gt;Existe data skew?&lt;/li&gt;
&lt;li&gt;Como o plano será executado?&lt;/li&gt;
&lt;li&gt;O dataset cabe adequadamente nas partições?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O problema está no driver ou nos executors?&lt;/p&gt;

&lt;p&gt;O gargalo é CPU, memória, disco ou rede?&lt;/p&gt;

&lt;p&gt;Essa transformação realmente precisa existir?&lt;/p&gt;

&lt;p&gt;É esse tipo de raciocínio que transforma conhecimento de uma API em conhecimento de Engenharia de Dados distribuída.&lt;/p&gt;

&lt;p&gt;Próximo artigo&lt;/p&gt;

&lt;p&gt;Na Parte 1, vamos começar pelos fundamentos:&lt;/p&gt;

&lt;p&gt;O que é Apache Spark e por que sua arquitetura foi tão importante?&lt;/p&gt;

&lt;p&gt;Vamos entender:&lt;/p&gt;

&lt;p&gt;o problema que Spark buscava resolver;&lt;/p&gt;

&lt;p&gt;a relação histórica com Hadoop;&lt;/p&gt;

&lt;p&gt;o papel dos RDDs;&lt;/p&gt;

&lt;p&gt;processamento em memória;&lt;/p&gt;

&lt;p&gt;tolerância a falhas;&lt;/p&gt;

&lt;p&gt;e como essas decisões influenciaram o Spark que usamos atualmente.&lt;/p&gt;

&lt;p&gt;Esta série será uma viagem pelas versões do Spark, mas principalmente pelos conceitos de Engenharia de Dados que sobreviveram a elas.&lt;/p&gt;

&lt;p&gt;Nos vemos na Parte 1.&lt;/p&gt;

</description>
      <category>spark</category>
      <category>bigdata</category>
      <category>dataengineering</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
