Object Storage — это распределённая система хранения неструктурированных данных (файлов, изображений, видео, бэкапов) с доступом по HTTP API. В отличие от файловых систем, объектное хранилище работает с плоским namespace и обеспечивает практически неограниченную масштабируемость.
Источник
System Design Interview Vol. 2
Глава 'Design S3' с детальным разбором архитектуры объектного хранилища.
Примеры Object Storage
- Amazon S3: стандарт индустрии, 11 девяток durability
- Google Cloud Storage: интеграция с BigQuery, ML сервисами
- Azure Blob Storage: Hot/Cool/Archive tiers
- MinIO: S3-совместимое open-source решение
- Ceph: распределённая система хранения (block, file, object)
Функциональные требования
Core API
PUT /bucket/object— загрузка объектаGET /bucket/object— скачивание объектаDELETE /bucket/object— удалениеLIST /bucket?prefix=— листинг
Расширенные функции
- Multipart upload (для больших файлов)
- Versioning (история версий объектов)
- Lifecycle policies (автоудаление, архивация)
- Pre-signed URLs (временный доступ)
Нефункциональные требования
| Требование | Целевое значение | Обоснование |
|---|---|---|
| Durability | 99.999999999% (11 девяток) | Данные не должны теряться |
| Availability | 99.99% | Доступ к данным почти всегда |
| Масштабируемость | Exabytes+ | Хранение петабайт данных |
| Throughput | Tbps+ | Параллельные загрузки/скачивания |
| Размер объекта | До 5 TB (S3) | Поддержка больших файлов |
Высокоуровневая архитектура
Теория
DDIA: Storage Engines
Глубокое погружение в B-Trees, LSM-Trees и устройство хранилищ.
Architecture Map
High-level data flowMetadata Service
Хранит метаданные объектов: имя, размер, content-type, checksum, версии, ACL. Это критический компонент — без метаданных нельзя найти объект.
Структура метаданных:
{
"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"]
}Data Store
Фактическое хранилище данных объектов. Объекты разбиваются на чанки и распределяются по множеству дисков/серверов с репликацией.
Стратегии хранения:
- Replication: 3 копии на разных узлах
- Erasure Coding: (k, m) схема для экономии
- Tiered Storage: Hot → Warm → Cold
Оптимизации:
- Append-only write для последовательной записи
- Batching мелких объектов
- Compaction для garbage collection
Upload Flow
Upload Flow
Simple upload pathMultipart Upload (большие файлы)
Для файлов больше 5 GB используется многочастевая загрузка. Это позволяет параллелизировать upload и возобновлять при сбоях.
# 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": "..."}
]
}Durability & Replication
Глубже
Database Internals
Детальный разбор репликации и консенсуса в распределённых системах.
Replication
Создание нескольких копий данных на разных узлах/дата-центрах.
Преимущества:
- Простота реализации
- Быстрое восстановление
- Низкая CPU нагрузка
Недостатки:
- 3x overhead по хранению
- Высокая стоимость
Erasure Coding
Разбиение данных на k частей + m parity блоков. Восстановление из любых k частей.
Преимущества:
- 1.5x overhead (vs 3x для replication)
- Экономия на холодных данных
- Высокая durability
Недостатки:
- Высокая CPU нагрузка при записи/чтении
- Сложное восстановление
Как получить много девяток в durability
Как достигается 99.999999999% durability с тремя репликами?
# Предположим: # - Annual Failure Rate (AFR) одного диска = 2% # - 3 реплики в разных failure domains P(потеря 1 диска) = 0.02 P(потеря 2 дисков одновременно) = 0.02 × 0.02 = 0.0004 P(потеря 3 дисков до восстановления) = 0.0004 × 0.02 = 0.000008 # Добавляем: разные AZ, быстрое восстановление, мониторинг # → 99.999999999% durability
Metadata Sharding
Метаданные — это bottleneck системы. При миллиардах объектов нужен шардинг метаданных.
1. Sharding by Bucket
Простой вариант: все объекты бакета на одном шарде. Проблема — hot buckets.
2. Sharding by Object Key Hash
hash(bucket_id + object_key) % N — равномерное распределение, но LIST операции требуют scatter-gather.
3. Hybrid Approach
Шардинг по bucket + secondary index для prefix queries. Используется в production системах.
Проблема LIST операций
При hash-based sharding LIST /bucket?prefix=/photos/требует запрос ко всем шардам (scatter-gather).
Решения:
- Separate index для prefix queries
- Range-based sharding для ordered listing
- Денормализация в отдельную таблицу
Trade-offs:
- Дополнительный storage overhead
- Consistency между индексами
- Сложность операций записи
Security Considerations
Access Control
- IAM Policies: user/role-based access
- Bucket Policies: resource-based rules
- ACLs: object-level permissions
- Pre-signed URLs: временный доступ
Encryption
- SSE-S3: server-side, managed keys
- SSE-KMS: customer-managed keys
- SSE-C: customer-provided keys
- Client-side: encrypt before upload
Storage Classes
Связь
CDN Integration
Object Storage часто используется как origin для CDN.
| Storage Class | Latency | Cost | Use Case |
|---|---|---|---|
| Standard (Hot) | ms | $$$ | Частый доступ, production данные |
| Infrequent Access | ms | $$ | Редкий доступ, бэкапы |
| Archive (Glacier) | hours | $ | Долгосрочное хранение, compliance |
| Deep Archive | 12+ hours | ¢ | Архивы, редко нужные данные |
Lifecycle Policies
{
"rules": [
{
"filter": {"prefix": "logs/"},
"transitions": [
{"days": 30, "storageClass": "INFREQUENT_ACCESS"},
{"days": 90, "storageClass": "GLACIER"}
],
"expiration": {"days": 365}
}
]
}Вопросы для интервью
1. Как достичь 11 девяток durability?
Репликация в разных AZ, erasure coding, checksums, быстрое восстановление, мониторинг disk health, periodic scrubbing.
2. Как оптимизировать загрузку больших файлов?
Multipart upload с параллельными частями, возобновление при сбоях, pre-signed URLs для прямой загрузки в storage.
3. Replication vs Erasure Coding?
Replication (3x overhead) для hot данных с частым доступом. Erasure coding (1.5x) для cold данных, где CPU overhead приемлем.
4. Как реализовать versioning?
Каждый PUT создаёт новую версию с уникальным version_id. Метаданные хранят историю версий. DELETE создаёт delete marker.
5. Как работает garbage collection?
Background compaction: объединение мелких файлов, удаление unreferenced данных, defragmentation дисков. Mark-and-sweep или reference counting.
Ключевые выводы
- ✓Разделение metadata и data — ключевой архитектурный паттерн для масштабирования
- ✓Durability через репликацию — 3+ копии в разных failure domains
- ✓Erasure coding для cold data — экономия storage при сохранении durability
- ✓Tiered storage — автоматическое перемещение данных по lifecycle policies
- ✓Multipart upload — для больших файлов с параллелизмом и resumability
