Привет, Хабр! Я Станислав Габдулгазиев, архитектор департамента поддержки продаж Arenadata. В этой статье рассмотрим, как большое количество мелких файлов влияет на производительность различных систем хранения, таких как HDFS и объектные хранилища с S3 API.

Разберём, какие технологии хранения лучше всего подходят для работы с мелкими файлами в архитектурах Data Lake и Lakehouse. Сравним производительность HDFS и объектных хранилищ с S3 API. На конкретных тестах покажем, почему именно HDFS эффективнее справляется с большим количеством небольших файлов. Обсудим также случаи, когда мелкие файлы становятся не просто нежелательной ситуацией, а неизбежной необходимостью, например в подходах типа Change Data Capture (CDC).

Хранение в Lakehouse

Часто звучит мнение, что для хранения датасетов с большим количеством мелких файлов лучше всего подходят объектные хранилища с S3 API. На практике это не всегда так. Реальные тесты показывают, что системы типа HDFS и Ozone с интерфейсом OFS в таких сценариях работают эффективнее и стабильнее.

Несмотря на ограничение HDFS по количеству файлов, связанное с хранением метаданных Name Node в памяти и особенностями работы Java GC, эта граница не является абсолютно жёсткой и может варьироваться в зависимости от настроек и сценариев использования, часто достигая сотен миллионов. Важно понимать, что это, скорее, практический предел, после превышения которого начинается существенная деградация производительности. Существуют механизмы федерализации для масштабирования, и наличие большого количества маленьких файлов всегда негативно сказывается на работе системы, поэтому их количество и время жизни должны быть строго ограничены. В связи с этим возникают ключевые вопросы: какой размер считать «маленьким» файлом и насколько критично размер файлов влияет на общую производительность Lakehouse?

Неожиданные показатели нагрузочного теста

Делаем небольшой «разведочный тест», а для имитации нагрузки будем использовать стандартный тест для оценки производительности систем TPC-DS. В качестве систем хранения возьмём HDFS, MinIO и Ozone. При этом для Ozone будем применять file system optimized (FSO) бакеты и доступ к ним по двум API OFS и S3 API.

Итак, что я сделал по шагам:

1. Сгенерировал два датасета TPC-DS 100, сохранил их в orc-формате в двух базах данных, назовём их условно tpcds-100 и tpcds-100-small. Для удобства можно использовать эти базы c датасетом 100 Гб. Они отличаются только количеством файлов, в которых лежат данные. В первой все данные лежат в 167 файлах, а во второй — в 14 тысячах.  Получается, что средний объём файла в первой 133,6 Мб, а во второй 2,3 Мб.

