Довольно часто возникает ситуация, когда клиенты неправильно понимают принципы работы S3. В результате они сталкиваются с проблемами при листинге объектов в бакете, получают ошибки 503 ServiceUnavailable, испытывают деградацию производительности и другие неприятные эффекты.
В этой статье мы рассмотрим практический кейс:
600 миллионов мелких объектов в одном бакете S3 (с перспективой роста) и разберём, как правильно организовать структуру ключей, чтобы обеспечить стабильную и масштабируемую работу.
Основные инструменты, которые нам в этом помогут:
- шардирование
- использование префиксов
- хеширование ключей объектов
Как S3 работает с префиксами
Для начала важно понять ключевой момент:
S3 — это не файловая система и не классическая база данных, а распределённое key-value хранилище, где ключ объекта — это просто строка. Для масштабирования S3 распределяет нагрузку по префиксам ключей. Каждый префикс (например, data/, logs/2024/05/) масштабируется независимо и имеет собственные лимиты по операциям в секунду.
Вывод:
Чем больше равномерно распределённых префиксов — тем выше суммарная пропускная способность бакета. Если же большая часть запросов идёт к одному и тому же префиксу (или к «пустому» префиксу), вы рано или поздно упрётесь в лимиты и получите:
- рост latency
- ошибки 503
- проблемы с массовыми операциями.
Исходные данные
Возьмём в расчёт:
- 600 миллионов объектов
- объекты небольшого размера
- постоянный рост объёма
- активный листинг и доступ к данным.
Рассмотрим возможные варианты структурирования бакета.
1. Один бакет и 600 млн объектов без структуры префиксов. Все объекты находятся в «корне» бакета, без какой-либо иерархии.
Проблемы:
- фактически используется один префикс — пустой;
- весь трафик концентрируется в одной точке;
- при высокой нагрузке лимиты по операциям быстро исчерпываются;
- появляются ошибки 503.
Вывод:
❌ Худший возможный вариант. Использовать нельзя.
2. Один бакет и 600 млн уникальных префиксов
Каждый объект имеет собственный уникальный «подкаталог».
Преимущества:
- идеальное распределение нагрузки;
- отсутствие «горячих» префиксов.
Проблемы:
- листинг бакета становится практически бесполезным
- навигация через консоль и API сильно усложняется
- внутренние механизмы S3 (каталогизация, биллинг, метаданные) не оптимизированы под миллионы уникальных префиксов
- высокая операционная сложность.
Вывод:
⚠️ Избыточное решение (overkill). Теоретически работает, но на практике не рекомендуется.
3. Один бакет и ~600 префиксов, по ~1 млн объектов в каждом
Создаётся ограниченное количество логических «шардов» (префиксов).
Преимущества:
- равномерное распределение нагрузки
- возможность параллельной работы с разными префиксами
- хорошая управляемость
- соответствует best practices S3
- легко масштабируется без изменения архитектуры
Вывод:
✅ Оптимальный подход для больших объёмов данных.
Выбор схемы именования префиксов
Использовать последовательные префиксы (data-1/, data-2/) не рекомендуется, если ключи объектов тоже последовательны — это может привести к появлению «горячих» префиксов.Лучшее решение — хеширование.
Алгоритм:
- берём уникальный идентификатор объекта (например, user_id или file_id)
- вычисляем его хеш (MD5, SHA-1, SHA-256)
- используем первые N символов хеша как префикс
Почему часто используют hex-префиксы. Шестнадцатеричные префиксы (0–9, a–f) — самый популярный вариант.
Причины:
- 16 возможных значений на символ → хорошее распределение
- большинство хеш-функций возвращают hex по умолчанию
- простота реализации
- предсказуемое масштабирование.
Варианты глубины шардирования
a) 1 hex-символ — 16 префиксов
0/, 1/, 2/, …, f/
Количество префиксов: 16
Объектов на префикс: ≈ 37.5 млн
Когда использовать:
- низкая нагрузка
- пилоты, прототипы
- сотни RPS.
Недостаток:
37 млн объектов на префикс — потенциальное узкое место.
b) 2 hex-символа — 256 префиксов
00/, 01/, …, fe/, ff/
Количество префиксов: 256
Объектов на префикс: ≈ 2.34 млн
Когда использовать:
- средняя нагрузка (тысячи RPS);
- типичный production-сценарий;
- хороший баланс простоты и производительности.
c) 3 hex-символа — 4096 префиксов
000/, 001/, …, ffe/, fff/
Количество префиксов: 4096
Объектов на префикс: ≈ 146 500
Когда использовать:
- высоконагруженные системы
- десятки тысяч RPS
- критичные сервисы
- выраженные пиковые нагрузки.
???? Для большинства продакшен-систем это наиболее универсальный вариант.
Пример реализации (Python)
import hashlib
def get_s3_path(object_id: str, prefix_level: int = 3) -> str:
"""
Генерирует путь к объекту в S3 с использованием hex-префиксов.
Args:
object_id: уникальный идентификатор объекта
prefix_level: количество hex-символов в префиксе (1–4)
"""
hash_hex = hashlib.md5(object_id.encode()).hexdigest()
prefix = hash_hex[:prefix_level]
# Формируем иерархию: a/b/c/object_id
prefix_path = "/".join(prefix)
return f"{prefix_path}/{object_id}"
# Примеры:
print(get_s3_path("user_12345.pdf", 2)) # 7/f/user_12345.pdf
print(get_s3_path("image_67890.jpg", 3)) # a/1/b/image_67890.jpg
Дополнительные советы и рекомендации
- Практика показывает, что 800+ млн объектов в одном бакете — зона повышенного риска.
- Используйте многоуровневые хеш-префиксы для равномерного распределения нагрузки.
- Не генерируйте префиксы случайно вручную — используйте хеш-функции.
- Помните: в S3 нет настоящей иерархии, есть только ключи.
- Рассмотрите разделение данных между несколькими бакетами, и используйте разные политики хранения
- Уровень шардирования 3 hex-символа (~4096 префиксов) подходит для большинства production-нагрузок.