파일 분할 및 버킷은 Spark SQL에서 일반적인 최적화 기술입니다. 파일이나 디렉토리에서 데이터를 미리 집계하여 데이터 왜곡 및 데이터 축소를 줄이는 데 도움이 될 수 있습니다. DataFrames는 영구 테이블로 저장할 때 정렬, 분할 및/또는 버킷화할 수 있습니다. 분할은 지정된 열에 따라 디렉토리 계층 구조에 파일을 저장하여 읽기를 최적화합니다. 예를 들어 DataFrame을 1년 단위로 분할하는 경우:
df.write.format("parquet")
.partitionBy("year")
.option("path", "/data ")
.saveAsTable("taxi")
디렉토리는 다음 구조를 가집니다.
After partitioning the data, when queries are made with filter operators on the partition column, the Spark SQL catalyst optimizer pushes down the partition filter to the datasource. The scan reads only the directories that match the partition filters, reducing disk I/O and data loaded into memory. For example, the following query reads only the files in the year = '2019' 디렉토리.
df.filter("year = '2019')
.groupBy("year").avg("fareamount")
When visualizing the physical plan for this query, you will see Scan PrunedInMemoryFileIndex[ /data/year=2019], PartitionFilters: [ (year = 2019)] .
분할과 마찬가지로 버킷화는 데이터를 값별로 분할합니다. 그러나 버킷화는 버킷 값의 해시를 통해 고정된 수의 버킷에 데이터를 배포하는 반면 분할은 각 파티션 열 값에 대한 디렉토리를 만듭니다. 테이블은 두 개 이상의 값으로 버킷화할 수 있으며 분할 여부에 관계없이 버킷화를 사용할 수 있습니다. 이전 예제에 버킷화를 추가하는 경우 디렉토리 구조는 이전과 동일하며, 연도 디렉토리의 데이터 파일은 4개의 버킷에 시간별로 그룹화됩니다.
df.write.format("parquet")
.partitionBy("year")
.bucketBy(4,"hour")
.option("path", "/data ")
.saveAsTable("taxi")
데이터를 버킷화한 후 버킷 값의 집계 및 조인(와이드 변환)은 파티션 간에 데이터를 셔플하여 네트워크와 디스크 I/O를 줄입니다. 또한 버킷 필터 가지치기가 데이터 소스로 푸시되어 디스크 I/O를 줄이고 메모리에 로드되는 데이터를 줄일 수 있습니다. 다음 쿼리는 연도의 파티션 필터를 데이터 원본으로 푸시다운하며 시간으로 집계하기 위한 셔플을 방지합니다.
df.filter("year = '2019')
.groupBy("hour")
.avg("hour")
분할은 필터링을 위해 쿼리에서 자주 사용되는 열과 디렉토리에서 파일을 배포할 수 있는 충분한 해당 데이터를 가진 열 값의 개수가 제한된 열에서만 사용해야 합니다. 작은 파일은 과도한 병렬이 있는 경우 덜 효율적이며 큰 파일이 너무 적으면 병렬성을 해칠 수 있습니다. 버킷화는 고유한 버킷화 열 값의 개수가 많을 때 잘 작동하며 버킷 열은 쿼리에서 자주 사용됩니다.