์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ์ž๋ฐ” ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ Apache Spark๋ฅผ ํ™œ์šฉํ•œ ๋น…๋ฐ์ดํ„ฐ ๋ถ„์„ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๐Ÿš€ ์ž๋ฐ” ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ Apache Spark๋ฅผ ํ™œ์šฉํ•œ ๋น…๋ฐ์ดํ„ฐ ๋ถ„์„ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ํ˜„๋Œ€์  ์ ‘๊ทผ๋ฒ•

๐ŸŽฏ ๋น…๋ฐ์ดํ„ฐ ์‹œ๋Œ€, ์™œ Spark์ธ๊ฐ€?

์•ˆ๋…•! ์˜ค๋Š˜์€ ์ž๋ฐ” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๊ผญ ์•Œ์•„์•ผ ํ•  Apache Spark์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค๊ณ  ํ•ด. ๐Ÿ˜Š

์š”์ฆ˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋งŽ์•„์กŒ๋Š”์ง€ ์•Œ์•„? ๋งค์ผ 2.5ํ€ธํ‹ธ๋ฆฌ์–ธ ๋ฐ”์ดํŠธ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ์žˆ์–ด. ์ด๊ฒŒ ์–ผ๋งˆ๋‚˜ ํฐ ์ˆซ์ž๋ƒ๋ฉด, 1 ๋’ค์— 0์ด 18๊ฐœ ๋ถ™๋Š” ์–ด๋งˆ์–ด๋งˆํ•œ ์–‘์ด์•ผ! ๐Ÿคฏ

์ „ํ†ต์ ์ธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์œผ๋กœ๋Š” ์ด๋Ÿฐ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๊ธฐ๊ฐ€ ์ •๋ง ํž˜๋“ค์–ด. ๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒŒ ๋ฐ”๋กœ Apache Spark์•ผ. Spark๋Š” 2009๋…„ UC ๋ฒ„ํด๋ฆฌ์˜ AMPLab์—์„œ ์‹œ์ž‘๋œ ํ”„๋กœ์ ํŠธ๋กœ, ํ˜„์žฌ๋Š” Apache ์žฌ๋‹จ์˜ ์ตœ์ƒ์œ„ ํ”„๋กœ์ ํŠธ๋กœ ์ž๋ฆฌ์žก์•˜์–ด.

๐Ÿ“Š Spark์˜ ํ•ต์‹ฌ ํŠน์ง• โ€ข ์†๋„: ๋ฉ”๋ชจ๋ฆฌ ๋‚ด ์ฒ˜๋ฆฌ๋กœ Hadoop MapReduce๋ณด๋‹ค ์ตœ๋Œ€ 100๋ฐฐ ๋น ๋ฆ„
โ€ข ๋ฒ”์šฉ์„ฑ: ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ, ์ŠคํŠธ๋ฆฌ๋ฐ, ๋จธ์‹ ๋Ÿฌ๋‹, ๊ทธ๋ž˜ํ”„ ์ฒ˜๋ฆฌ ๋ชจ๋‘ ๊ฐ€๋Šฅ
โ€ข ๋‹ค์–‘ํ•œ ์–ธ์–ด ์ง€์›: Java, Scala, Python, R ๋ชจ๋‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
โ€ข ํ™•์žฅ์„ฑ: ๋‹จ์ผ ๋…ธ๋“œ๋ถ€ํ„ฐ ์ˆ˜์ฒœ ๊ฐœ ๋…ธ๋“œ๊นŒ์ง€ ํ™•์žฅ ๊ฐ€๋Šฅ
Java Application Apache Spark Big Data Analysis Data Processing Pipeline Fast Processing Distributed Scalable

๐Ÿ’ก Apache Spark์˜ ์•„ํ‚คํ…์ฒ˜ ์ดํ•ดํ•˜๊ธฐ

Spark๋ฅผ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € ๊ทธ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•ด์•ผ ํ•ด. ๋งˆ์น˜ ์ž๋™์ฐจ๋ฅผ ์šด์ „ํ•˜๊ธฐ ์ „์— ์—”์ง„์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ๋ฉด ๋” ์ž˜ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์•ผ! ๐Ÿš—

Spark๋Š” ๋งˆ์Šคํ„ฐ-์›Œ์ปค ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‚ฌ์šฉํ•ด. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ํ•œ ๋ช…์˜ ๊ฐ๋…(Driver)์ด ์—ฌ๋Ÿฌ ๋ช…์˜ ์ž‘์—…์ž(Executor)์—๊ฒŒ ์ผ์„ ๋ถ„๋ฐฐํ•˜๋Š” ๊ตฌ์กฐ์•ผ.

๐Ÿ—๏ธ Spark ์•„ํ‚คํ…์ฒ˜์˜ ํ•ต์‹ฌ ๊ตฌ์„ฑ์š”์†Œ 1. Driver Program (๋“œ๋ผ์ด๋ฒ„ ํ”„๋กœ๊ทธ๋žจ)
โ€ข ์‚ฌ์šฉ์ž์˜ main() ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋Š” ํ”„๋กœ์„ธ์Šค
โ€ข SparkContext๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ „์ฒด ์ž‘์—…์„ ์กฐ์œจ
โ€ข ์ž‘์—…์„ Task๋กœ ๋ถ„ํ• ํ•˜๊ณ  Executor์— ํ• ๋‹น

2. Cluster Manager (ํด๋Ÿฌ์Šคํ„ฐ ๋งค๋‹ˆ์ €)
โ€ข ํด๋Ÿฌ์Šคํ„ฐ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ด€๋ฆฌ
โ€ข Standalone, YARN, Mesos, Kubernetes ๋“ฑ ์ง€์›

3. Executor (์‹คํ–‰์ž)
โ€ข ์‹ค์ œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์›Œ์ปค ํ”„๋กœ์„ธ์Šค
โ€ข ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๋‚˜ ๋””์Šคํฌ์— ์บ์‹ฑ
โ€ข Task๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ Driver์— ๋ฐ˜ํ™˜

๐Ÿ”„ Spark์˜ ๋ฐ์ดํ„ฐ ์ถ”์ƒํ™”: RDD, DataFrame, Dataset

Spark๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์„ธ ๊ฐ€์ง€ ์ฃผ์š” ์ถ”์ƒํ™” ๊ฐœ๋…์„ ์ œ๊ณตํ•ด. ๊ฐ๊ฐ์˜ ํŠน์ง•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ“š

๐Ÿ’Ž RDD (Resilient Distributed Dataset) Spark์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์•ผ. "ํƒ„๋ ฅ์  ๋ถ„์‚ฐ ๋ฐ์ดํ„ฐ์…‹"์ด๋ผ๋Š” ๋œป์ธ๋ฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์ด ์žˆ์–ด:

โ€ข ๋ถˆ๋ณ€์„ฑ(Immutable): ํ•œ ๋ฒˆ ์ƒ์„ฑ๋˜๋ฉด ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด
โ€ข ๋ถ„์‚ฐ: ์—ฌ๋Ÿฌ ๋…ธ๋“œ์— ๊ฑธ์ณ ๋ถ„์‚ฐ ์ €์žฅ
โ€ข ๋ณต์›๋ ฅ: ์žฅ์•  ๋ฐœ์ƒ ์‹œ ์ž๋™์œผ๋กœ ๋ณต๊ตฌ ๊ฐ€๋Šฅ
โ€ข ์ง€์—ฐ ํ‰๊ฐ€(Lazy Evaluation): ์‹ค์ œ ์•ก์…˜์ด ํ˜ธ์ถœ๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰ ์•ˆ ํ•จ
โšก DataFrame RDD์— ์Šคํ‚ค๋งˆ(๊ตฌ์กฐ)๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฐœ๋…์ด์•ผ. ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ”๊ณผ ๋น„์Šทํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ:

โ€ข ์ปฌ๋Ÿผ๋ช…๊ณผ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์ •์˜๋จ
โ€ข SQL ์ฟผ๋ฆฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
โ€ข Catalyst ์˜ตํ‹ฐ๋งˆ์ด์ €๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”
โ€ข Python์˜ Pandas DataFrame๊ณผ ์œ ์‚ฌํ•œ API
๐ŸŽฏ Dataset DataFrame์˜ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ๋”ํ•œ ์ตœ์‹  API์•ผ (Scala์™€ Java์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ):

โ€ข ์ปดํŒŒ์ผ ํƒ€์ž„์— ํƒ€์ž… ์ฒดํฌ
โ€ข ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์Šคํƒ€์ผ
โ€ข DataFrame์˜ ์ตœ์ ํ™” + RDD์˜ ํƒ€์ž… ์•ˆ์ •์„ฑ

