Объектное хранилище кажется простым API для файлов, но внутри это архитектура про долговечность данных, огромное пространство имён и фоновые процессы восстановления.
Кейс помогает связать слой метаданных, размещение байтов, загрузку больших файлов по частям, выбор между репликацией и кодированием с избыточностью, а также гарантии чтения и LIST-операций.
В интервью и архитектурных разборах он полезен тем, что быстро показывает, понимаете ли вы разницу между удобным клиентским интерфейсом и реальной ценой хранения на масштабе S3.
Долговечность данных
Ключевой вопрос не только в количестве копий, но и в том, как быстро система замечает повреждения, чинит их и разводит данные по независимым доменам отказа.
Слой метаданных
Метаданные определяют, где лежит объект и как работает LIST, поэтому именно этот слой чаще всего становится настоящим узким местом системы.
Горячие бакеты
Неравномерная нагрузка по bucket и prefix быстро создаёт горячие точки, если заранее не продумать разбиение каталога и отдельные индексы.
Цена хранения
Репликация, кодирование с избыточностью, классы хранения и сетевой трафик влияют на стоимость не меньше, чем сами диски.
хранит файлы, изображения, видео и резервные копии как независимые объекты с доступом по HTTP API. В отличие от файловой системы оно опирается на плоское и проектируется вокруг очень высокой , предсказуемой стоимости хранения и практически неограниченного масштаба.
Источник
System Design Interview Vol. 2
Глава Design S3 разбирает путь записи, слой метаданных и причины, по которым S3 выглядит простым только снаружи.
Примеры объектных хранилищ
- Amazon S3: эталонный интерфейс индустрии с очень высокой долговечностью данных.
- Google Cloud Storage: тесно связан с экосистемой аналитики и ML в Google Cloud.
- Azure Blob Storage: предлагает несколько классов хранения от горячего до архивного.
- MinIO: S3-совместимое решение с открытым исходным кодом для частных инсталляций и локальной инфраструктуры.
- Ceph: универсальная распределённая платформа, которая поддерживает блочное, файловое и объектное хранение.
Функциональные требования
Базовые операции
PUT /bucket/object— загрузка объектаGET /bucket/object— чтение или скачивание объектаDELETE /bucket/object— удаление объектаLIST /bucket?prefix=— перечисление объектов по префиксу
Дополнительные возможности
- Загрузка больших объектов по частям
- Хранение нескольких версий одного объекта
- Автоматический перевод данных между классами хранения
- Временный доступ через pre-signed URL
Нефункциональные требования
Для S3-подобной системы важно заранее договориться не только о масштабе хранения, но и о целевой сервиса и ожидаемой на пути чтения и записи.
| Требование | Целевое значение | Почему это важно |
|---|---|---|
| Долговечность данных | 99.999999999% (11 девяток) | Потеря пользовательских данных недопустима даже при сбоях оборудования. |
| Доступность | 99.99% | Объекты должны быть доступны почти постоянно для приложений и пользователей. |
| Масштаб | Экзабайты и выше | Хранилище должно расти без переразметки всей системы. |
| Пропускная способность | Tbps+ | Система должна выдерживать массовые параллельные загрузки и скачивания. |
| Размер объекта | До 5 TB (как в S3) | Нужно обслуживать и небольшие файлы, и очень крупные архивы. |
Высокоуровневая архитектура
Теория
DDIA: Storage Engines
Помогает понять, почему слой метаданных и слой данных живут по разным правилам и требуют разных структур хранения.
Архитектурная схема
Путь записи и чтения в объектном хранилищеСервис метаданных
Здесь хранятся имя объекта, размер, content-type, checksum, версии, правила доступа и ссылка на физическое размещение данных. Этот слой отвечает за поиск объекта по ключу и за операции LIST, поэтому именно он чаще всего становится главным источником эксплуатационной сложности.
Пример записи метаданных:
{
"bucket_id": "uuid",
"object_key": "/photos/2024/vacation.jpg",
"object_id": "uuid",
"size": 4_500_000,
"content_type": "image/jpeg",
"checksum": "sha256:abc123...",
"version_id": "v3",
"created_at": "2024-01-15T10:30:00Z",
"storage_class": "STANDARD",
"replicas": ["node1", "node2", "node3"]
}Слой хранения данных
Сами байты объекта лежат отдельно от метаданных. Такой разрез позволяет независимо масштабировать каталог объектов и физическое хранение, а также выбирать разные стратегии размещения для горячих и холодных данных.
Подходы к хранению:
- Репликация: несколько копий на разных узлах или в разных AZ
- Кодирование с избыточностью: меньше накладных расходов на хранение ценой более сложного восстановления
- Многоуровневое хранение: горячие данные, редкий доступ и архив
Практические оптимизации:
- Последовательная запись крупных объектов
- Пакетирование мелких файлов
- Фоновая очистка и уплотнение редко используемых сегментов
Путь загрузки объекта
Путь загрузки
Упрощённый сценарий записи объектаЗагрузка крупных файлов по частям
нужна для больших объектов: клиент может передавать части параллельно, возобновлять операцию после сбоя и не пересылать уже записанные куски повторно.
# 1. Инициализация
POST /bucket/object?uploads → upload_id
# 2. Загрузка частей (параллельно)
PUT /bucket/object?uploadId=X&partNumber=1 → ETag1
PUT /bucket/object?uploadId=X&partNumber=2 → ETag2
...
# 3. Завершение
POST /bucket/object?uploadId=X
{
"parts": [
{"partNumber": 1, "ETag": "..."},
{"partNumber": 2, "ETag": "..."}
]
}Долговечность данных и размещение копий
Глубже
Database Internals
Полезно для разговора о репликации, восстановлении и цене каждой дополнительной гарантии хранения.
На практике объектное хранилище сочетает для быстрого восстановления и для экономии места на больших объёмах холодных данных.
Репликация
Один и тот же объект хранится в нескольких копиях на разных узлах, стойках или в разных зонах доступности.
Плюсы:
- Простой путь записи и чтения
- Быстрое восстановление после отказа узла
- Низкая вычислительная нагрузка
Минусы:
- Примерно трёхкратные затраты на хранение при трёх копиях
- Высокая стоимость для огромных объёмов архивных данных
Кодирование с избыточностью
Данные разбиваются на k полезных фрагментов и m дополнительных блоков, поэтому объект можно восстановить даже после потери части носителей.
Плюсы:
- Сильно ниже накладные расходы на хранение, чем у трёх копий
- Хорошо подходит для редко читаемых данных и архивов
- Позволяет сохранить высокую долговечность без чрезмерной цены
Минусы:
- Выше нагрузка на CPU во время записи, чтения и восстановления
- Ремонт после сбоя сложнее, чем при простой репликации
Откуда берутся одиннадцать девяток
Само число не появляется из магии. Оно складывается из разнесения копий по независимым доменам отказа, быстрой замены повреждённых фрагментов и постоянной проверки целостности.
# Допустим: # - Annual Failure Rate (AFR) одного диска = 2% # - 3 копии в разных failure domains P(потеря одного диска) = 0.02 P(потеря двух дисков одновременно) = 0.02 × 0.02 = 0.0004 P(потеря трёх дисков до восстановления) = 0.0004 × 0.02 = 0.000008 # Дальше помогают: # - разные AZ # - быстрое восстановление # - scrubbing и контроль checksum # → очень высокая долговечность данных
Шардирование метаданных
Когда число объектов доходит до миллиардов, слой метаданных становится главным узким местом. снимает нагрузку, но сразу усложняет LIST-запросы и выбор приемлемой между основным каталогом и дополнительными индексами.
1. Разбиение по bucket
Самый простой вариант: все объекты одного bucket лежат на одном шарде. Проблема в том, что популярные bucket быстро становятся горячими точками.
2. Разбиение по хэшу object key
hash(bucket_id + object_key) % N даёт хорошее распределение нагрузки, но LIST-запрос приходится отправлять во все шарды и затем собирать результат обратно.
3. Гибридный подход
Основной каталог шардируется по одному ключу, а для prefix-запросов поддерживается отдельный индекс. Именно такой путь чаще всего выбирают боевые системы.
Почему LIST дороже, чем кажется
При хэш-разбиении запрос LIST /bucket?prefix=/photos/ не знает заранее, в каком шарде окажется нужный диапазон ключей. Поэтому координатор рассылает запрос по всем шардам и затем объединяет частичные ответы.
Что помогает:
- Отдельный индекс для prefix-запросов
- Диапазонное разбиение для упорядоченного листинга
- Денормализация в отдельную таблицу каталога
Цена решения:
- Дополнительные затраты на хранение индексов
- Необходимость синхронизировать основной каталог и индексы
- Более сложный путь записи и обновления метаданных
Безопасность
Контроль доступа
- IAM Policies: права на уровне пользователя или роли
- Bucket Policies: правила доступа на уровне ресурса
- ACLs: права на конкретный объект, если нужен более тонкий контроль
- Pre-signed URLs: временный доступ без постоянной выдачи ключей
Шифрование
- SSE-S3: шифрование на стороне сервиса с управляемыми ключами
- SSE-KMS: серверное шифрование с ключами, управляемыми через KMS
- SSE-C: серверное шифрование с ключами, которые приносит клиент
- Client-side: шифрование до отправки объекта в хранилище
Классы хранения
Связь
Интеграция с сетью доставки контента
Объектное хранилище часто выступает исходным слоем для глобальной раздачи контента, где важны версия объектов, предсказуемое чтение и контроль стоимости.
| Класс | Задержка | Стоимость | Типичный сценарий |
|---|---|---|---|
| Standard (Hot) | миллисекунды | $$$ | Частый доступ, рабочие данные приложения |
| Infrequent Access | миллисекунды | $$ | Резервные копии и данные с редкими чтениями |
| Archive (Glacier) | часы | $ | Долгосрочное хранение и регуляторные архивы |
| Deep Archive | 12+ часов | ¢ | Очень редкие обращения и архивы “на всякий случай” |
Политики жизненного цикла
{
"rules": [
{
"filter": {"prefix": "logs/"},
"transitions": [
{"days": 30, "storageClass": "INFREQUENT_ACCESS"},
{"days": 90, "storageClass": "GLACIER"}
],
"expiration": {"days": 365}
}
]
}Вопросы для интервью
1. Как добраться до одиннадцати девяток долговечности?
Разнести копии или кодовые фрагменты по разным AZ, регулярно проверять checksum, быстро чинить повреждённые данные и не экономить на мониторинге фонового ремонта.
2. Как ускорить загрузку очень больших файлов?
Загружать файл по частям, разрешать параллельную отправку, поддерживать возобновление после обрыва и по возможности отдавать клиенту pre-signed URL для прямой записи в хранилище.
3. Когда выбирать репликацию, а когда кодирование с избыточностью?
Репликация подходит для горячих данных с частыми чтениями и требованием к быстрому восстановлению. Кодирование с избыточностью выгоднее для холодных данных, когда экономия места важнее дополнительной вычислительной нагрузки.
4. Как реализовать версионирование без потери старых объектов?
Каждый PUT создаёт новую запись с уникальным version_id, а DELETE пишет delete marker в метаданные, не стирая данные немедленно.
5. Что делать с “мусором” после удалений и незавершённых загрузок?
Нужна фоновая сборка мусора: находить неподтверждённые части, убирать неиспользуемые блоки, периодически уплотнять сегменты и следить, чтобы очистка не ломала путь чтения.
Ключевые выводы
- ✓Метаданные и данные живут отдельно — именно это позволяет независимо масштабировать каталог и фактическое хранение байтов.
- ✓Долговечность достигается сочетанием техник — разнесение копий, фоновый ремонт, checksum и контроль доменов отказа работают вместе.
- ✓Классы хранения меняют экономику — горячие и архивные данные нельзя держать по одной и той же цене.
- ✓LIST и метаданные часто сложнее, чем PUT/GET — именно здесь чаще всего появляются горячие точки, индексы и спорные гарантии консистентности.
- ✓Загрузка по частям нужна не ради красоты API — она делает работу с большими объектами устойчивой к сбоям и удобной для клиентов.
Связанные главы
- Content Delivery Network (CDN) - показывает, как объектное хранилище работает как исходный слой для глобальной раздачи и кэширования контента.
- System Design Interview: An Insider's Guide (краткий обзор) - даёт классический разбор S3-подобной системы с акцентом на масштаб, долговечность данных и путь записи.
- Database Internals: A Deep Dive (short summary) - помогает глубже понять устройство слоя метаданных и компромиссы движка хранения для объектного каталога.
- Designing Data-Intensive Applications, 2nd Edition (short summary) - даёт распределённую базу для разговора о репликации, консистентности и восстановлении после сбоев.
- Distributed File System (GFS/HDFS) - даёт смежный кейс по распределённому хранению: размещение данных, восстановление и работа кластера под высокой нагрузкой.
- Acing the System Design Interview (краткий обзор) - помогает собрать ответ про объектное хранилище в интервью-рамку: API, критический путь, компромиссы и риски.
- Примеры задач по системному дизайну - ставит объектное хранилище рядом с другими кейсами и упрощает сравнение архитектурных решений между доменами.
