Object Storage (S3-like) — system design case showing buckets, immutable objects, PUT/GET/DELETE, multipart upload, erasure coding 12+4 for 11-nines durability, metadata vs data plane separation, tiered storage (Standard/IA/Glacier), versioning, lifecycle policies. 5 scenarios: PUT with EC, multi-region GET, multipart upload, lifecycle migration to Glacier, tail latency at scale. 2 ADRs on metadata/data plane split and EC vs replication.
Object storage нужен почти в каждом крупном продукте: изображения, видео, backups, data lake, ML datasets, архивы, логи, static assets, user uploads. S3-like система отличается от файловой системы и block storage: клиент работает с buckets, keys и objects через API, а не с POSIX directories и random writes. Это позволяет строить exabyte-scale хранилище с огромной durability, низкой стоимостью и простым API.
Кейс проверяет понимание разделения metadata plane и data plane. Metadata отвечает на вопросы: существует ли bucket, какие права, какая версия объекта, где лежат chunks/fragments, какой checksum, какой storage class. Data plane отвечает за поток байтов: upload, multipart, chunking, erasure coding, placement, repair и delivery.
Масштаб S3-like системы огромен: триллионы objects, exabytes storage, миллионы PUT/GET в секунду, metadata petabytes, durability уровня 11 nines и availability около четырех nines. Поэтому нельзя просто положить файлы на NFS: нужны шардированная metadata DB, placement service, rack-aware storage nodes, background scrub, lifecycle tiering и replication.
Связанные темы: ::concept{slug="object-storage-s3"}, ::concept{slug="sharding"}, ::concept{slug="replication"}, ::concept{slug="consistency-models"}, ::concept{slug="partitioning-strategies"}.
Ментальная модель: object storage это immutable blobs плюс mutable metadata. Клиент делает PUT bucket/key, API frontend проверяет подпись и quota, metadata service проверяет namespace, placement service решает, куда положить chunks, storage nodes сохраняют fragments, а metadata atomically публикует новую версию объекта.
Большой объект разбивается на chunks или multipart parts. Для durability система не обязана хранить три полные копии каждого байта. Часто используется erasure coding, например 12 data fragments + 4 parity fragments. Это дает возможность восстановить объект при потере нескольких fragments с overhead около 33%, а не 200% как при 3 replicas. Цена: чтение и repair сложнее, маленькие объекты могут быть неэффективны, latency write path выше.
Buckets и keys выглядят как директории, но это logical namespace. photos/2026/a.jpg не обязан лежать в дереве каталогов. Metadata шардируется по hash(bucket, key) или более сложной scheme, чтобы hot prefixes не перегружали один shard.
Диаграмма показывает client SDK, API frontend, auth/quota, metadata service, metadata DB/cache, placement service, storage nodes, background services и cold tiers. API frontend stateless: его можно масштабировать горизонтально. Он не должен хранить данные локально, иначе failover и retries станут опасными.
Metadata service находится на критическом пути почти всех операций. Для GET нужно найти текущую версию key и location manifest. Для PUT нужно зарезервировать upload, принять data fragments и потом опубликовать metadata. Для DELETE часто создается delete marker, особенно если включено versioning.
Storage layer показывает chunk split и erasure-coded placement across racks/AZ. Placement service должен избегать correlated failure: fragments одного объекта нельзя класть на один rack, одну power domain или один disk batch.
Background services не второстепенны. GC удаляет orphaned multipart parts и старые versions, rebalancer переносит chunks, integrity scan находит silent corruption, tiering отправляет холодные объекты в cheaper storage, replication копирует данные в другой region.
PUT small object показывает write path. Клиент отправляет объект, API проверяет auth, metadata service проверяет bucket, placement выбирает nodes, fragments пишутся на storage nodes, затем metadata atomically указывает на location. Важный урок: metadata publish должен происходить после durable write, иначе GET увидит объект, который еще не восстановим.
GET hot object показывает metadata cache и streaming. API получает location из cache или metadata DB, читает достаточное число fragments и stream-ит ответ. Для hot objects cache на metadata и CDN/object cache может снимать большую часть нагрузки.
Multipart upload показывает, как загружать терабайтные объекты. Upload сначала инициируется, затем parts загружаются независимо и параллельно, а final complete создает manifest. Это позволяет retry отдельной части, resume после сбоя и не держать весь файл в памяти.
Disk failure показывает durability mechanics. Integrity service обнаруживает missing или corrupt fragment, читает surviving fragments, восстанавливает потерянный parity/data fragment и обновляет location. Durability достигается не только replication factor, а постоянным repair before next failure.
Hot bucket throttle показывает metadata hotspot. Если миллионы PUT идут в один bucket/prefix, metadata shard может перегреться. Нужны adaptive partitioning, prefix hashing, quotas, request shaping и иногда рекомендации клиентам по key naming.
Lifecycle tiering показывает cost control. Холодные объекты перемещаются в infrequent access, archive или tape-like tier. GET после archival может быть медленным и платным, поэтому API должен явно отражать restore state.
Replication против erasure coding. Полные реплики проще и быстрее на read/repair, но дороже по storage. Erasure coding дешевле на exabyte scale, но требует больше CPU, network coordination и careful placement.
Strong read-after-write против eventual metadata. Современный S3-style UX ожидает, что после успешного PUT GET сразу увидит объект. Это требует сильной metadata consistency или carefully designed commit path. Eventual listing может быть проще, но хуже для разработчиков.
Маленькие объекты против больших объектов. Система, оптимизированная под 5 ТБ objects, может страдать от миллиардов 1 КБ files из-за metadata overhead. Нужны packing, small object optimization или отдельный tier.
Versioning повышает recoverability, но увеличивает storage и усложняет delete semantics. DELETE может лишь добавлять marker, а не освобождать bytes. Compliance retention и legal hold еще сильнее усложняют GC.
Multi-region replication повышает availability и disaster recovery, но создает задержку, стоимость egress и конфликтные сценарии для updates/delete markers.
Amazon S3 является эталоном API и durability expectation. Google Cloud Storage и Azure Blob Storage похожи по модели buckets/containers, objects/blobs, lifecycle, versioning и multi-region options. MinIO дает S3-compatible object storage для self-hosted setups. Ceph RADOS Gateway реализует S3/Swift поверх распределенного object store.
Внутри больших компаний object storage часто становится data lake foundation: Spark/Presto/Trino читают objects, ML pipelines хранят datasets, backup systems складывают snapshots. В таких сценариях важны listing performance, consistency, multipart behavior и cost lifecycle.
Первый anti-pattern: использовать object storage как POSIX filesystem. Частые append, rename, file locks и random writes плохо ложатся на object API. Лучше писать новые objects и обновлять manifest.
Второй anti-pattern: хранить metadata только в имени объекта. Если права, content type, version, checksum и lifecycle не живут в надежной metadata system, операции становятся дорогими и небезопасными.
Третий anti-pattern: делать synchronous cross-region write на каждый PUT без понимания latency. Это может убить p99 и availability. Нужен осознанный выбор между regional durability, async replication и strong multi-region.
Четвертый anti-pattern: забыть cleanup multipart uploads. Незавершенные uploads могут годами занимать storage и не отображаться как обычные объекты.
Пятый anti-pattern: не проверять checksums. Silent data corruption реальна: disks, memory, network и firmware ошибаются. Нужны end-to-end checksums и background scrub.
Не используйте object storage для low-latency random block IO. Для базы данных нужен block storage/local NVMe, а не S3 API.
Не используйте его как primary transactional database. Нет joins, multi-object transactions в обычном смысле и миллисекундных updates отдельных полей.
Не используйте S3-like storage для tiny frequently-mutated records, если KV/SQL решает задачу проще. Metadata overhead будет доминировать.
Не используйте archive tier для данных, которые нужны немедленно. Восстановление из cold tier может занимать минуты или часы.
Изучите S3 API semantics: buckets, keys, versioning, multipart upload, ETag, server-side encryption, lifecycle policies и event notifications. Затем разберите erasure coding, Reed-Solomon, placement groups, bit rot detection, metadata sharding и object manifests. Для смежных тем полезны ::concept{slug="cdn-design"}, ::concept{slug="replication"}, ::concept{slug="consistency-models"} и ::concept{slug="capacity-planning"}.