โš™๏ธ Java์—์„œ Spark ์‹œ์ž‘ํ•˜๊ธฐ

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ Java๋กœ Spark๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž! ๐ŸŽฎ

๋จผ์ € ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•ด์•ผ ํ•ด. Maven์ด๋‚˜ Gradle์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘์ด์•ผ.

๐Ÿ“ฆ Maven ์˜์กด์„ฑ ์„ค์ •

<dependencies>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.12</artifactId>
        <version>3.5.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-sql_2.12</artifactId>
        <version>3.5.0</version>
    </dependency>
</dependencies>
๐Ÿ” ๋ฒ„์ „ ์„ ํƒ ํŒ โ€ข _2.12๋Š” Scala ๋ฒ„์ „์„ ์˜๋ฏธํ•ด (Spark๋Š” Scala๋กœ ์ž‘์„ฑ๋จ)
โ€ข Java 8 ์ด์ƒ ํ•„์š” (Java 11 ๋˜๋Š” 17 ๊ถŒ์žฅ)
โ€ข ์ตœ์‹  ์•ˆ์ • ๋ฒ„์ „ ์‚ฌ์šฉ์„ ๊ถŒ์žฅ (ํ˜„์žฌ 3.5.x ์‹œ๋ฆฌ์ฆˆ)

๐Ÿš€ ์ฒซ ๋ฒˆ์งธ Spark ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ "Word Count" ์˜ˆ์ œ๋กœ ์‹œ์ž‘ํ•ด๋ณด์ž. ์ด๊ฑด ๋น…๋ฐ์ดํ„ฐ์˜ "Hello World"๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ‘‹

import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.SparkConf;
import scala.Tuple2;
import java.util.Arrays;

public class WordCountExample {
    public static void main(String[] args) {
        // Spark ์„ค์ • ์ƒ์„ฑ
        SparkConf conf = new SparkConf()
            .setAppName("Word Count Example")
            .setMaster("local[*]"); // ๋กœ์ปฌ ๋ชจ๋“œ๋กœ ์‹คํ–‰
        
        // SparkContext ์ƒ์„ฑ
        JavaSparkContext sc = new JavaSparkContext(conf);
        
        // ํ…์ŠคํŠธ ํŒŒ์ผ ์ฝ๊ธฐ
        JavaRDD<String> lines = sc.textFile("input.txt");
        
        // ๋‹จ์–ด ๋ถ„๋ฆฌ ๋ฐ ์นด์šดํŒ…
        JavaRDD<String> words = lines.flatMap(
            line -> Arrays.asList(line.split(" ")).iterator()
        );
        
        // ๊ฐ ๋‹จ์–ด๋ฅผ (๋‹จ์–ด, 1) ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜
        JavaPairRDD<String, Integer> pairs = words.mapToPair(
            word -> new Tuple2<>(word, 1)
        );
        
        // ๊ฐ™์€ ํ‚ค(๋‹จ์–ด)๋ผ๋ฆฌ ํ•ฉ์‚ฐ
        JavaPairRDD<String, Integer> wordCounts = pairs.reduceByKey(
            (a, b) -> a + b
        );
        
        // ๊ฒฐ๊ณผ ์ถœ๋ ฅ
        wordCounts.foreach(tuple -> {
            System.out.println(tuple._1() + ": " + tuple._2());
        });
        
        // SparkContext ์ข…๋ฃŒ
        sc.close();
    }
}
๐Ÿ’ก ์ฝ”๋“œ ์„ค๋ช… 1. SparkConf: Spark ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ค์ •์„ ๋‹ด๋Š” ๊ฐ์ฒด
โ€ข setAppName(): ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„ ์„ค์ •
โ€ข setMaster(): ์‹คํ–‰ ๋ชจ๋“œ ์„ค์ • (local[*]๋Š” ๋กœ์ปฌ์˜ ๋ชจ๋“  ์ฝ”์–ด ์‚ฌ์šฉ)

2. JavaSparkContext: Spark ๊ธฐ๋Šฅ์˜ ์ง„์ž…์ 
โ€ข RDD ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ
โ€ข ํด๋Ÿฌ์Šคํ„ฐ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ

3. Transformation vs Action:
โ€ข Transformation (flatMap, mapToPair, reduceByKey): ์ง€์—ฐ ํ‰๊ฐ€, ์ƒˆ๋กœ์šด RDD ๋ฐ˜ํ™˜
โ€ข Action (foreach, collect, count): ์ฆ‰์‹œ ์‹คํ–‰, ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜

๐Ÿ“Š DataFrame API๋กœ ๋” ์‰ฝ๊ฒŒ ๋ฐ์ดํ„ฐ ๋‹ค๋ฃจ๊ธฐ

RDD๋„ ์ข‹์ง€๋งŒ, ์‹ค๋ฌด์—์„œ๋Š” DataFrame API๋ฅผ ๋” ๋งŽ์ด ์‚ฌ์šฉํ•ด. SQL์ฒ˜๋Ÿผ ์ง๊ด€์ ์ด๊ณ , ์„ฑ๋Šฅ๋„ ๋” ์ข‹๊ฑฐ๋“ ! โšก

DataFrame์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ํ›จ์”ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, DataFrame๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜!

๐ŸŽฏ SparkSession ์ƒ์„ฑํ•˜๊ธฐ

DataFrame์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € SparkSession์„ ๋งŒ๋“ค์–ด์•ผ ํ•ด. ์ด๊ฑด Spark 2.0๋ถ€ํ„ฐ ๋„์ž…๋œ ํ†ตํ•ฉ ์ง„์ž…์ ์ด์•ผ.

import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;

public class DataFrameExample {
    public static void main(String[] args) {
        // SparkSession ์ƒ์„ฑ
        SparkSession spark = SparkSession.builder()
            .appName("DataFrame Example")
            .master("local[*]")
            .getOrCreate();
        
        // CSV ํŒŒ์ผ ์ฝ๊ธฐ
        Dataset<Row> df = spark.read()
            .option("header", "true")
            .option("inferSchema", "true")
            .csv("data.csv");
        
        // ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ํ™•์ธ
        df.printSchema();
        
        // ์ฒ˜์Œ 5๊ฐœ ํ–‰ ์ถœ๋ ฅ
        df.show(5);
        
        // SparkSession ์ข…๋ฃŒ
        spark.close();
    }
}

๐Ÿ” ๋ฐ์ดํ„ฐ ํƒ์ƒ‰ ๋ฐ ๋ณ€ํ™˜

DataFrame์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ์ž‘์—…๋“ค์„ ์‚ดํŽด๋ณด์ž! ๐Ÿ”ฌ

// ํŠน์ • ์ปฌ๋Ÿผ ์„ ํƒ
Dataset<Row> names = df.select("name", "age");

// ์กฐ๊ฑด ํ•„ํ„ฐ๋ง
Dataset<Row> adults = df.filter(df.col("age").gt(18));

// ๊ทธ๋ฃนํ™” ๋ฐ ์ง‘๊ณ„
Dataset<Row> avgByDept = df.groupBy("department")
    .avg("salary");

// ์ •๋ ฌ
Dataset<Row> sorted = df.orderBy(df.col("salary").desc());

// ์ƒˆ๋กœ์šด ์ปฌ๋Ÿผ ์ถ”๊ฐ€
Dataset<Row> withBonus = df.withColumn(
    "bonus", 
    df.col("salary").multiply(0.1)
);

// ์ปฌ๋Ÿผ๋ช… ๋ณ€๊ฒฝ
Dataset<Row> renamed = df.withColumnRenamed("old_name", "new_name");

// ์ค‘๋ณต ์ œ๊ฑฐ
Dataset<Row> distinct = df.distinct();

// ๊ฒฐ์ธก์น˜ ์ฒ˜๋ฆฌ
Dataset<Row> filled = df.na().fill(0); // null์„ 0์œผ๋กœ ์ฑ„์šฐ๊ธฐ
Dataset<Row> dropped = df.na().drop(); // null์ด ์žˆ๋Š” ํ–‰ ์ œ๊ฑฐ
๐ŸŽจ DataFrame ์ฃผ์š” ๋ฉ”์„œ๋“œ ์ •๋ฆฌ

๋ฉ”์„œ๋“œ ์„ค๋ช… ์˜ˆ์‹œ
select() ์ปฌ๋Ÿผ ์„ ํƒ df.select("name", "age")
filter() ํ–‰ ํ•„ํ„ฐ๋ง df.filter("age > 20")
groupBy() ๊ทธ๋ฃนํ™” df.groupBy("dept").count()
join() ์กฐ์ธ df1.join(df2, "id")
agg() ์ง‘๊ณ„ df.agg(sum("salary"))

๐Ÿ’พ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค ์ฝ๊ณ  ์“ฐ๊ธฐ