hdfs dfs -count -h /apps/hive/hdd/*

25          167             21.8 G /…/[каталог с tpcds-100].db

25       14.0 K             31.2 G /…/[каталог с tpcds-100-small].db

2. Скопируем их на MinIO и Ozone, накинем внешние таблицы. Подсчитаем статистику.

3. Для нагрузки возьмём 72 тестовых запроса, которые исполняются в Impala. Будем использовать однопоточный режим, это несколько отличается от методики TPC. Необходимо оценить деградацию при работе с файлами, отличающимися от эталонного значения в меньшую сторону. Кстати, я призываю вас не сравнивать абсолютные значения производительности HDFS, MinIO и Ozone. Во-первых, аппаратное обеспечение отличалось, хоть и немного, во-вторых, это не цель данной статьи.

Ниже в таблице коротко представлены характеристики используемого тестового стенда.

Параметр

Значение

Примечание

Коэффициент масштабирования

100

100 Гб

Формат оптимизации

 

 

orc

 

Тестируемый сервис

 

ADH.HDFS 3.3.6_arenadata1

ADH.Ozone 1.4.1_arenadata1

MINIO RELEASE.2025-02-07

ADH.Impala 4.4.0_arenadata2

 

 

 Impala mem_limit = 24G, 12 vcore 3 узла обработки

 

Запускаем нагрузочные запросы, результаты представлены в таблице ниже.

Результаты теста

 

Среднее время выполнения, сек большие файлы / маленькие файлы

Среднее время выполнения, сек большие файлы / маленькие файлы

Среднее время выполнения, сек большие файлы / маленькие файлы

Среднее время выполнения, сек большие файлы / маленькие файлы

Деградация в %

Impala.HDFS

Impala.Ozone OFS

Impala.MinIO

Impala.Ozone S3

HDFS

.Ozone OFS

MinIO

Ozone S3

query-1

1,5

3,0

1,91

5,8

2,6

9,6

3,53

8,779

99%

207%

271%

149%

query-2

3,9

24,3

2,83

15,1

8,5

113,5

11,98

75,52

531%

435%

1228%

530%

query-3

4,5

36,8

2,56

17,3

6,6

75,4

10,68

62,11

715%

578%

1043%

482%

query-4

30,4

42,5

26,64

58,1

60,3

582,9

85,73

405,1

40%

118%

867%

372%

query-6

2,1

5,8

2,63

11,3

5,5

65,9

8,714

55,7

179%

330%

1089%

539%

query-7

0,7

2,5

0,84

0,6

0,5

0,6

0,516

0,574

247%

−25%

16%

11%

query-8

1,5

2,8

2,14

11,8

5,3

75,0

8,456

65,07

84%

450%

1307%

670%

query-9

9,0

39,7

9,08

55,9

60,7

1137,0

84,71

731,6

339%

515%

1772%

764%

query-11

13,9

20,8

12,19

32,6

21,6

255,8

30,85

190,9

49%

168%

1082%

519%

query-13

2,4

3,1

2,54

0,7

0,7

0,8

2,314

0,7

30%

−71%

11%

−70%

query-15

1,6

4,2

1,80

8,0

4,0

51,5

5,004

41,05

161%

341%

1181%

720%

query-17

4,1

10,4

4,49

23,8

12,9

168,9

18,82

135,4

154%

431%

1209%

619%

query-18

1,1

3,7

1,07

0,9

1,2

1,4

1,121

1,049

235%

-18%

18%

−6%

query-19

2,5

6,3

2,99

14,3

8,6

109,3

13,93

87,41

149%

378%

1174%

528%

query-22

24,6

25,3

23,53

23,7

24,6

24,4

25,49

24,74

2%

1%

−1%

−3%

query-25

3,9

9,6

5,03

23,6

15,0

163,5

23,34

138,2

144%

369%

993%

492%

query-26

0,7

2,5

0,52

0,5

0,5

0,6

0,513

0,553

252%

−4%

18%

8%

query-27

0,8

2,8

0,87

0,5

0,6

0,6

0,808

0,559

241%

−41%

0%

−31%

query-28

9,1

24,6

8,65

44,8

56,1

893,4

83,02

588,9

170%

418%

1493%

609%

query-29

3,5

9,5

4,29

21,8

12,2

164,2

19,37

132,9

175%

408%

1248%

586%

query-30

1,1

1,4

1,23

1,5

1,7

7,1

2,29

5,242

31%

20%

317%

129%

query-31

5,8

11,3

5,72

22,3

20,5

295,6

30,26

204,1

96%

289%

1342%

575%

query-33

1,8

3,8

1,83

10,3

7,0

84,7

10,79

62,65

114%

462%

1107%

480%

query-34

0,7

2,7

0,80

0,4

0,5

0,5

0,717

0,505

279%

−48%

18%

−30%

query-38

4,0

11,1

4,02

16,7

5,3

98,3

6,218

76,55

175%

315%

1746%

1131%

query-41

0,6

1,4

0,63

0,6

0,4

0,6

0,716

0,595

137%

−8%

24%

−17%

query-42

1,7

3,9

2,33

10,6

7,2

75,2

11,96

61,44

130%

357%

948%

414%

query-43

2,9

5,2

3,33

12,1

4,9

81,9

7,623

65,72

79%

265%

1578%

762%

query-44

3,8

10,8

3,60

25,1

27,2

428,6

41,14

289,9

181%

598%

1476%

605%

query-45

1,5

4,4

1,66

5,3

3,3

30,8

4,412

24,61

185%

217%

841%

458%

query-46

3,5

8,9

4,07

18,7

10,7

162,1

14,22

125,6

154%

359%

1417%

783%

query-47

15,2

21,1

13,29

25,1

23,1

247,3

33,63

169,1

39%

89%

973%

403%

query-48

1,5

2,7

1,61

0,6

0,6

0,6

1,538

0,571

81%

−61%

9%

−63%

query-49

4,1

11,9

4,42

30,6

26,7

356,6

40,07

266,8

187%

592%

1233%

566%

query-50

2,5

6,6

3,21

15,0

7,5

99,5

11,16

83,14

171%

366%

1233%

645%

query-51

9,2

11,5

9,57

21,9

15,0

151,1

20,99

116,5

25%

129%

904%

455%

query-52

1,7

3,9

1,85

10,8

7,0

72,9

11,97

61,95

125%

485%

946%

418%

query-53

0,9

0,8

0,93

0,3

0,2

0,3

1,003

0,26

−8%

−64%

38%

−74%

query-54

0,8

1,2

0,77

0,4

0,4

0,6

0,867

0,433

48%

−43%

24%

−50%

query-55

1,7

4,0

2,21

10,6

7,0

72,5

11,91

61,2

141%

378%

928%

414%

query-56

2,3

4,5

2,65

14,0

10,3

117,3

16,4

91,43

94%

426%

1039%

458%

query-57

7,7

12,3

7,82

14,3

14,2

148,3

18,68

99,72

60%

82%

947%

434%

query-58

4,9

7,9

5,42

16,1

13,1

133,3

21,04

99,61

61%

196%

921%

373%

query-59

3,0

6,1

3,10

14,1

8,9

148,4

12,89

103,9

102%

354%

1563%

707%

query-60

2,3

4,4

2,52

14,1

10,4

116,5

16,34

91,55

97%

460%

1023%

460%

query-61

1,7

6,0

1,59

1,2

1,3

1,5

2,06

1,325

259%

−26%

10%

−36%

query-62

2,7

4,5

2,78

5,5

3,3

34,8

4,247

26,83

69%

97%

950%

532%

query-63

0,9

0,6

0,94

0,2

0,4

0,3

0,907

0,261

−32%

−74%

−21%

−71%

query-64

8,7

12,1

8,05

14,0

14,5

91,7

19,62

69,9

39%

74%

531%

256%

query-65

7,3

12,8

7,07

18,9

16,2

168,0

24,35

121,4

74%

167%

939%

399%

query-66

1,4

4,9

1,49

0,9

0,9

1,0

1,324

0,855

262%

−41%

11%

−35%

query-67

106,6

106,5

#####

106,7

106,7

185,1

104,5

156,6

0%

6%

73%

50%

query-68

4,3

9,8

5,03

20,7

15,4

184,8

21,59

142,4

125%

312%

1101%

560%

query-69

2,2

7,3

2,33

12,7

5,5

100,2

6,095

75,19

226%

444%

1730%

1134%

query-71

1,0

4,0

0,92

0,7

1,0

0,8

0,975

0,716

283%

−19%

−18%

−27%

query-73

0,9

2,6

0,62

0,5

0,5

0,6

0,598

0,563

188%

−27%

19%

−6%

query-74

11,1

50,2

7,97

24,7

15,1

198,5

21,45

144,5

352%

210%

1211%

574%

query-75

3,1

4,5

2,88

3,6

6,2

21,2

6,167

15,77

45%

24%

244%

156%

query-76

10,6

41,2

4,00

22,2

19,4

261,5

27,77

185,5

287%

456%

1250%

568%

query-78

20,1

29,7

20,32

39,4

36,2

285,6

52,36

209,1

48%

94%

689%

299%

query-79

3,2

7,9

3,63

18,3

9,6

155,7

14,94

122,9

147%

405%

1528%

723%

query-81

1,5

1,8

1,42

1,9

2,2

10,9

3,072

8,706

17%

33%

401%

183%

query-83

1,7

1,9

1,40

2,3

2,5

12,0

2,873

8,904

11%

65%

380%

210%

query-84

0,9

3,2

0,80

1,1

1,3

6,3

1,716

3,323

273%

33%

406%

94%

query-85

2,3

5,2

1,89

5,9

4,5

40,8

7,601

32,74

126%

214%

800%

331%

query-88

9,7

31,4

9,20

34,1

24,1

546,6

27,42

346,9

223%

270%

2171%

1165%

query-89

1,0

0,9

0,95

0,4

0,2

0,3

0,979

0,287

−6%

−60%

36%

−71%

query-91

0,8

3,3

0,75

0,7

0,5

0,6

0,714

0,592

330%

−1%

10%

−17%

query-93

2,4

9,5

2,56

12,8

9,5

101,0

12,9

80,75

293%

402%

967%

526%

query-96

1,5

4,4

1,56

10,6

3,2

75,2

3,895

62,51

181%

575%

2241%

1505%

query-97

4,7

6,7

4,25

13,8

8,6

97,8

12,75

77,43

43%

226%

1041%

507%

query-99

6,5

5,6

6,09

9,6

5,2

65,9

7,121

48,67

−14%

58%

1163%

583%

Среднее

 

 

 

 

 

 

 

 

148%

210%

812%

389%

Результат с MinIO удивил: мы ожидали падения производительности на мелких файлах, но не такого сильного. Для MinIO решили попробовать рекомендованный в различных статьях тюнинг, например, но не помогло.

Ozone и HDFS в тестах стабильно показывали хорошие результаты с минимальными отклонениями. Дополнительно аналогичные тесты провели в Trino. Результаты подтвердили тот же тренд: при работе с мелкими файлами производительность падает значительно.

Какой можно сделать из этого вывод? Похоже, что HDFS гораздо лучше справляется с обработкой маленьких файлов. Кроме того, Apache Ozone с OFS также показал лучшее значение, чем MinIO, что согласовывается с исследованием компании Cloudera.

Давайте отойдём от экспериментов и дадим этим наблюдениям теоретическое обоснование. Исследований на эту тему довольно много, но я приведу два, которые более релевантны и интересны, на мой взгляд.

Теоретические подтверждения неожиданных показателей

Результаты тестов заставили обратиться к существующим исследованиям и экспертам, чтобы разобраться в причинах. Главная причина разницы производительности — фундаментальные архитектурные различия между HDFS и S3.

HDFS оптимизирована для работы именно с большим количеством файлов, что обеспечивается централизованным управлением метаданными и пакетной обработкой операций. В свою очередь, объектные хранилища с S3 API изначально предназначены для объектного хранения и плохо подходят для аналитических сценариев с частым доступом к большому числу мелких файлов. Их основные проблемы — высокие накладные расходы на метаданные и значительные задержки при работе через HTTP-протокол.

Ожидаемо! Основная причина такой деградации производительности связана с эффектом «малых файлов» и накладными расходами на метаданные и сетевые запросы, особенно в объектных хранилищах.

Рассмотрим сравнение производительности операций с метаданными файловых систем HDFS, S3 и JuiceFS, таких как создание, открытие, переименование и удаление. Исследования показали кратное преимущество HDFS — от 10 до 20 раз в зависимости от параллельности нагрузки перед S3, а также значительно меньшую задержку. На графике выше показана задержка выполнения каждой операции при 20 одновременных операциях (не при полной нагрузке), и вы можете видеть, что S3 работает очень медленно.

Кроме этого, в статье сделаны выводы что HDFS в отличие от S3, который начинает проседать, показывает линейный рост производительности при увеличении числа параллельных операций. Пропускная способность (количество операций в секунду при высокой нагрузке) у S3 в 10–100 раз ниже, чем у HDFS. S3 требует больше ресурсов для достижения производительности, сопоставимой с HDFS. Если важна производительность операций с метаданными (например, при большом числе мелких файлов), HDFS будет намного предпочтительнее.

Сравним два решения: HBase и Phoenix, операционная база данных Cloudera (COD) с использованием в качестве слоя хранения двух вариантов HDFS или облачного хранилища. И облачное хранилище на графиках выглядит очень привлекательно по производительности. Для поддержки S3 используется высокопроизводительный кэш, а когда S3 используется без кэша, то производительность в разы меньше. Такие статьи нам не подходят, по ним невозможно оценить производительность самого объектного хранилища. Зачем тогда это всё? Продвижение технологии S3 очень выгодно определённому количеству производителей ПО, и часто они расставляют акценты так, что у невнимательного читателя происходит подмена понятий, как, например, здесь, когда производительность системы кэш и S3 выдают за производительность S3.

В исследовании представлена методология и набор инструментов для анализа производительности S3 API, а также возможности использования Minio для задач High Performance Computing (HPC). В нём выявлены интересные для нашего материала закономерности. Обработка мелких файлов через S3 API приводит к значительным потерям производительности из-за высоких накладных расходов на протокол HTTP и метаданные. Крупные файлы (от 1 МБ и выше) обрабатываются заметно быстрее, чем мелкие. Это подтверждается как в тестах MinIO, так и при анализе облачных решений. Для файлов размером 32 МБ и более производительность близка к теоретической пропускной способности сети, особенно в оптимизированных сценариях (S3Embedded).

Маленькие файлы (до 64 КБ) демонстрируют значительное падение производительности, особенно при работе через REST API и в стандартных S3-совместимых системах.

Как видно из приведённых изображений, полученные в статье результаты полностью согласуются с теми, что мы наблюдали в экспериментах. Напомню, получена хорошая производительность при размере файлов более 13 Мб и сильная деградация производительности при значении близком к 1 Мб.

Однако здесь также заметна явная зависимость между такими показателями, как Throughput (пропускная способность) и Ops/s (операций в секунду). Вопрос, который появляется, когда мы видим эту зависимость: «А как ведёт себя S3, если идёт речь не о большом количестве маленьких файлов, а о большом количестве файлов в датасете?»  

Провести этот эксперимент достаточно дорого и существенно сложнее. Возможно, если будет интерес к теме и необходимость, сделаем более глубокое исследование. Могу только дать спойлер: статистически значимая зависимость имеется.

Требующий особого внимания побочный эффект большого количества файлов в датасете — это стоимость операций PUT, HEAD, LIST, POST и особенно GET. За небольшое время в течение суток и не слишком большое количество запросов счёт за эти операции превысил счёт за хранение в три раза.

Маленькие файлы в современных архитектурах загрузки и повседневной жизни производственного озера данных

Многие хорошо настроенные производственные системы стремятся избегать большого количества мелких файлов, но полностью устранить эту проблему невозможно. На практике можно выделить три типичных ситуации.

Первая — неизбежная действительность больших систем. Исследование B. Уэлча и Г. Ноера показало, что в реальных установках от 25% до 90% файлов имеют размер 64 КБ или меньше. При этом они занимают незначительный объём (обычно до 3%, иногда до 15%), но создают существенную нагрузку на файловую систему. Схожие результаты получили также исследователи Королевского технологического института вместе с Oracle и Spotify: около четверти файлов в производственных средах HDFS занимают менее 16 КБ, а на них приходится до 42% всех файловых операций.

Вторая ситуация связана с параллельной обработкой данных (MPP и MapReduce). Чем выше параллельность записи, тем больше одновременно создаваемых файлов, в том числе очень мелких.

Третий случай — это потоковая обработка данных, в частности архитектура Change Data Capture (CDC), и открытые табличные форматы вроде Iceberg или Hudi. Рассмотрим его детальнее.

Маленькие файлы в CDC-архитектуре

На изображении ниже схематически представлен процесс непрерывной обработки и загрузки в Iceberg-таблицу CDC-логов PostgreSQL в Debezium-формате. Она происходит с использованием микробатчей Spark Structuring Streaming с триггером срабатывания 50 сек. Для формирования таблицы в формате Iceberg на приёмнике используется механизм отслеживания изменений SCD1 (Slowly Changing Dimensions).

На приёмнике существует точная, самая актуальная копия таблицы, к которой задействуется затирание. Данные в таблице полностью заменяются на новые (самые актуальные). Для «накатки» актуальных данных я использовал SQL-выражение MERGE INTO. 

Эта модель загрузки предполагает существование сотен и тысяч файлов в определённый момент времени до того, как вы будете осуществлять операции обслуживания, например следующие: rewrite_data_files, rewrite_manifests. Эти операции не бесплатные, они потребляют ресурсы. Ниже на изображении показан пример операции обслуживания файловой системы — оптимизация, когда из множества промежуточных файлов данных и метаданных создаётся один большой файл данных (merge) и несколько оптимизированных файлов метаданных.

Далее «устаревшие» файлы со временем нужно удалить.  

Рассмотрим результаты дополнительного теста, в котором замерили время выполнения операций «MERGE INTO» и «rewrite_data_files».

Сначала проверили, как зависит время выполнения SQL-команды «MERGE INTO» от количества файлов. Время росло линейно, но по-разному: на HDFS зависимость была слабой, а объектное хранилище MinIO демонстрировало гораздо более заметное увеличение времени обработки.

Целью теста было сравнить динамику изменения производительности MinIO и HDFS при равных начальных условиях и аналогичных ресурсах. В начале эксперимента скорость операции MERGE INTO была сопоставима: 6 секунд у HDFS и 8,5 секунд у MinIO. Но с ростом количества файлов MinIO сильно замедлился — в 9,3 раза, а HDFS всего в 3,2 раза. Из-за этого MinIO перестал укладываться в заданное окно триггера в 50 секунд, которое пришлось увеличить до 90 секунд.

Операция «rewrite_data_files» также показала похожую картину:

  • HDFS: 17 секунд при 3 тысячах файлов и 29 секунд при 9 тысячах (замедление в 1,7 раза).

  • MinIO: 81 секунда при 3 тысячах файлов и 162 секунды при 9 тысячах (замедление в 2 раза).

Абсолютные значения также важны: MinIO изначально был намного медленнее, и разница только увеличивалась по мере роста нагрузки.

Что делать?

Отказываться от объектных хранилищ с протоколом S3 не нужно. Такие системы отлично работают, когда речь идёт о точечной выборке небольшого числа объектов по ключу. Но в аналитических сценариях или задачах High Performance Computing, где происходит активная работа с большим числом мелких файлов, у S3 возникают существенные ограничения.

Если вы рассматриваете миграцию с HDFS на S3, обязательно проведите предварительный анализ распределения файлов по размерам. Если в вашей системе много файлов менее 10 Мб, миграция может привести к существенному падению производительности и росту стоимости. Именно поэтому крупные компании («экзабайтный клуб») не торопятся переходить на объектные хранилища полностью.

Для обеспечения стабильной работы Lakehouse нужно непрерывно контролировать состояние хранилища. Такой подход требует значительных ресурсов и опыта. Поэтому в некоторых случаях логичнее остаться на HDFS, используя механизмы агрегации файлов, например Hadoop Archive (HAR). HAR позволяет хранить и обрабатывать множество мелких файлов как единый архив без потерь в производительности. Кстати, для продвижения этой технологии руку приложил и наш разработчик Тигран (pull).

Кроме того, кажется вполне оправданным в некоторых случаях гибридный подход, где вы имеете «под ногами» как HDFS, так и объектное хранилище, например Ozone. В ADH это возможно!

Ну и наконец, решения класса «классическое MPP хранилище данных с проприетарным и жёстко связанным слоем хранения», например Greenplum, не имеют, очевидно, подобных проблем.

Список литературы

Комментарии (3)


  1. hard_sign
    11.06.2025 10:24

    Вообще-то S3 — это всего лишь протокол. И ваше исследование говорит не о недостатках протокола как такового, а об отсутствии нормальных on-premise платформ, реализующих этот протокол.


    1. StanislavRG Автор
      11.06.2025 10:24

      Вы правы, S3 — это именно протокол, и напрямую он не ограничивает производительность. В статье речь не о самом протоколе, а о том, как его текущие on-premise реализации (в частности, MinIO) ведут себя в сценариях с большим количеством мелких файлов — и именно в этих реализациях возникают серьёзные узкие места.

      При этом даже в облачных сервисах (AWS S3 и др.) при активной работе с мелкими файлами остаются фундаментальные ограничения из-за особенностей API (работа по HTTP, операции с метаданными и т. п.). Поэтому вопрос не только в реализации, но и в архитектурной природе подхода.


    1. poige
      11.06.2025 10:24

      Добавлю ещё, что брать Minio для чего-либо, кроме пресловутого SOHO в 2025 — ну так себе идейка. Можно было и не тестировать ничего, с оной давно всё понятно.