- 들어가며 — 이틀 전에 조용히 뒤집힌 기본값
- 무엇이 바뀌었나 — 설정 세 개
- 얼마나 빨라지나 — 아무도 숫자를 내놓지 않았습니다
- 진짜 이야기는 타입 강제 변환이다
- 225칸 중 132칸
- 가장 위험한 18칸 — 아무것도 실패하지 않는다
- 당신의 UDF는 Arrow가 아닐 수도 있다 — import 순서 함정
- 같이 딸려오는 것들
- 그래서 어떻게 올릴 것인가
- 언제 이 기본값을 끄고 살 것인가
- 마치며
- 참고 자료
들어가며 — 이틀 전에 조용히 뒤집힌 기본값
Apache Spark 4.2.0이 2026년 7월 14일에 릴리스됐습니다. 4.x 라인의 세 번째 릴리스이고, 릴리스 노트의 표현대로라면 1,700개가 넘는 Jira 티켓과 250명 이상의 기여자가 들어간 판입니다. 헤드라인은 화려합니다 — GEOMETRY/GEOGRAPHY 지오스페이셜 타입, SQL CHANGES 절로 하는 CDC, NEAREST BY top-K 조인, Data Source V2 트랜잭션.
그런데 PySpark로 배치를 돌리는 사람에게 실제로 오늘 밤 잠을 설치게 할 항목은 그 목록에서 훨씬 눈에 덜 띄는 한 줄입니다.
[SPARK-54555] Enable Arrow-optimized Python UDFs and Arrow-based PySpark IPC by default
기본값 하나 뒤집은 겁니다. 새 API도, 새 문법도 없습니다. 그런데 이 한 줄이 당신이 3년째 돌리던 Python UDF의 출력 데이터를 바꿀 수 있습니다. 에러 없이, 경고 없이요.
이 글은 그 기본값이 정확히 무엇을 바꾸는지를, Spark 저장소에 실제로 체크인돼 있는 자료로만 따라가 봅니다. 결론부터 말하면, 흥미로운 부분은 속도가 아니라 타입 강제 변환(type coercion)입니다.
무엇이 바뀌었나 — 설정 세 개
먼저 사실 확인부터 하겠습니다. SPARK-54555는 PR #53264로 들어왔고, 커밋 ea0a35e065d3이 2025년 12월 2일에 머지됐습니다. Jira 티켓의 fixVersion은 4.2.0이고, 이 커밋은 v4.2.0 태그에는 포함돼 있지만 v4.1.0에는 없습니다.
바뀐 건 SQLConf.scala의 기본값입니다. branch-4.1과 branch-4.2의 같은 파일을 직접 비교하면 이렇습니다.
설정 4.1 4.2
------------------------------------------------------------
spark.sql.execution.pythonUDF.arrow.enabled false -> true
spark.sql.execution.pythonUDTF.arrow.enabled false -> true
spark.sql.execution.arrow.enabled false -> true
세 번째 항목이 조금 헷갈리는데, 문서와 마이그레이션 가이드가 말하는 spark.sql.execution.arrow.pyspark.enabled는 자체 기본값을 갖지 않고 fallbackConf로 spark.sql.execution.arrow.enabled를 가리킵니다. 그래서 실제 소스에서 뒤집힌 건 후자입니다. 효과는 같습니다 — DataFrame.toPandas와 pandas/NumPy 입력을 받는 SparkSession.createDataFrame이 이제 기본적으로 Arrow 컬럼 전송을 씁니다.
즉 이번 릴리스에서 Python 쪽은 세 갈래로 동시에 Arrow를 탑니다. 일반 Python UDF, Python UDTF, 그리고 JVM과 Python 사이의 컬럼 데이터 교환(IPC).
얼마나 빨라지나 — 아무도 숫자를 내놓지 않았습니다
이 부분은 정직하게 쓰겠습니다.
PR #53264의 "Why are the changes needed?" 항목은 이렇게 말합니다 — Arrow의 컬럼 IPC가 JVM과 Python 사이 처리량을 "significantly improves"하고 직렬화/역직렬화 오버헤드를 줄인다고요. 그런데 그 PR에는 벤치마크 숫자가 하나도 없습니다. Databricks의 Spark 4.2 소개 블로그도 "기존 UDF가 코드 재작성 없이 더 빠른 컬럼 경로를 쓸 수 있다"고만 말하고 수치는 제시하지 않습니다.
Spark 저장소에는 sql/core/benchmarks/UDFBenchmark-results.txt라는 체크인된 벤치마크 결과가 있긴 한데, 열어 보면 Scala UDF의 whole-stage codegen on/off 비교입니다. Arrow 대 pickle Python UDF를 재는 물건이 아닙니다. python/pyspark/sql/tests/pandas/bench_arrow_columnar_udf.py 같은 벤치마크 하네스는 저장소에 있지만, 결과 파일은 체크인돼 있지 않습니다.
그러니 이 글은 "N배 빨라진다"고 말하지 않겠습니다. 그런 숫자는 이번 릴리스와 함께 공개된 적이 없습니다. 방향성은 충분히 납득할 만합니다 — 행 단위 pickle 직렬화 대신 컬럼 배치를 넘기는 게 왜 유리한지는 Catalyst와 Tungsten 편에서 다룬 논리와 같고, Polars가 pandas를 상대로 이긴 이유와도 같은 계열의 이야기입니다. 다만 당신의 UDF가 몇 퍼센트 빨라지는지는 당신이 재야 합니다. 그게 이 릴리스에서 확인 가능한 전부입니다.
그리고 어차피, 이 기본값 변경에서 정말 중요한 건 속도가 아닙니다.
진짜 이야기는 타입 강제 변환이다
Python UDF를 쓸 때 우리는 반환 타입을 선언합니다.
@udf("int")
def f(x):
return some_value
문제는 some_value가 선언한 int와 다른 타입일 때 무슨 일이 벌어지느냐입니다. pickle 방식과 Arrow 방식은 이 지점에서 규칙이 다릅니다. 이건 알려진 사실이고, 심지어 PySpark 소스에 주석으로 박혀 있었습니다. python/pyspark/sql/udf.py의 _create_py_udf 함수 위에 있는 주석입니다.
# Arrow and Pickle have different type coercion rules, so a UDF might have a
# different result with/without Arrow optimization. That's the main reason the
# Arrow optimization for Python UDFs is disabled by default.
재미있는 건 이 주석이 branch-4.2에도 그대로 남아 있다는 점입니다. "그래서 기본적으로 꺼 둔다"고 적혀 있는데, 정작 4.2에서는 켜졌습니다. 주석이 릴리스를 따라오지 못한 셈입니다. 게다가 이 주석은 강제 변환 표가 python/pyspark/sql/tests/udf_type_tests에 있다고 안내하는데, 그 경로는 branch-4.2에 존재하지 않습니다.
표는 옮겨 갔습니다. 실제 위치는 python/pyspark/sql/tests/coercion/이고, 여기엔 CI가 검증하는 골든 파일이 들어 있습니다.
golden_python_udf_return_type_coercion_vanilla.csv <- pickle (4.1 기본)
golden_python_udf_return_type_coercion_with_arrow.csv <- Arrow (4.2 기본)
golden_python_udf_return_type_coercion_with_arrow_and_pandas.csv
세 번째 파일은 4.1 이전의 Arrow 동작(중간에 pandas 변환을 한 번 더 거치던 시절)입니다. spark.sql.legacy.execution.pythonUDF.pandas.conversion.enabled가 4.2에서 false가 기본이므로, 4.2의 살아 있는 표는 두 번째 파일입니다. 참고로 PySpark 공식 문서는 아직 강제 변환 차이를 2023년에 열린 PR #41706이라는 "DO NOT MERGE" 문서용 PR로 안내하고 있는데, 저장소 안의 골든 파일이 더 최신이고 CI가 실제로 지키는 자료입니다.
그래서 이 두 골든 파일을 직접 diff해 봤습니다.
225칸 중 132칸
표는 선언 SQL 타입 15종 × Python 반환값 15종 = 225칸입니다. 두 파일을 칸 단위로 비교하면 132칸이 다릅니다. 절반이 넘습니다. X는 에러를 뜻합니다.
카테고리별로 갈라 보면 이렇게 나옵니다.
분류 칸 수
---------------------------------------------
1. None -> 에러 (조용하던 NULL이 이제 실패) 99
2. None -> 값 (조용하던 NULL이 이제 데이터) 18
3. 값 -> 에러 (되던 게 이제 실패) 2
4. 에러 -> 값 (실패하던 게 이제 값) 7
5. 값 -> 다른 값 (출력 문자열이 달라짐) 6
---------------------------------------------
합계 132
1번(99칸)이 압도적으로 큽니다. pickle Python UDF의 설계는 "선언 타입과 안 맞으면 조용히 None으로 만든다"였습니다. 이게 사라졌습니다. 이제 대부분의 불일치는 예외입니다. 이건 방향으로는 명백히 개선입니다 — 타입 실수를 NULL로 삼켜 버리는 건 데이터 파이프라인에서 가장 고약한 종류의 버그입니다. 하지만 당신의 잘 돌던 잡이 4.2에서 갑자기 죽는다면 십중팔구 이 99칸 중 하나입니다. 그리고 죽는 게 맞습니다. 다만 그걸 릴리스 노트 한 줄로 알게 되는 게 문제죠.
실제 칸 몇 개를 골라 보겠습니다. 값은 골든 파일에 적힌 그대로입니다.
선언 타입 반환한 Python 값 pickle(4.1) Arrow(4.2)
----------------------------------------------------------------------------------------------
int 1.0 (float) None 1
int 1970-01-01 (date) None 0
bigint 1970-01-01 00:00:00 None 0
boolean 1 (int) None True
double 1 (Decimal) None 1.0
int 'a' (str) None X (에러)
tinyint True (bool) None X (에러)
binary 'a' (str) b'a' X (에러)
struct<_1:int> [1] (list) Row(_1=1) X (에러)
date 1 (int) X (에러) datetime.date(1970, 1, 2)
string 1970-01-01 (date) 'java.util.GregorianCalendar[...' '1970-01-01'
string (1,) (tuple) '[Ljava.lang.Object;@<hash>' '(1,)'
string bytearray(b'ABC') '[B@<hash>' "bytearray(b'ABC')"
마지막 세 줄은 순수한 버그 수정입니다. pickle 경로에서 date를 string으로 선언하면 java.util.GregorianCalendar[time=?,...라는 JVM 내부 객체의 toString이 그대로 데이터로 흘러들어 갔습니다. bytearray는 [B@<hash> — Java 바이트 배열의 identity hash입니다. 이런 게 프로덕션 테이블에 들어가 있었다면, 4.2는 그걸 '1970-01-01'로 고쳐 줍니다. 고마운 일이지만, 그 컬럼을 읽는 다운스트림이 있다면 그 값도 같이 바뀝니다.
4번(7칸)도 눈여겨볼 만합니다. date를 선언하고 정수 1을 반환하면 pickle에서는 에러였는데, Arrow에서는 1970-01-02가 됩니다. epoch + 1일이죠. 논리적으로 일관되지만, "에러로 걸러지던 실수가 이제 그럴듯한 날짜가 되어 통과한다"는 뜻이기도 합니다.
가장 위험한 18칸 — 아무것도 실패하지 않는다
99칸은 시끄럽게 실패합니다. 시끄러운 건 관리할 수 있습니다. 배포하고, 터지고, 롤백하거나 고치면 됩니다.
문제는 2번 카테고리, 18칸입니다. 여기서는 아무 일도 일어나지 않습니다. 예외도 없고, 경고도 없고, 로그도 없습니다. 그냥 어제 NULL이던 칸에 오늘 값이 들어 있습니다.
전부 나열하면 이렇습니다.
선언 반환값 pickle -> Arrow
--------------------------------------------------
boolean 1 (int) None -> True
boolean 1.0 (float) None -> True
tinyint 1.0 (float) None -> 1
tinyint 1 (Decimal) None -> 1
smallint 1.0 (float) None -> 1
smallint 1 (Decimal) None -> 1
int 1970-01-01 (date) None -> 0
int 1.0 (float) None -> 1
int 1 (Decimal) None -> 1
bigint 1970-01-01 00:00:00 None -> 0
bigint 1.0 (float) None -> 1
bigint 1 (Decimal) None -> 1
float True (bool) None -> 1.0
float 1 (int) None -> 1.0
float 1 (Decimal) None -> 1.0
double True (bool) None -> 1.0
double 1 (int) None -> 1.0
double 1 (Decimal) None -> 1.0
이 목록을 보면 패턴이 보입니다. 대부분 "숫자 타입을 선언했는데 다른 숫자 타입을 반환한 경우"입니다. 그리고 이건 실무에서 실수하기 가장 쉬운 조합입니다. @udf("int")를 달아 놓고 안에서 나눗셈을 하면 Python은 float를 반환합니다. @udf("double")을 달아 놓고 Decimal을 반환하는 것도 흔합니다.
4.1까지 이런 UDF는 전부 NULL 컬럼을 뱉고 있었습니다. 아무도 눈치채지 못했다면, 그 NULL은 이미 다운스트림에 녹아 있습니다. COUNT(*), WHERE x IS NULL 필터, AVG 집계, 조인 키, 대시보드 숫자 — 전부요. 4.2로 올리는 순간 그 칸들이 실제 값으로 채워지고, 그 모든 게 조용히 움직입니다.
이건 회귀가 아닙니다. 4.2 쪽이 맞습니다. int를 선언하고 1.0을 반환했으면 1이 나오는 게 옳지 NULL이 옳을 리 없습니다. 하지만 "고쳐지는 것"과 "출력이 바뀌는 것"은 운영에서 같은 사건이고, 후자로 취급하지 않으면 다칩니다. 특히 재처리(backfill)를 돌릴 때 과거 배치와 신규 배치의 결과가 달라지는 형태로요.
당신의 UDF는 Arrow가 아닐 수도 있다 — import 순서 함정
여기서 정말 얄궂은 부분이 나옵니다. 이 기본값이 균일하게 적용되지 않습니다.
python/pyspark/sql/udf.py의 _create_py_udf를 보면 이렇게 판단합니다.
is_arrow_enabled = False
if useArrow is None:
from pyspark.sql import SparkSession
session = SparkSession._instantiatedSession
is_arrow_enabled = (
False
if session is None
else session.conf.get("spark.sql.execution.pythonUDF.arrow.enabled") == "true"
)
else:
is_arrow_enabled = useArrow
SparkSession._instantiatedSession이 None이면 설정을 읽어 보지도 않고 False입니다. 그리고 이 판단은 UDF를 호출할 때가 아니라 UDF 객체를 만들 때 한 번 일어나서 eval type으로 굳습니다.
무슨 뜻이냐면, 모듈 최상단에 이렇게 써 둔 UDF는
# udfs.py — import 시점에 데코레이터가 평가된다
from pyspark.sql.functions import udf
@udf("int")
def parse_score(s):
return float(s) / 2
SparkSession.builder.getOrCreate()보다 먼저 import되면 세션이 아직 없으므로 pickle UDF로 굳습니다. 4.2에서도요. 같은 코드를 세션 생성 이후에 import하면 Arrow UDF가 됩니다.
즉 한 잡 안에서 UDF마다 강제 변환 규칙이 다를 수 있습니다. import 순서에 따라서요. 위 예시는 int를 선언하고 float를 반환하니 정확히 그 18칸 중 하나(int + 1.0(float))에 해당합니다 — import 순서에 따라 NULL이 나오거나 값이 나옵니다.
이 로직 자체는 새것이 아닙니다. branch-4.1의 udf.py에도 글자 그대로 같은 코드가 있습니다. 다만 4.1에서는 설정 기본값이 어차피 false라서 session is None 분기가 같은 답을 냈을 뿐입니다. 4.2에서 기본값이 뒤집히면서, 이 분기는 이제 새 기본값을 조용히 무효화하는 경로가 됐습니다.
비슷한 조용한 이탈이 하나 더 있습니다. 같은 함수에서 PyArrow나 pandas가 없으면 이렇게 처리합니다.
except ImportError:
is_arrow_enabled = False
warnings.warn(
"Arrow optimization failed to enable because PyArrow or Pandas is not installed. "
"Falling back to a non-Arrow-optimized UDF.",
RuntimeWarning,
)
RuntimeWarning 하나 띄우고 pickle로 돌아갑니다. 드라이버 이미지에는 PyArrow가 있고 다른 환경에는 없다면, 같은 코드가 환경에 따라 다른 강제 변환 규칙으로 도는 셈입니다. 참고로 4.2는 PyArrow 최소 버전을 15.0.0에서 18.0.0으로 올렸습니다. pandas 최소는 2.2.0으로 그대로입니다.
한 가지 더 짚자면, 이 폴백은 UDF에는 없습니다. spark.sql.execution.arrow.pyspark.fallback.enabled(기본 true)라는 설정이 있어서 "Arrow가 에러 나면 비최적화 구현으로 자동 폴백"을 해 주는데, 설정 문서를 읽어 보면 이건 spark.sql.execution.arrow.pyspark.enabled가 켜는 최적화, 즉 toPandas와 createDataFrame 경로에만 적용됩니다. UDF 실행 중에 발생한 강제 변환 에러는 폴백되지 않고 그냥 잡을 죽입니다.
같이 딸려오는 것들
4.2 마이그레이션 가이드에는 Python 쪽 변경이 몇 개 더 있습니다. Arrow 기본값과 별개지만 같은 업그레이드에 묶여 오는 것들입니다.
- pandas UDF의 nullable 정수 컬럼. 배치에 null이 섞인 nullable 정수 컬럼이 이제
float64가 아니라 pandas의 nullable 정수 확장 dtype(Int8/Int16/Int32/Int64)으로 전달됩니다.float64입력을 가정하고 짠 UDF 코드는 손봐야 합니다. - PyPy 지원 중단. 4.2부터 PyPy는 공식 지원이 아닙니다. CPython을 쓰라고 안내합니다.
- NumPy ndarray로 createDataFrame. 이제 pandas가 아니라 PyArrow를 요구하고, pandas를 거치지 않고 Arrow로 직접 변환합니다. Arrow를 끈 채 NumPy dtype 기반 스키마 추론에 의존했다면 추론된 스키마를 다시 확인하라고 가이드가 명시적으로 경고합니다.
- Python Data Source 스키마 불일치. 반환한 Arrow 데이터의 컬럼 타입이 선언 스키마와 다르면
DATA_SOURCE_RETURN_SCHEMA_MISMATCH로 실패합니다. 이전에는 컬럼 개수와 이름 불일치만 이 에러를 냈습니다.
Arrow 배치 크기는 spark.sql.execution.arrow.maxRecordsPerBatch로 조절하고 기본값은 10000입니다. UDF 안에서 행당 메모리를 많이 쓴다면 이제 이 값이 executor 메모리 프로파일에 영향을 줍니다 — 예전엔 행 단위로 흘러가던 게 이제 배치로 뭉쳐 오니까요.
그래서 어떻게 올릴 것인가
정리하면 이렇게 하는 게 합리적입니다.
1. 먼저 끄고 올리세요. 4.2로 올라가는 것과 Arrow UDF로 갈아타는 것은 별개의 변경입니다. 한 번에 하지 마세요.
spark.sql.execution.pythonUDF.arrow.enabled false
spark.sql.execution.pythonUDTF.arrow.enabled false
spark.sql.execution.arrow.pyspark.enabled false
이러면 4.2의 나머지(지오스페이셜, CDC, DSv2 트랜잭션, Java 25)를 4.1과 동일한 Python 의미론으로 받을 수 있습니다.
2. 그다음 UDF 인벤토리를 만드세요. 실제로 위험한 건 "선언 타입과 반환 타입이 다른 UDF"뿐입니다. 둘이 일치하면 132칸은 당신과 무관합니다. 골든 테이블의 대각선(선언 타입 = 반환 타입)은 두 표가 동일합니다. 그러니 질문은 하나입니다 — 내 UDF는 선언한 대로 반환하고 있나?
이건 눈으로 읽어서 알 수 없습니다. 숫자 타입 UDF 안의 나눗셈 하나, Decimal 하나면 끝나니까요. 소규모 샘플에 대해 양쪽 설정으로 같은 잡을 돌리고 출력을 비교하는 게 유일하게 믿을 만한 방법입니다.
3. 비교할 때 NULL 개수를 보세요. 18칸 카테고리는 조용하므로, 에러 로그를 봐서는 절대 안 잡힙니다. 컬럼별 NULL 카운트가 줄었다면 그게 바로 그 18칸입니다. 줄어든 게 반가운 상황일 수도 있지만, 그건 당신이 판단할 일이지 릴리스가 대신 판단할 일이 아닙니다.
4. import 순서를 고정하세요. 앞의 함정 때문에, "설정을 켰다"는 것만으로는 Arrow가 켜졌다고 확신할 수 없습니다. 확실하게 하려면 udf(..., useArrow=True)로 명시하면 됩니다. useArrow가 명시되면 세션 유무와 설정을 모두 무시하고 그 값을 씁니다. 반대로 확실히 pickle로 남기고 싶은 UDF는 useArrow=False로 못 박으세요. 애매하게 기본값에 맡기는 게 가장 나쁩니다.
언제 이 기본값을 끄고 살 것인가
솔직히, 계속 꺼 두고 사는 게 맞는 경우가 있습니다.
- UDF가 몇 개 없고 전부 가벼운 경우. Arrow 이득은 JVM과 Python 사이를 오가는 데이터가 많을 때 나옵니다. 하루 몇만 행 처리하는 잡이면 재검증 비용이 이득보다 큽니다.
- 선언 타입과 반환 타입이 어긋난 UDF가 많은데, 지금 그걸 다 감사할 여력이 없는 경우. 132칸이 당신 얘기입니다. 끄고, 일정을 잡고, 나중에 하세요.
- 출력이 계약으로 묶여 있는 경우. 다운스트림이 스키마뿐 아니라 값의 분포까지 기대하고 있다면(예: 규제 리포트, 재무 마감 배치), 조용한 NULL 감소는 그 자체로 사고입니다. 먼저 합의하고 켜세요.
반대로 지금 바로 켤 만한 경우는 이렇습니다.
- UDF가 무겁고 데이터가 많다. 그러면 컬럼 경로가 값을 할 가능성이 높습니다 — 다만 앞서 말했듯 숫자는 당신이 직접 재야 합니다.
- 선언 타입과 반환 타입이 일치한다는 걸 테스트로 보증할 수 있다. 그러면 132칸은 무관하고, 그냥 공짜입니다.
- 애초에 UDF를 쓰지 않는 게 최선인 경우도 잊지 마세요. 내장 함수나 SQL로 표현되는 로직을 Python UDF로 짜고 있었다면, Arrow로 빨라지는 것보다 UDF를 없애는 게 훨씬 큰 이득입니다. 4.2는
time_bucket을 포함해 내장 함수를 또 늘렸습니다.
마치며
Spark 4.2.0의 Python 이야기는 릴리스 노트 한 줄로는 전달되지 않습니다. "Arrow-optimized Python UDFs enabled by default"는 성능 항목처럼 읽히지만, 저장소에 체크인된 골든 테이블을 실제로 열어 보면 225칸 중 132칸의 의미론이 바뀝니다. 그중 99칸은 조용하던 NULL이 이제 에러가 되고, 18칸은 아무 신호 없이 NULL이 값으로 바뀝니다.
Apache 마이그레이션 가이드는 이걸 정직하게 적어 뒀습니다 — 설정 이름과 되돌리는 법까지요. 반면 벤더 블로그는 "코드 재작성 없이 더 빠른 컬럼 경로"라고만 소개하고 강제 변환 이야기는 꺼내지 않습니다. 둘 다 거짓말은 아니지만, 업그레이드 계획을 세울 때 읽어야 할 건 전자입니다.
그리고 이 변경의 밑바닥에 있는 판단 자체는 옳다고 봅니다. "타입이 안 맞으면 조용히 NULL"은 애초에 방어할 수 없는 기본값이었습니다. 다만 그 잘못된 기본값 위에서 3년쯤 돌아간 파이프라인들이 있고, 그것들에게 이 수정은 수정이 아니라 출력 변경으로 도착합니다. 끄고 올린 다음, 재세요. 그다음에 켜세요. 순서가 전부입니다.
참고 자료
- Spark Release 4.2.0 — 공식 릴리스 노트 (2026-07-14)
- Apache Spark News — 릴리스 날짜 확인
- SPARK-54555 — Enable Arrow-optimized Python UDFs by default (Jira)
- PR #53264 — SPARK-54555 구현 PR
- Upgrading PySpark 4.1 → 4.2 — 공식 마이그레이션 가이드 (branch-4.2)
- 강제 변환 골든 파일 — python/pyspark/sql/tests/coercion (branch-4.2)
- udf.py —
_create_py_udf의 세션/Arrow 판단 로직 (branch-4.2) - SQLConf.scala — 설정 기본값 원본 (branch-4.2)
- Introducing Apache Spark 4.2 — Databricks (벤더 발표)
- Apache Arrow Python UDFs — PySpark 공식 문서
현재 단락 (1/155)
[Apache Spark 4.2.0](https://spark.apache.org/releases/spark-release-4-2-0.html)이 2026년 7월 14일에 릴리스됐...