Spark๋Š” ์ •๋ง ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํฌ๋งท์„ ์ง€์›ํ•ด. CSV, JSON, Parquet, Avro, ORC ๋“ฑ๋“ฑ... ๋ญ๋“ ์ง€ ๋‹ค ์ฝ์„ ์ˆ˜ ์žˆ์–ด! ๐Ÿ“

// JSON ์ฝ๊ธฐ
Dataset<Row> jsonDF = spark.read().json("data.json");

// Parquet ์ฝ๊ธฐ (์ปฌ๋Ÿผ ๊ธฐ๋ฐ˜ ์ €์žฅ ํฌ๋งท, ๋งค์šฐ ํšจ์œจ์ !)
Dataset<Row> parquetDF = spark.read().parquet("data.parquet");

// JDBC๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฝ๊ธฐ
Dataset<Row> jdbcDF = spark.read()
    .format("jdbc")
    .option("url", "jdbc:mysql://localhost:3306/mydb")
    .option("dbtable", "users")
    .option("user", "username")
    .option("password", "password")
    .load();

// ๋ฐ์ดํ„ฐ ์ €์žฅ
df.write()
    .mode("overwrite") // append, overwrite, ignore, error
    .parquet("output.parquet");

// ํŒŒํ‹ฐ์…˜์„ ๋‚˜๋ˆ ์„œ ์ €์žฅ (์„ฑ๋Šฅ ์ตœ์ ํ™”!)
df.write()
    .partitionBy("year", "month")
    .parquet("partitioned_output");
โš ๏ธ ํŒŒ์ผ ํฌ๋งท ์„ ํƒ ๊ฐ€์ด๋“œ CSV: ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฝ์ง€๋งŒ ๋А๋ฆฌ๊ณ  ํƒ€์ž… ์ •๋ณด ์—†์Œ
JSON: ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ, ์ค‘์ฒฉ ๊ตฌ์กฐ ์ง€์›, ํ•˜์ง€๋งŒ ์šฉ๋Ÿ‰ ํผ
Parquet: ์ปฌ๋Ÿผ ๊ธฐ๋ฐ˜, ์••์ถ• ํšจ์œจ ์ข‹์Œ, ๋น…๋ฐ์ดํ„ฐ์— ์ตœ์  โญ
Avro: ํ–‰ ๊ธฐ๋ฐ˜, ์Šคํ‚ค๋งˆ ์ง„ํ™” ์ง€์›, ์ŠคํŠธ๋ฆฌ๋ฐ์— ์ ํ•ฉ
ORC: Parquet๊ณผ ์œ ์‚ฌ, Hive ์ตœ์ ํ™”

๐ŸŽญ Spark SQL: SQL๋กœ ๋น…๋ฐ์ดํ„ฐ ๋‹ค๋ฃจ๊ธฐ

SQL์„ ์ข‹์•„ํ•œ๋‹ค๋ฉด ์ข‹์€ ์†Œ์‹์ด์•ผ! Spark๋Š” ์™„์ „ํ•œ SQL ์ง€์›์„ ์ œ๊ณตํ•ด. DataFrame์„ ํ…Œ์ด๋ธ”์ฒ˜๋Ÿผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์„œ ์ •๋ง ํŽธ๋ฆฌํ•ด! ๐ŸŽ‰

๐Ÿ“ Temporary View ์ƒ์„ฑํ•˜๊ธฐ

// DataFrame์„ ์ž„์‹œ ๋ทฐ๋กœ ๋“ฑ๋ก
df.createOrReplaceTempView("employees");

// SQL ์ฟผ๋ฆฌ ์‹คํ–‰
Dataset<Row> result = spark.sql(
    "SELECT department, AVG(salary) as avg_salary " +
    "FROM employees " +
    "WHERE age > 25 " +
    "GROUP BY department " +
    "ORDER BY avg_salary DESC"
);

result.show();

// ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋„ ๊ฐ€๋Šฅ!
Dataset<Row> complexQuery = spark.sql(
    "SELECT e.name, e.salary, d.dept_name " +
    "FROM employees e " +
    "JOIN departments d ON e.dept_id = d.id " +
    "WHERE e.salary > (SELECT AVG(salary) FROM employees)"
);
๐Ÿ’ก Global Temporary View ์„ธ์…˜ ๊ฐ„์— ๊ณต์œ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Global Temporary View๋ฅผ ์‚ฌ์šฉํ•ด:

df.createGlobalTempView("global_employees")

์ ‘๊ทผํ•  ๋•Œ๋Š” global_temp ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ช…์‹œํ•ด์•ผ ํ•ด:
spark.sql("SELECT * FROM global_temp.global_employees")

๐Ÿ”ง User Defined Functions (UDF)

๋‚ด์žฅ ํ•จ์ˆ˜๋กœ ๋ถ€์กฑํ•  ๋•Œ๋Š” ์ง์ ‘ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด! ๐Ÿ› ๏ธ

import org.apache.spark.sql.api.java.UDF1;
import org.apache.spark.sql.types.DataTypes;

// UDF ์ •์˜
spark.udf().register("upperCase", 
    (String str) -> str.toUpperCase(), 
    DataTypes.StringType
);

// SQL์—์„œ ์‚ฌ์šฉ
Dataset<Row> result = spark.sql(
    "SELECT name, upperCase(name) as upper_name FROM employees"
);

// DataFrame API์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
import static org.apache.spark.sql.functions.*;
Dataset<Row> dfResult = df.withColumn(
    "upper_name", 
    callUDF("upperCase", col("name"))
);
// ๋” ๋ณต์žกํ•œ UDF ์˜ˆ์ œ
spark.udf().register("calculateBonus", 
    (Double salary, String level) -> {
        double multiplier = switch(level) {
            case "Senior" -> 0.2;
            case "Mid" -> 0.15;
            case "Junior" -> 0.1;
            default -> 0.05;
        };
        return salary * multiplier;
    }, 
    DataTypes.DoubleType
);

Dataset<Row> bonuses = spark.sql(
    "SELECT name, salary, level, " +
    "calculateBonus(salary, level) as bonus " +
    "FROM employees"
);

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต

Spark๋ฅผ ์“ด๋‹ค๊ณ  ๋ฌด์กฐ๊ฑด ๋น ๋ฅธ ๊ฑด ์•„๋‹ˆ์•ผ. ์ œ๋Œ€๋กœ ์ตœ์ ํ™”ํ•ด์•ผ ์ง„์งœ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

์—ฌ๊ธฐ์„œ ์†Œ๊ฐœํ•˜๋Š” ๊ธฐ๋ฒ•๋“ค์€ ์‹ค๋ฌด์—์„œ ์ •๋ง ์ค‘์š”ํ•ด. ์ด๊ฑธ ์•Œ๊ณ  ๋ชจ๋ฅด๊ณ ์˜ ์ฐจ์ด๊ฐ€ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ๋ช‡ ๋ฐฐ๋‚˜ ์ฐจ์ด๋‚˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฑฐ๋“ !

๐ŸŽฏ 1. ํŒŒํ‹ฐ์…”๋‹ (Partitioning)

๋ฐ์ดํ„ฐ๋ฅผ ์ ์ ˆํžˆ ๋‚˜๋ˆ„๋Š” ๊ฒŒ ์„ฑ๋Šฅ์˜ ํ•ต์‹ฌ์ด์•ผ. ํŒŒํ‹ฐ์…˜์ด ๋„ˆ๋ฌด ๋งŽ์•„๋„, ๋„ˆ๋ฌด ์ ์–ด๋„ ๋ฌธ์ œ๊ฐ€ ๋ผ.

// ํ˜„์žฌ ํŒŒํ‹ฐ์…˜ ์ˆ˜ ํ™•์ธ
int numPartitions = df.rdd().getNumPartitions();
System.out.println("ํŒŒํ‹ฐ์…˜ ์ˆ˜: " + numPartitions);

// ํŒŒํ‹ฐ์…˜ ์ˆ˜ ์กฐ์ •
Dataset<Row> repartitioned = df.repartition(100);

// ํŒŒํ‹ฐ์…˜ ์ˆ˜ ์ค„์ด๊ธฐ (์…”ํ”Œ ์—†์ด)
Dataset<Row> coalesced = df.coalesce(10);

