← 학습 카테고리

Learn

Spark

38개 모듈 · 현재 2번째

Spark 모듈 2/38 spark-learn-02

SparkSession, DataFrame, 파티션

Spark: The Definitive Guide (Excerpts, Databricks Preview, 2017) — Chapters 2-6 — Bill Chambers & Matei Zaharia Chapter 2: A Gentle Introduction to Spark — Starting Spark, SparkSession, DataFrames, Partitions (pp.7-9)

이 모듈을 다 읽으면

  • SparkSession이 드라이버 프로세스와 어떤 관계인지 설명할 수 있다
  • spark-shell/pyspark와 spark-submit의 차이를 구분해 설명할 수 있다
  • DataFrame과 로컬 언어의 DataFrame(pandas 등)의 근본적 차이를 설명할 수 있다
  • 파티션 개수와 익스큐터 개수가 병렬성에 미치는 관계를 판단할 수 있다

Spark를 인터랙티브하게 시작하면(spark-shell 또는 pyspark) 드라이버 프로세스가 사용자에게 SparkSession이라는 변수로 드러나며, 이후 모든 사용자 정의 조작은 이 SparkSession을 통해 클러스터 전역에 실행된다. DataFrame은 스프레드시트처럼 행과 열, 스키마를 갖춘 표 형태의 데이터지만 한 대의 머신이 아니라 수천 대의 컴퓨터에 걸쳐 존재할 수 있다는 점이 근본적으로 다르며, 이는 데이터가 너무 크거나 한 머신에서 계산하기에는 시간이 너무 오래 걸리기 때문이다. Spark는 이 분산 데이터를 병렬로 처리하기 위해 파티션이라는 단위로 데이터를 쪼개며, 파티션 수와 익스큐터 수 중 더 작은 쪽이 실질적인 병렬성을 결정한다.

Spark 시작하기와 SparkSession

로컬 모드로 Spark를 시작하려면 Scala 콘솔은 ./bin/spark-shell, Python 콘솔은 ./bin/pyspark를 실행하면 된다. 이 방식은 인터랙티브한 Spark 애플리케이션을 시작하는 방법이다. 이와 별개로 spark-submit이라는 제출 방식도 있는데, 이는 사용자 콘솔 없이 미리 작성된 코드를 클러스터에서 하나의 독립된 애플리케이션으로 실행하는 방식이다.

Spark를 인터랙티브 모드로 시작하면 그 즉시 SparkSession이 암묵적으로 생성되어 해당 Spark 애플리케이션을 관리한다. 드라이버 프로세스는 사용자에게 SparkSession이라는 형태로 나타나며, 사용자가 정의한 조작을 클러스터 전역에 실행시키는 창구 역할을 한다. Scala와 Python 콘솔에서는 SparkSession이 `spark`라는 변수로 곧바로 사용 가능하다.

핵심 포인트

  • spark-shell(Scala)과 pyspark(Python)은 인터랙티브 콘솔을 여는 방식, spark-submit은 콘솔 없이 준비된 코드를 클러스터에 애플리케이션으로 제출하는 방식이다
  • SparkSession은 드라이버 프로세스가 사용자에게 드러나는 형태이며, 인터랙티브 모드 시작 시 암묵적으로 생성된다
  • Scala/Python 콘솔에서는 SparkSession이 spark 변수로 바로 제공된다

첫 DataFrame 만들기와 DataFrame 개념

SparkSession을 이용해 숫자 범위를 담은 간단한 DataFrame을 만들어볼 수 있다.

val myRange = spark.range(1000).toDF("number")
myRange = spark.range(1000).toDF("number")

이 코드는 0부터 999까지의 값을 가진 1000개 행, 1개 열짜리 DataFrame을 만든다. 클러스터에서 실행되면 이 숫자 범위의 각 부분이 서로 다른 익스큐터에 나뉘어 존재하게 되는데, 이렇게 나뉘어 존재하는 컬렉션이 바로 Spark가 정의하는 DataFrame이다.