// ํŠน์ • ์ปฌ๋Ÿผ ๊ธฐ์ค€์œผ๋กœ ํŒŒํ‹ฐ์…”๋‹
Dataset<Row> partitionedByDept = df.repartition(
    col("department")
);
๐Ÿ“Š ํŒŒํ‹ฐ์…˜ ์ˆ˜ ๊ฒฐ์ • ๊ฐ€์ด๋“œ โ€ข ๊ธฐ๋ณธ ์›์น™: ํŒŒํ‹ฐ์…˜๋‹น 128MB ~ 256MB๊ฐ€ ์ ๋‹น
โ€ข ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด: ์˜ค๋ฒ„ํ—ค๋“œ ์ฆ๊ฐ€, ์Šค์ผ€์ค„๋ง ๋น„์šฉ ์ƒ์Šน
โ€ข ๋„ˆ๋ฌด ์ ์œผ๋ฉด: ๋ณ‘๋ ฌ์„ฑ ์ €ํ•˜, ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์œ„ํ—˜
โ€ข ๊ถŒ์žฅ ๊ณต์‹: (์ „์ฒด ๋ฐ์ดํ„ฐ ํฌ๊ธฐ / 128MB) ร— 2~3๋ฐฐ
โ€ข ์ฝ”์–ด ์ˆ˜ ๊ณ ๋ ค: ์ตœ์†Œํ•œ ์ฝ”์–ด ์ˆ˜์˜ 2~3๋ฐฐ๋Š” ๋˜์–ด์•ผ ํ•จ

๐Ÿ’พ 2. ์บ์‹ฑ๊ณผ ํผ์‹œ์Šคํ„ด์Šค (Caching & Persistence)

๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์บ์‹ฑ์€ ํ•„์ˆ˜์•ผ! ๐Ÿ’Ž

// ๋ฉ”๋ชจ๋ฆฌ์— ์บ์‹ฑ
df.cache();
// ๋˜๋Š”
df.persist(StorageLevel.MEMORY_ONLY());

// ๋ฉ”๋ชจ๋ฆฌ์™€ ๋””์Šคํฌ ๋ชจ๋‘ ์‚ฌ์šฉ
df.persist(StorageLevel.MEMORY_AND_DISK());

// ์ง๋ ฌํ™”ํ•ด์„œ ์ €์žฅ (๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ)
df.persist(StorageLevel.MEMORY_ONLY_SER());

// ์บ์‹œ ํ•ด์ œ
df.unpersist();
โœ… ์บ์‹ฑ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ์บ์‹ฑํ•˜๋ฉด ์ข‹์„ ๋•Œ:
โ€ข ๊ฐ™์€ DataFrame์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉ
โ€ข ๋ฐ˜๋ณต์ ์ธ ๋จธ์‹ ๋Ÿฌ๋‹ ํ•™์Šต
โ€ข ๋Œ€ํ™”ํ˜• ๋ฐ์ดํ„ฐ ๋ถ„์„

์บ์‹ฑํ•˜์ง€ ๋ง์•„์•ผ ํ•  ๋•Œ:
โ€ข ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ
โ€ข ๋ฐ์ดํ„ฐ๊ฐ€ ๋„ˆ๋ฌด ์ปค์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์šฐ๋ ค
โ€ข ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋‚ฎ์€ ๊ฐ„๋‹จํ•œ ๋ณ€ํ™˜

๐Ÿ”„ 3. ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์กฐ์ธ (Broadcast Join)

์ž‘์€ ํ…Œ์ด๋ธ”๊ณผ ํฐ ํ…Œ์ด๋ธ”์„ ์กฐ์ธํ•  ๋•Œ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—„์ฒญ๋‚˜๊ฒŒ ๋นจ๋ผ์ ธ! โšก

import static org.apache.spark.sql.functions.broadcast;

// ์ž‘์€ DataFrame์„ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ
Dataset<Row> smallDF = spark.read().csv("small_data.csv");
Dataset<Row> largeDF = spark.read().csv("large_data.csv");

// ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์กฐ์ธ
Dataset<Row> joined = largeDF.join(
    broadcast(smallDF), 
    "key"
);

// ์ž๋™ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์ž„๊ณ„๊ฐ’ ์„ค์ • (๊ธฐ๋ณธ 10MB)
spark.conf().set(
    "spark.sql.autoBroadcastJoinThreshold", 
    "50MB"
);
โš ๏ธ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์ฃผ์˜์‚ฌํ•ญ โ€ข ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธํ•  ๋ฐ์ดํ„ฐ๋Š” ๊ฐ Executor ๋ฉ”๋ชจ๋ฆฌ์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ
โ€ข ์ผ๋ฐ˜์ ์œผ๋กœ 100MB ์ดํ•˜ ๋ฐ์ดํ„ฐ์— ์ ํ•ฉ
โ€ข ๋„ˆ๋ฌด ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธํ•˜๋ฉด OutOfMemory ๋ฐœ์ƒ ๊ฐ€๋Šฅ
โ€ข ๋“œ๋ผ์ด๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ๋„ ์ถฉ๋ถ„ํ•ด์•ผ ํ•จ

๐ŸŽจ 4. ๋ฐ์ดํ„ฐ ์Šคํ ์ฒ˜๋ฆฌ (Data Skew)

๋ฐ์ดํ„ฐ๊ฐ€ ํŠน์ • ํŒŒํ‹ฐ์…˜์— ๋ชฐ๋ฆฌ๋Š” ํ˜„์ƒ์„ "์Šคํ"๋ผ๊ณ  ํ•ด. ์ด๊ฑธ ํ•ด๊ฒฐํ•˜์ง€ ์•Š์œผ๋ฉด ์ผ๋ถ€ ํƒœ์Šคํฌ๋งŒ ์—„์ฒญ ์˜ค๋ž˜ ๊ฑธ๋ ค! ๐Ÿ˜ฐ

// ์Šคํ ํ™•์ธ
df.groupBy("key").count().orderBy(desc("count")).show();

// ์†”ํŠธ ๊ธฐ๋ฒ•์œผ๋กœ ์Šคํ ํ•ด๊ฒฐ
import org.apache.spark.sql.functions.*;

Dataset<Row> salted = df.withColumn(
    "salted_key",
    concat(col("key"), lit("_"), 
           (rand().multiply(10)).cast("int"))
);

// ์Šคํ๋œ ํ‚ค์— ๋Œ€ํ•œ ํŠน๋ณ„ ์ฒ˜๋ฆฌ
Dataset<Row> skewedKeys = df.groupBy("key")
    .count()
    .filter(col("count").gt(1000000));

Dataset<Row> normalData = df.join(
    broadcast(skewedKeys), 
    Seq("key"), 
    "left_anti"
);

Dataset<Row> skewedData = df.join(
    broadcast(skewedKeys), 
    Seq("key"), 
    "inner"
);

๐Ÿ” 5. ํ”„๋กœ์ ์…˜ ํ‘ธ์‹œ๋‹ค์šด & ํ”„๋ฆฌ๋””์ผ€์ดํŠธ ํ‘ธ์‹œ๋‹ค์šด

ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ์ฝ๊ณ , ํ•„ํ„ฐ๋ง์„ ์ตœ๋Œ€ํ•œ ๋นจ๋ฆฌ ํ•˜๋Š” ๊ฒŒ ํ•ต์‹ฌ์ด์•ผ! ๐ŸŽฏ

// โŒ ๋‚˜์œ ์˜ˆ: ๋ชจ๋“  ์ปฌ๋Ÿผ์„ ์ฝ์€ ํ›„ ํ•„ํ„ฐ๋ง
Dataset<Row> bad = spark.read()
    .parquet("large_data.parquet")
    .filter(col("age").gt(30))
    .select("name", "age");

// โœ… ์ข‹์€ ์˜ˆ: ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ์ฝ๊ณ  ์กฐ๊ธฐ ํ•„ํ„ฐ๋ง
Dataset<Row> good = spark.read()
    .parquet("large_data.parquet")
    .select("name", "age")
    .filter(col("age").gt(30));

// Parquet ํŒŒ์ผ์˜ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ํ‘ธ์‹œ๋‹ค์šด๋จ
// ํ•˜์ง€๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ์ˆœ์„œ๋ฅผ ์กฐ์ •ํ•˜๋ฉด ๋” ์ข‹์Œ
Performance Optimization Techniques Partitioning Data Distribution Parallel Processing Caching Memory Storage Reuse Data Broadcast Small Table Join Avoid Shuffle Skew Handling Balance Load Salt Technique 2-3x Speed Improvement 10x With Caching 100x Broadcast Join 5x Skew Resolution

๐ŸŒŠ Spark Streaming: ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€๋Š” ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ค๋ค˜์–ด. ์ด์ œ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐ŸŽฌ

Spark Streaming์€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋งˆ์ดํฌ๋กœ ๋ฐฐ์น˜๋กœ ์ฒ˜๋ฆฌํ•ด. ์นดํ”„์นด, ํ”Œ๋Ÿผ, ํ‚ค๋„ค์‹œ์Šค ๋“ฑ ๋‹ค์–‘ํ•œ ์†Œ์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด.

๐Ÿ“ก Structured Streaming ๊ธฐ๋ณธ

import org.apache.spark.sql.streaming.StreamingQuery;
import org.apache.spark.sql.streaming.Trigger;

// ์ŠคํŠธ๋ฆฌ๋ฐ ์†Œ์Šค ์ •์˜ (ํŒŒ์ผ ๊ธฐ๋ฐ˜ ์˜ˆ์ œ)
Dataset<Row> streamingDF = spark.readStream()
    .format("csv")
    .option("header", "true")
    .schema(schema) // ์Šคํ‚ค๋งˆ ๋ช…์‹œ ํ•„์ˆ˜!
    .load("input_directory/");

// ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
Dataset<Row> processed = streamingDF
    .filter(col("value").gt(100))
    .groupBy(
        window(col("timestamp"), "10 minutes"),
        col("category")
    )
    .count();

// ๊ฒฐ๊ณผ ์ถœ๋ ฅ (์ฝ˜์†”)
StreamingQuery query = processed.writeStream()
    .outputMode("complete")
    .format("console")
    .trigger(Trigger.ProcessingTime("10 seconds"))
    .start();

query.awaitTermination();

๐Ÿ”Œ Kafka์™€ ์—ฐ๋™ํ•˜๊ธฐ

์‹ค๋ฌด์—์„œ๋Š” Kafka๋ฅผ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•ด. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋„ ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ์ด๋‚˜ ๋กœ๊ทธ ์ฒ˜๋ฆฌ์— ์ด๋Ÿฐ ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ“จ

// Kafka์—์„œ ์ฝ๊ธฐ
Dataset<Row> kafkaDF = spark.readStream()
    .format("kafka")
    .option("kafka.bootstrap.servers", "localhost:9092")
    .option("subscribe", "topic1,topic2")
    .option("startingOffsets", "earliest")
    .load();

// Kafka ๋ฉ”์‹œ์ง€๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์˜ค๋ฏ€๋กœ ๋ณ€ํ™˜ ํ•„์š”
Dataset<Row> parsedDF = kafkaDF
    .selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
    .select(from_json(col("value"), schema).as("data"))
    .select("data.*");

// ์ฒ˜๋ฆฌ ํ›„ ๋‹ค์‹œ Kafka๋กœ ์“ฐ๊ธฐ
StreamingQuery kafkaQuery = parsedDF
    .selectExpr("CAST(id AS STRING) AS key", 
                "to_json(struct(*)) AS value")
    .writeStream()
    .format("kafka")
    .option("kafka.bootstrap.servers", "localhost:9092")
    .option("topic", "output_topic")
    .option("checkpointLocation", "/tmp/checkpoint")
    .start();
๐ŸŽฏ Streaming Output Modes 1. Append Mode:
โ€ข ์ƒˆ๋กœ์šด ํ–‰๋งŒ ์ถœ๋ ฅ
โ€ข ์ง‘๊ณ„ ์—†๋Š” ์ฟผ๋ฆฌ์— ์ ํ•ฉ
โ€ข ๊ฐ€์žฅ ๋น ๋ฆ„

2. Complete Mode:
โ€ข ์ „์ฒด ๊ฒฐ๊ณผ ํ…Œ์ด๋ธ” ์ถœ๋ ฅ
โ€ข ์ง‘๊ณ„ ์ฟผ๋ฆฌ์— ์‚ฌ์šฉ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๋†’์Œ

3. Update Mode:
โ€ข ๋ณ€๊ฒฝ๋œ ํ–‰๋งŒ ์ถœ๋ ฅ
โ€ข ์ง‘๊ณ„ ์ฟผ๋ฆฌ์— ์ ํ•ฉ
โ€ข Complete์™€ Append์˜ ์ค‘๊ฐ„

โฐ ์œˆ๋„์šฐ ์—ฐ์‚ฐ๊ณผ ์›Œํ„ฐ๋งˆํฌ

์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์ง‘๊ณ„๋ฅผ ํ•  ๋•Œ๋Š” ์œˆ๋„์šฐ์™€ ์›Œํ„ฐ๋งˆํฌ๋ฅผ ์ž˜ ์ดํ•ดํ•ด์•ผ ํ•ด! โฑ๏ธ

// ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ
Dataset<Row> windowedCounts = streamingDF
    .withWatermark("timestamp", "10 minutes")
    .groupBy(
        window(col("timestamp"), "10 minutes", "5 minutes"),
        col("word")
    )
    .count();

// ์„ธ์…˜ ์œˆ๋„์šฐ
Dataset<Row> sessionWindow = streamingDF
    .withWatermark("timestamp", "30 minutes")
    .groupBy(
        session_window(col("timestamp"), "5 minutes"),
        col("userId")
    )
    .count();

// ํ…€๋ธ”๋ง ์œˆ๋„์šฐ (๊ฒน์น˜์ง€ ์•Š์Œ)
Dataset<Row> tumblingWindow = streamingDF
    .withWatermark("timestamp", "10 minutes")
    .groupBy(
        window(col("timestamp"), "10 minutes"),
        col("category")
    )
    .agg(
        sum("amount").as("total"),
        avg("amount").as("average")
    );
๐Ÿ’ก ์›Œํ„ฐ๋งˆํฌ(Watermark)๋ž€? ์›Œํ„ฐ๋งˆํฌ๋Š” "์ด ์‹œ๊ฐ„ ์ด์ „์˜ ๋ฐ์ดํ„ฐ๋Š” ๋” ์ด์ƒ ์˜ค์ง€ ์•Š์„ ๊ฒƒ"์ด๋ผ๊ณ  ์‹œ์Šคํ…œ์— ์•Œ๋ ค์ฃผ๋Š” ๊ฑฐ์•ผ.

โ€ข ๋Šฆ๊ฒŒ ๋„์ฐฉํ•˜๋Š” ๋ฐ์ดํ„ฐ(Late Data) ์ฒ˜๋ฆฌ
โ€ข ์ƒํƒœ ์ €์žฅ ํฌ๊ธฐ ์ œํ•œ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์ตœ์ ํ™”

์˜ˆ: withWatermark("timestamp", "10 minutes")
โ†’ ํ˜„์žฌ ์ตœ๋Œ€ ์ด๋ฒคํŠธ ์‹œ๊ฐ„ - 10๋ถ„ ์ด์ „ ๋ฐ์ดํ„ฐ๋Š” ๋ฒ„๋ฆผ

๐Ÿค– Spark MLlib: ๋จธ์‹ ๋Ÿฌ๋‹ ํŒŒ์ดํ”„๋ผ์ธ

Spark๋Š” ๋จธ์‹ ๋Ÿฌ๋‹๋„ ์ง€์›ํ•ด! MLlib๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋กœ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ ํ•™์Šตํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿง 

๐Ÿ”ง ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ

import org.apache.spark.ml.feature.*;
import org.apache.spark.ml.Pipeline;
import org.apache.spark.ml.PipelineStage;

// ๋ฌธ์ž์—ด์„ ์ˆซ์ž๋กœ ๋ณ€ํ™˜
StringIndexer indexer = new StringIndexer()
    .setInputCol("category")
    .setOutputCol("categoryIndex");

// ์›-ํ•ซ ์ธ์ฝ”๋”ฉ
OneHotEncoder encoder = new OneHotEncoder()
    .setInputCol("categoryIndex")
    .setOutputCol("categoryVec");

// ํŠน์„ฑ ๋ฒกํ„ฐ ์ƒ์„ฑ
VectorAssembler assembler = new VectorAssembler()
    .setInputCols(new String[]{"age", "salary", "categoryVec"})
    .setOutputCol("features");

// ํŠน์„ฑ ์Šค์ผ€์ผ๋ง
StandardScaler scaler = new StandardScaler()
    .setInputCol("features")
    .setOutputCol("scaledFeatures")
    .setWithStd(true)
    .setWithMean(true);

๐Ÿ“Š ๋ถ„๋ฅ˜ ๋ชจ๋ธ ํ•™์Šต

import org.apache.spark.ml.classification.LogisticRegression;
import org.apache.spark.ml.classification.LogisticRegressionModel;
import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator;

// ๋ฐ์ดํ„ฐ ๋ถ„ํ• 
Dataset<Row>[] splits = df.randomSplit(new double[]{0.8, 0.2}, 42);
Dataset<Row> trainingData = splits[0];
Dataset<Row> testData = splits[1];

// ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ๋ชจ๋ธ
LogisticRegression lr = new LogisticRegression()
    .setMaxIter(10)
    .setRegParam(0.01)
    .setElasticNetParam(0.8)
    .setFeaturesCol("scaledFeatures")
    .setLabelCol("label");

// ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ
Pipeline pipeline = new Pipeline()
    .setStages(new PipelineStage[]{
        indexer, encoder, assembler, scaler, lr
    });

// ๋ชจ๋ธ ํ•™์Šต
PipelineModel model = pipeline.fit(trainingData);

// ์˜ˆ์ธก
Dataset<Row> predictions = model.transform(testData);

// ํ‰๊ฐ€
BinaryClassificationEvaluator evaluator = 
    new BinaryClassificationEvaluator()
        .setLabelCol("label")
        .setRawPredictionCol("rawPrediction")
        .setMetricName("areaUnderROC");

double auc = evaluator.evaluate(predictions);
System.out.println("AUC: " + auc);

๐ŸŽฏ ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹

import org.apache.spark.ml.tuning.*;
import org.apache.spark.ml.param.ParamMap;

// ํŒŒ๋ผ๋ฏธํ„ฐ ๊ทธ๋ฆฌ๋“œ ์ •์˜
ParamGridBuilder paramGrid = new ParamGridBuilder()
    .addGrid(lr.regParam(), new double[]{0.01, 0.1, 1.0})
    .addGrid(lr.elasticNetParam(), new double[]{0.0, 0.5, 1.0})
    .addGrid(lr.maxIter(), new int[]{10, 50, 100});

// ๊ต์ฐจ ๊ฒ€์ฆ
CrossValidator cv = new CrossValidator()
    .setEstimator(pipeline)
    .setEvaluator(evaluator)
    .setEstimatorParamMaps(paramGrid.build())
    .setNumFolds(5)
    .setParallelism(4);

// ์ตœ์  ๋ชจ๋ธ ์ฐพ๊ธฐ
CrossValidatorModel cvModel = cv.fit(trainingData);

// ์ตœ์  ํŒŒ๋ผ๋ฏธํ„ฐ ํ™•์ธ
LogisticRegressionModel bestModel = 
    (LogisticRegressionModel) cvModel.bestModel()
        .stages()[cvModel.bestModel().stages().length - 1];

System.out.println("์ตœ์  RegParam: " + bestModel.getRegParam());
System.out.println("์ตœ์  ElasticNetParam: " + 
    bestModel.getElasticNetParam());
โœ… MLlib ์ฃผ์š” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ถ„๋ฅ˜: Logistic Regression, Decision Tree, Random Forest, GBT, Naive Bayes, SVM

ํšŒ๊ท€: Linear Regression, Decision Tree Regression, Random Forest Regression, GBT Regression

ํด๋Ÿฌ์Šคํ„ฐ๋ง: K-Means, Gaussian Mixture, LDA, Bisecting K-Means

ํ˜‘์—… ํ•„ํ„ฐ๋ง: ALS (Alternating Least Squares)

์ฐจ์› ์ถ•์†Œ: PCA, SVD

๐Ÿ› ๋””๋ฒ„๊น…๊ณผ ๋ชจ๋‹ˆํ„ฐ๋ง

Spark ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์šด์˜ํ•˜๋‹ค ๋ณด๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ๋งˆ๋ จ์ด์•ผ. ํšจ๊ณผ์ ์œผ๋กœ ๋””๋ฒ„๊น…ํ•˜๊ณ  ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ”

๐Ÿ“Š Spark UI ํ™œ์šฉํ•˜๊ธฐ

Spark๋Š” ๊ฐ•๋ ฅํ•œ ์›น UI๋ฅผ ์ œ๊ณตํ•ด. ๊ธฐ๋ณธ์ ์œผ๋กœ http://localhost:4040์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด.

๐ŸŽฏ Spark UI ์ฃผ์š” ํƒญ 1. Jobs: ์‹คํ–‰๋œ ์ž‘์—… ๋ชฉ๋ก๊ณผ ์ƒํƒœ
2. Stages: ๊ฐ ์Šคํ…Œ์ด์ง€์˜ ์„ธ๋ถ€ ์ •๋ณด์™€ ํƒœ์Šคํฌ
3. Storage: ์บ์‹œ๋œ RDD/DataFrame ์ •๋ณด
4. Environment: Spark ์„ค์ • ๋ฐ ํ™˜๊ฒฝ ๋ณ€์ˆ˜
5. Executors: Executor ์ƒํƒœ ๋ฐ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰
6. SQL: SQL ์ฟผ๋ฆฌ ์‹คํ–‰ ๊ณ„ํš ๋ฐ ์„ฑ๋Šฅ

๐Ÿ” ์‹คํ–‰ ๊ณ„ํš ๋ถ„์„

// ๋…ผ๋ฆฌ์  ์‹คํ–‰ ๊ณ„ํš ํ™•์ธ
df.explain(true);

// ์ถœ๋ ฅ ์˜ˆ์‹œ:
// == Parsed Logical Plan ==
// == Analyzed Logical Plan ==
// == Optimized Logical Plan ==
// == Physical Plan ==

// ๊ฐ„๋‹จํ•œ ์‹คํ–‰ ๊ณ„ํš
df.explain();

// ์ฝ”์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ตœ์ ํ™” ์ •๋ณด ํฌํ•จ
df.explain("cost");

๐Ÿ“ ๋กœ๊น… ์„ค์ •

import org.apache.log4j.Logger;
import org.apache.log4j.Level;

// ๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ค์ •
Logger.getLogger("org").setLevel(Level.WARN);
Logger.getLogger("akka").setLevel(Level.WARN);

// ์ปค์Šคํ…€ ๋กœ๊ฑฐ
Logger logger = Logger.getLogger(MySparkApp.class);
logger.info("์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘");
logger.warn("๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€");
logger.error("์—๋Ÿฌ ๋ฐœ์ƒ", exception);

โš ๏ธ ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ์ฑ…

1. OutOfMemoryError ์›์ธ:
โ€ข Executor ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ
โ€ข ๋„ˆ๋ฌด ํฐ ๋ฐ์ดํ„ฐ๋ฅผ collect()
โ€ข ํŒŒํ‹ฐ์…˜ ์ˆ˜ ๋ถ€์กฑ

ํ•ด๊ฒฐ์ฑ…:
โ€ข Executor ๋ฉ”๋ชจ๋ฆฌ ์ฆ๊ฐ€: --executor-memory 8g
โ€ข ํŒŒํ‹ฐ์…˜ ์ˆ˜ ์ฆ๊ฐ€: df.repartition(200)
โ€ข collect() ๋Œ€์‹  take() ์‚ฌ์šฉ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ ์ฆ๊ฐ€: spark.executor.memoryOverhead
2. ๋А๋ฆฐ ์ž‘์—… (Slow Tasks) ์›์ธ:
โ€ข ๋ฐ์ดํ„ฐ ์Šคํ
โ€ข ๋น„ํšจ์œจ์ ์ธ ์กฐ์ธ
โ€ข ๊ณผ๋„ํ•œ ์…”ํ”Œ

ํ•ด๊ฒฐ์ฑ…:
โ€ข ์Šคํ ์ฒ˜๋ฆฌ (์†”ํŠธ ๊ธฐ๋ฒ•)
โ€ข ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์กฐ์ธ ์‚ฌ์šฉ
โ€ข ํŒŒํ‹ฐ์…˜ ํ‚ค ์žฌ์„ค์ •
โ€ข ์ค‘๊ฐ„ ๊ฒฐ๊ณผ ์บ์‹ฑ
3. Shuffle ๊ด€๋ จ ์—๋Ÿฌ ์›์ธ:
โ€ข ์…”ํ”Œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋„ˆ๋ฌด ํผ
โ€ข ๋„คํŠธ์›Œํฌ ํƒ€์ž„์•„์›ƒ
โ€ข ๋””์Šคํฌ ๊ณต๊ฐ„ ๋ถ€์กฑ

ํ•ด๊ฒฐ์ฑ…:
โ€ข ์…”ํ”Œ ํŒŒํ‹ฐ์…˜ ์ˆ˜ ์กฐ์ •: spark.sql.shuffle.partitions
โ€ข ์••์ถ• ํ™œ์„ฑํ™”: spark.shuffle.compress=true
โ€ข ํƒ€์ž„์•„์›ƒ ์ฆ๊ฐ€: spark.network.timeout
โ€ข ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ๊ณต๊ฐ„ ํ™•๋ณด

๐Ÿš€ ์‹ค์ „ ํ”„๋กœ์ ํŠธ: ๋กœ๊ทธ ๋ถ„์„ ์‹œ์Šคํ…œ

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! ์›น ์„œ๋ฒ„ ๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ•˜๋Š” ์‹œ์Šคํ…œ์ด์•ผ. ๐ŸŽฏ

๐Ÿ“‹ ํ”„๋กœ์ ํŠธ ์š”๊ตฌ์‚ฌํ•ญ