DataFrame은 행과 열로 이루어진 표 형태의 데이터이며, 컬럼 목록과 각 컬럼의 타입을 스키마라 부른다. 스프레드시트와 비슷하다고 볼 수 있지만, 결정적인 차이는 스프레드시트가 한 대의 컴퓨터 한 위치에 존재하는 반면 Spark DataFrame은 수천 대의 컴퓨터에 걸쳐 존재할 수 있다는 점이다. 데이터를 여러 머신에 나눠 두는 이유는 데이터가 너무 커서 한 머신에 담을 수 없거나, 한 머신에서 계산하기에는 시간이 지나치게 오래 걸리기 때문이다.

DataFrame이라는 개념 자체는 Spark만의 것이 아니라 R과 Python에도 비슷한 개념이 있다. 다만 (일부 예외를 제외하면) Python/R의 DataFrame은 여러 머신이 아니라 한 대의 머신에만 존재하기 때문에, 그 머신이 가진 자원만큼만 활용할 수 있다는 한계가 있다. Spark는 Python과 R 양쪽에 언어 인터페이스를 제공하므로 Spark DataFrame을 pandas(Python) DataFrame으로 손쉽게 변환할 수도 있다.

Spark는 DataFrame 외에도 Dataset, SQL 테이블, RDD(Resilient Distributed Dataset)라는 핵심 추상화들을 갖고 있다. 이들은 모두 분산 데이터 컬렉션을 표현하지만 데이터를 다루는 인터페이스가 서로 다르다. 그중 가장 쉽고 효율적이며 모든 언어에서 사용 가능한 것이 DataFrame이다.

핵심 포인트

  • spark.range(1000).toDF("number")는 0~999 값을 가진 1000행 1열 DataFrame을 만들며, 클러스터에서는 이 데이터가 여러 익스큐터에 나뉘어 존재한다
  • DataFrame은 스프레드시트와 비슷한 표 형태 데이터지만, 한 머신이 아니라 수천 대의 컴퓨터에 걸쳐 분산 존재할 수 있다는 점이 근본적으로 다르다
  • Python/R의 로컬 DataFrame(pandas 등)은 한 머신에만 존재해 그 머신의 자원 한계에 묶이지만, Spark DataFrame과는 상호 변환이 가능하다
  • Spark의 핵심 분산 데이터 추상화는 Dataset, DataFrame, SQL 테이블, RDD 네 가지이며 이 중 DataFrame이 가장 쉽고 효율적이다

파티션

모든 익스큐터가 병렬로 작업을 수행할 수 있으려면, Spark는 데이터를 파티션이라 불리는 덩어리로 쪼개야 한다. 파티션은 클러스터의 한 물리 머신에 위치한 로우(row)들의 집합이다. DataFrame의 파티션들은 실행 시점에 데이터가 클러스터 전체에 물리적으로 어떻게 분산되어 있는지를 나타낸다.

파티션 수와 병렬성의 관계는 다음과 같다: 파티션이 1개뿐이라면 익스큐터가 수천 개 있어도 Spark는 병렬성 1로만 동작한다. 반대로 파티션이 아무리 많아도 익스큐터가 1개뿐이라면, 사용 가능한 연산 자원이 하나뿐이므로 이 역시 병렬성 1로만 동작한다. 즉 실질적인 병렬성은 파티션 수와 익스큐터(연산 자원) 수 중 더 제약이 되는 쪽에 의해 결정된다.

중요한 점은, DataFrame을 다룰 때 우리는 (대체로) 파티션을 개별적으로 조작하지 않는다는 것이다. 물리적 파티션에 대한 고수준 트랜스포메이션을 지정하기만 하면, Spark가 이 작업을 클러스터에서 실제로 어떻게 실행할지 알아서 결정한다. 파티션을 더 낮은 수준에서 직접 다루는 API도 존재하는데, 이는 RDD 인터페이스를 통해서다.

핵심 포인트

  • 파티션은 클러스터의 한 물리 머신에 위치한 로우들의 집합이며, DataFrame의 물리적 분산 상태를 나타낸다
  • 파티션이 1개면 익스큐터가 아무리 많아도 병렬성은 1이고, 익스큐터가 1개면 파티션이 아무리 많아도 병렬성은 1이다 — 실질 병렬성은 둘 중 더 제약이 되는 쪽에 좌우된다
  • DataFrame API에서는 파티션을 개별적으로 다루지 않고 고수준 트랜스포메이션만 지정하며, Spark가 실제 실행 방식을 결정한다(파티션을 직접 다루려면 RDD 인터페이스를 쓴다)