๋ชฉํ‘œ: ๋Œ€์šฉ๋Ÿ‰ ์›น ์„œ๋ฒ„ ๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ธ์‚ฌ์ดํŠธ ๋„์ถœ

๊ธฐ๋Šฅ:
โ€ข ์‹œ๊ฐ„๋Œ€๋ณ„ ํŠธ๋ž˜ํ”ฝ ๋ถ„์„
โ€ข ์ธ๊ธฐ ํŽ˜์ด์ง€ ์ˆœ์œ„
โ€ข ์—๋Ÿฌ ๋ฐœ์ƒ ํŒจํ„ด ๋ถ„์„
โ€ข ์‚ฌ์šฉ์ž ํ–‰๋™ ๋ถ„์„
โ€ข ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ ์—…๋ฐ์ดํŠธ

๐Ÿ’ป ์ „์ฒด ์ฝ”๋“œ ๊ตฌํ˜„

import org.apache.spark.sql.*;
import org.apache.spark.sql.types.*;
import static org.apache.spark.sql.functions.*;

public class LogAnalysisSystem {
    
    public static void main(String[] args) {
        // SparkSession ์ƒ์„ฑ
        SparkSession spark = SparkSession.builder()
            .appName("Log Analysis System")
            .config("spark.sql.shuffle.partitions", "200")
            .config("spark.sql.adaptive.enabled", "true")
            .getOrCreate();
        
        // ๋กœ๊ทธ ์Šคํ‚ค๋งˆ ์ •์˜
        StructType schema = new StructType()
            .add("timestamp", DataTypes.TimestampType)
            .add("ip", DataTypes.StringType)
            .add("method", DataTypes.StringType)
            .add("url", DataTypes.StringType)
            .add("status", DataTypes.IntegerType)
            .add("size", DataTypes.LongType)
            .add("referer", DataTypes.StringType)
            .add("userAgent", DataTypes.StringType);
        
        // ๋กœ๊ทธ ํŒŒ์ผ ์ฝ๊ธฐ
        Dataset<Row> logs = spark.read()
            .schema(schema)
            .json("logs/*.json")
            .cache(); // ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์บ์‹ฑ
        
        // 1. ์‹œ๊ฐ„๋Œ€๋ณ„ ํŠธ๋ž˜ํ”ฝ ๋ถ„์„
        Dataset<Row> hourlyTraffic = logs
            .withColumn("hour", hour(col("timestamp")))
            .groupBy("hour")
            .agg(
                count("*").as("requests"),
                sum("size").as("totalBytes"),
                countDistinct("ip").as("uniqueUsers")
            )
            .orderBy("hour");
        
        hourlyTraffic.show();
        
        // 2. ์ธ๊ธฐ ํŽ˜์ด์ง€ Top 10
        Dataset<Row> topPages = logs
            .groupBy("url")
            .agg(
                count("*").as("hits"),
                avg("size").as("avgSize")
            )
            .orderBy(desc("hits"))
            .limit(10);
        
        topPages.show();
        
        // 3. ์—๋Ÿฌ ๋ถ„์„
        Dataset<Row> errors = logs
            .filter(col("status").geq(400))
            .groupBy("status", "url")
            .count()
            .orderBy(desc("count"));
        
        errors.show();
        
        // 4. ์‚ฌ์šฉ์ž ์„ธ์…˜ ๋ถ„์„
        Dataset<Row> userSessions = logs
            .withWatermark("timestamp", "30 minutes")
            .groupBy(
                col("ip"),
                session_window(col("timestamp"), "30 minutes")
            )
            .agg(
                count("*").as("pageViews"),
                collect_list("url").as("visitedPages"),
                min("timestamp").as("sessionStart"),
                max("timestamp").as("sessionEnd")
            );
        
        // 5. ๊ฒฐ๊ณผ ์ €์žฅ
        hourlyTraffic.write()
            .mode("overwrite")
            .partitionBy("hour")
            .parquet("output/hourly_traffic");
        
        topPages.write()
            .mode("overwrite")
            .json("output/top_pages");
        
        // 6. ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์œ„ํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ
        Dataset<Row> streamingLogs = spark.readStream()
            .schema(schema)
            .json("streaming_logs/");
        
        Dataset<Row> realtimeStats = streamingLogs
            .withWatermark("timestamp", "10 minutes")
            .groupBy(
                window(col("timestamp"), "1 minute"),
                col("status")
            )
            .count();
        
        StreamingQuery query = realtimeStats.writeStream()
            .outputMode("update")
            .format("console")
            .trigger(Trigger.ProcessingTime("10 seconds"))
            .start();
        
        query.awaitTermination();
        spark.close();
    }
}

๐Ÿ“ˆ ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ ์šฉ

// ์ตœ์ ํ™”๋œ ๋ฒ„์ „
public class OptimizedLogAnalysis {
    
    public static Dataset<Row> analyzeWithOptimization(
        SparkSession spark, String inputPath) {
        
        // 1. ํŒŒํ‹ฐ์…˜ ํ”„๋ฃจ๋‹์„ ์œ„ํ•œ ํŒŒํ‹ฐ์…˜ ์ปฌ๋Ÿผ ํ™œ์šฉ
        Dataset<Row> logs = spark.read()
            .parquet(inputPath)
            .filter(col("date").geq("2024-01-01")) // ํŒŒํ‹ฐ์…˜ ํ•„ํ„ฐ
            .repartition(200, col("ip")); // ์Šคํ ๋ฐฉ์ง€
        
        // 2. ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์กฐ์ธ์œผ๋กœ IP ์ •๋ณด ์ถ”๊ฐ€
        Dataset<Row> ipInfo = spark.read()
            .parquet("ip_database.parquet")
            .select("ip", "country", "city");
        
        Dataset<Row> enriched = logs.join(
            broadcast(ipInfo),
            "ip"
        );
        
        // 3. ์ค‘๊ฐ„ ๊ฒฐ๊ณผ ์บ์‹ฑ
        enriched.cache();
        
        // 4. ์ง‘๊ณ„ ์ตœ์ ํ™”
        Dataset<Row> stats = enriched
            .groupBy("country", "hour")
            .agg(
                count("*").as("requests"),
                approx_count_distinct("ip", 0.05).as("uniqueUsers"), // ๊ทผ์‚ฌ์น˜
                sum("size").as("totalBytes")
            );
        
        // 5. ๊ฒฐ๊ณผ๋ฅผ ํŒŒํ‹ฐ์…˜๋ณ„๋กœ ์ €์žฅ
        stats.write()
            .mode("overwrite")
            .partitionBy("country")
            .parquet("output/stats_by_country");
        
        return stats;
    }
}

๐ŸŽ“ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค์™€ ํŒ

์‹ค๋ฌด์—์„œ Spark๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์–ป์€ ๋…ธํ•˜์šฐ๋“ค์„ ๊ณต์œ ํ• ๊ฒŒ! ๐Ÿ’ก

โœ… 1. ๋ฐ์ดํ„ฐ ํฌ๋งท ์„ ํƒ โ€ข Parquet ์šฐ์„ : ์ปฌ๋Ÿผ ๊ธฐ๋ฐ˜, ์••์ถ• ํšจ์œจ, ์Šคํ‚ค๋งˆ ํฌํ•จ
โ€ข ํŒŒํ‹ฐ์…”๋‹: ์ž์ฃผ ํ•„ํ„ฐ๋งํ•˜๋Š” ์ปฌ๋Ÿผ์œผ๋กœ ํŒŒํ‹ฐ์…˜
โ€ข ๋ฒ„ํ‚ทํŒ…: ์กฐ์ธ์ด ๋งŽ๋‹ค๋ฉด ๋ฒ„ํ‚ทํŒ… ๊ณ ๋ ค
โ€ข ์••์ถ•: Snappy (์†๋„) vs Gzip (์••์ถ•๋ฅ )
โœ… 2. ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ โ€ข Executor ๋ฉ”๋ชจ๋ฆฌ: ๋ฐ์ดํ„ฐ ํฌ๊ธฐ์˜ 2-3๋ฐฐ
โ€ข Driver ๋ฉ”๋ชจ๋ฆฌ: collect() ์‚ฌ์šฉ ์‹œ ์ถฉ๋ถ„ํžˆ ํ• ๋‹น
โ€ข ์˜ค๋ฒ„ํ—ค๋“œ: ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ์˜ 10-15% ์˜ˆ์•ฝ
โ€ข ์บ์‹ฑ ์ „๋žต: ์ •๋ง ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์บ์‹ฑ
โœ… 3. ์ฝ”๋“œ ์ž‘์„ฑ ํŒ โ€ข ์ง€์—ฐ ํ‰๊ฐ€ ํ™œ์šฉ: ์—ฌ๋Ÿฌ ๋ณ€ํ™˜์„ ์ฒด์ด๋‹
โ€ข ์กฐ๊ธฐ ํ•„ํ„ฐ๋ง: ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ ๋ฐ์ดํ„ฐ ์ค„์ด๊ธฐ
โ€ข ํƒ€์ž… ์•ˆ์ •์„ฑ: Dataset API ํ™œ์šฉ (Java/Scala)
โ€ข UDF ์ตœ์†Œํ™”: ๋‚ด์žฅ ํ•จ์ˆ˜๊ฐ€ ํ›จ์”ฌ ๋น ๋ฆ„
โœ… 4. ํด๋Ÿฌ์Šคํ„ฐ ์„ค์ • โ€ข ๋™์  ํ• ๋‹น: ๋ฆฌ์†Œ์Šค ํšจ์œจ์  ์‚ฌ์šฉ
โ€ข ์ถ”์ธก ์‹คํ–‰: ๋А๋ฆฐ ํƒœ์Šคํฌ ์žฌ์‹คํ–‰
โ€ข ์••์ถ•: ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ ๊ฐ์†Œ
โ€ข ์ง๋ ฌํ™”: Kryo ์‚ฌ์šฉ ๊ถŒ์žฅ

โš™๏ธ ์ถ”์ฒœ ์„ค์ •๊ฐ’

SparkConf conf = new SparkConf()
    // ๊ธฐ๋ณธ ์„ค์ •
    .set("spark.app.name", "MyApp")
    .set("spark.master", "yarn")
    
    // ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •
    .set("spark.executor.memory", "8g")
    .set("spark.executor.memoryOverhead", "1g")
    .set("spark.driver.memory", "4g")
    
    // ์ฝ”์–ด ์„ค์ •
    .set("spark.executor.cores", "4")
    .set("spark.executor.instances", "10")
    
    // ์…”ํ”Œ ์ตœ์ ํ™”
    .set("spark.sql.shuffle.partitions", "200")
    .set("spark.sql.adaptive.enabled", "true")
    .set("spark.sql.adaptive.coalescePartitions.enabled", "true")
    
    // ์••์ถ•
    .set("spark.sql.parquet.compression.codec", "snappy")
    .set("spark.shuffle.compress", "true")
    
    // ์ง๋ ฌํ™”
    .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
    .set("spark.kryo.registrationRequired", "false")
    
    // ๋™์  ํ• ๋‹น
    .set("spark.dynamicAllocation.enabled", "true")
    .set("spark.dynamicAllocation.minExecutors", "2")
    .set("spark.dynamicAllocation.maxExecutors", "20")
    
    // ์ถ”์ธก ์‹คํ–‰
    .set("spark.speculation", "true")
    .set("spark.speculation.multiplier", "1.5");

๐ŸŒ ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ์˜ Spark

์š”์ฆ˜์€ ํด๋ผ์šฐ๋“œ์—์„œ Spark๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด. ๊ฐ ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ์˜ ํŠน์ง•์„ ์•Œ์•„๋ณด์ž! โ˜๏ธ

โ˜๏ธ AWS EMR (Elastic MapReduce)

ํŠน์ง•: โ€ข ์™„์ „ ๊ด€๋ฆฌํ˜• Hadoop/Spark ํด๋Ÿฌ์Šคํ„ฐ
โ€ข S3์™€ ๊ธด๋ฐ€ํ•œ ํ†ตํ•ฉ
โ€ข ์ŠคํŒŸ ์ธ์Šคํ„ด์Šค๋กœ ๋น„์šฉ ์ ˆ๊ฐ
โ€ข EMR Studio๋กœ ๋…ธํŠธ๋ถ ํ™˜๊ฒฝ ์ œ๊ณต

์‚ฌ์šฉ ์˜ˆ์‹œ:
spark.read().parquet("s3://my-bucket/data/")

โ˜๏ธ Google Cloud Dataproc

ํŠน์ง•: โ€ข ๋น ๋ฅธ ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ (90์ดˆ ์ด๋‚ด)
โ€ข BigQuery์™€ ํ†ตํ•ฉ
โ€ข ์ดˆ ๋‹จ์œ„ ๊ณผ๊ธˆ
โ€ข ์ž๋™ ์Šค์ผ€์ผ๋ง

์‚ฌ์šฉ ์˜ˆ์‹œ:
spark.read().format("bigquery").load("project.dataset.table")

โ˜๏ธ Azure HDInsight / Databricks

ํŠน์ง•: โ€ข Azure ์„œ๋น„์Šค์™€ ํ†ตํ•ฉ
โ€ข Databricks๋Š” Spark ์ฐฝ์‹œ์ž๊ฐ€ ๋งŒ๋“  ํ”Œ๋žซํผ
โ€ข ํ˜‘์—… ๋…ธํŠธ๋ถ ํ™˜๊ฒฝ
โ€ข Delta Lake ์ง€์›

์‚ฌ์šฉ ์˜ˆ์‹œ:
spark.read().format("delta").load("/mnt/delta/table")

๐ŸŽฏ ๋งˆ์น˜๋ฉฐ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์–ด. ๐ŸŽ‰

์šฐ๋ฆฌ๋Š” Apache Spark์˜ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์‹ค์ „ ํ™œ์šฉ๊นŒ์ง€ ๋ชจ๋“  ๊ฒƒ์„ ๋‹ค๋ค˜์–ด. RDD, DataFrame, SQL, Streaming, MLlib, ๊ทธ๋ฆฌ๊ณ  ์„ฑ๋Šฅ ์ตœ์ ํ™”๊นŒ์ง€... ์ •๋ง ๋งŽ์€ ๋‚ด์šฉ์ด์—ˆ์ง€? ๐Ÿ˜Š

Spark๋Š” ๋น…๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์˜ ์‚ฌ์‹ค์ƒ ํ‘œ์ค€์ด ๋˜์—ˆ์–ด. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋„ ๋Œ€์šฉ๋Ÿ‰ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ฑฐ๋‚˜, ์ถ”์ฒœ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๊ฑฐ๋‚˜, ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ด๋Ÿฐ ๊ธฐ์ˆ ๋“ค์ด ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์–ด.

์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•˜๋‚˜์”ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ์ตํžˆ๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒˆ Spark ๋งˆ์Šคํ„ฐ๊ฐ€ ๋˜์–ด ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿš€

๐Ÿ’ก ํ•™์Šต ๋กœ๋“œ๋งต ์ถ”์ฒœ 1๋‹จ๊ณ„ (1-2์ฃผ): ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ RDD ์ดํ•ด
2๋‹จ๊ณ„ (2-3์ฃผ): DataFrame๊ณผ Spark SQL ๋งˆ์Šคํ„ฐ
3๋‹จ๊ณ„ (2-3์ฃผ): ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋ฒ• ํ•™์Šต
4๋‹จ๊ณ„ (3-4์ฃผ): Streaming๊ณผ MLlib ์‹ค์Šต
5๋‹จ๊ณ„ (๊ณ„์†): ์‹ค์ „ ํ”„๋กœ์ ํŠธ์™€ ๋ฌธ์ œ ํ•ด๊ฒฐ

๊พธ์ค€ํžˆ ์‹ค์Šตํ•˜๊ณ , ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์ฐธ์—ฌํ•˜๊ณ , ์ตœ์‹  ํŠธ๋ Œ๋“œ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋Š” ๊ฒŒ ์ค‘์š”ํ•ด!
๐Ÿ”— ์œ ์šฉํ•œ ๋ฆฌ์†Œ์Šค โ€ข ๊ณต์‹ ๋ฌธ์„œ: spark.apache.org/docs/latest/
โ€ข GitHub: github.com/apache/spark
โ€ข Stack Overflow: Spark ํƒœ๊ทธ๋กœ ์งˆ๋ฌธ/๋‹ต๋ณ€
โ€ข Databricks Blog: ์ตœ์‹  ๊ธฐ์ˆ ๊ณผ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค
โ€ข Spark Summit: ์—ฐ๋ก€ ์ปจํผ๋Ÿฐ์Šค ์˜์ƒ
โ€ข ์žฌ๋Šฅ๋„ท: ๋น…๋ฐ์ดํ„ฐ ์ „๋ฌธ๊ฐ€์™€ ์ง€์‹ ๊ณต์œ  ๊ฐ€๋Šฅ!

Happy Sparking! ๐ŸŽŠ
๋น…๋ฐ์ดํ„ฐ์˜ ์„ธ๊ณ„์—์„œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์„ฑ๊ณต์„ ์‘์›ํ•ฉ๋‹ˆ๋‹ค!

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.