MidПрактика6 min

Sizing хранилища

Расчёт объёма storage: объекты × размер × retention × репликация, индексы 20-40%, компрессия, hot/warm/cold tiers

Базовая формула

Total storage = objects × size_per_object × retention_years × replication_factor
                + indexes (+20-40%)
                + backups
                + overhead (WAL, vacuum, fragmentation) ~10-20%

Storage sizing ошибаются чаще всего -- забывают про индексы, реплики, бэкапы. Итог: планировали 1 TB, получили 5 TB счёт.

Компоненты объёма

1. Raw data

Основные данные: строки БД, документы, файлы. Считается как:

raw = N × avg_size_per_record

Типичные размеры:

Объект Размер
User record (id, email, name, метаданные) 1-2 KB
Tweet / short post 280-500 bytes
Blog post (average) 5-10 KB
Comment 500 bytes - 2 KB
Photo (Instagram, full) 2-5 MB
Photo thumbnail 50-200 KB
Short video (TikTok 15s) 5-10 MB
Long video (1min 1080p) 50-100 MB
4K video (1 hour) 5-10 GB
Event / log line 500 bytes - 2 KB
Time series point 16-64 bytes

2. Indexes

Индексы -- это 20-40% от raw data, иногда больше для write-heavy workloads.

Тип индекса Оверхед
B-tree primary key ~10%
B-tree secondary (per column) 10-20%
Full-text index 30-100% (зависит от языка)
Geospatial 15-30%
Bitmap 5-15%

Правило: если в таблице 5 индексов -- считайте, что storage удваивается.

3. Replication factor

Distributed storage всегда реплицирован. Типичные значения:

Система Replication
PostgreSQL primary + replica 2x
PostgreSQL + 2 replicas 3x
HDFS default 3x
Cassandra (RF=3, quorum) 3x
S3 standard ~3x (но оплата 1x)
Kafka (replication.factor=3) 3x

Без replication factor ваша оценка в 3 раза меньше реальности.

4. Backups

Бэкапы хранятся минимум 30 дней, часто дольше. Типично:

  • Full backup weekly + incremental daily = ~1.5x от production
  • Plus point-in-time recovery WAL = +10-20%

Если data = 10 TB, backup storage ≈ 15-20 TB.

5. WAL / write-ahead log

Postgres WAL, Kafka segments, Cassandra commit log -- всё это overhead на писательскую нагрузку. Обычно 10-20% от raw data.

Компрессия

Компрессия сильно зависит от типа данных.

Тип данных Коэффициент сжатия
Text (JSON, logs) 5-10x
Structured text (CSV, SQL dump) 3-5x
HTML / XML 5-8x
Already compressed (JPG, MP4, ZIP) 1.0-1.05x (почти ничего)
Binary protocol (Protobuf, Avro) 1.5-2x
Time series (Prometheus, InfluxDB) 10-30x

Практика. Включайте компрессию в БД (Postgres TOAST работает автоматически, MongoDB снижает на 50%+ для документов). Для объектного storage -- gzip/zstd перед загрузкой в S3.

Hot / Warm / Cold tiers

Не всё storage одинаково. Большинство данных старше 90 дней почти никто не читает. Сэкономьте 80% на cost, переместив в cheaper tier.

Tier Доступ Latency Cost (AWS примерно)
Hot Постоянный < 10 ms $0.023/GB/month (S3 Standard)
Warm Редкий 10-100 ms $0.0125/GB/month (S3 Standard-IA)
Cold Архив минуты $0.004/GB/month (S3 Glacier)
Deep archive Часы-дни 12+ hours $0.00099/GB/month (Glacier Deep)

Lifecycle пример

day 0-30:   S3 Standard  (hot, постоянный доступ)
day 31-90:  Standard-IA  (warm, редкий)
day 91-365: Glacier      (cold, аудит/compliance)
day 366+:   Deep Archive (или delete, если не нужно)

Это снижает годовую стоимость на 70-90% для logs/events.

Growth curve

Не проектируйте под сегодня -- проектируйте под 3-5 лет роста.

Storage (TB)
  │                                       ╭── ← 5x от сегодня
  │                              ╭────────╯
  │                     ╭────────╯
  │            ╭────────╯
  │   ╭────────╯
  │───╯
  └──┬────┬────┬────┬────┬────→ years
     0    1    2    3    4    5

Year 0: 10 TB
Year 1: 20 TB (2x)
Year 3: 50 TB (5x)
Year 5: 100 TB (10x)

Правило. Assume 2x growth per year для растущего продукта. Для зрелого -- 1.2-1.5x.

Worked пример: Twitter-подобный сервис

Входные данные

  • 500 M tweets per day (Twitter ~500M публичных tweet'ов/день на пике)
  • avg tweet size: 280 bytes (text) + 300 bytes (metadata: user_id, timestamp, geo, retweet_of) = 580 bytes
  • retention: вечно (публичные твиты)
  • replication factor: 3 (Cassandra RF=3)

Text storage

Daily raw text  = 500M × 580 B = 290 GB/day
Daily compressed (5x) = 58 GB/day

Indexes

Indexes (30%) = 58 × 0.3 ≈ 17 GB/day
Daily with indexes = 58 + 17 = 75 GB/day

Replication

Daily replicated = 75 × 3 = 225 GB/day

Media (25% tweets have image)

Images/day    = 500M × 0.25 = 125M
Image size    = 200 KB (с учётом thumbnails)
Media raw/day = 125M × 200 KB = 25 TB/day (NOT compressible)
Media × RF=3  = 75 TB/day

Годовой объём

Text + indexes (с RF):  225 GB × 365 = 82 TB/year
Media (с RF):           75 TB × 365  = 27 PB/year

Вывод: основной storage -- медиа (99.7% объёма), не тексты. Поэтому Twitter использует специализированный media storage (похожее на S3) с aggressive CDN-кэшированием.

Worked пример: e-commerce каталог

Входные данные

  • 10M products
  • avg product record: 5 KB (description, attributes, variants, prices history)
  • 10 images per product, avg 300 KB
  • 5 years retention
  • Postgres primary + 2 replicas = RF 3

Подсчёт

Products raw:    10M × 5 KB = 50 GB
Indexes (+40%):  50 + 20 = 70 GB
Replication x3:  70 × 3 = 210 GB

Images raw:      10M × 10 × 300 KB = 30 TB
Image RF x3:     30 × 3 = 90 TB

Рост за 5 лет (assume 2x catalog)

Total (5 years): 210 GB × 2 = 420 GB (SQL DB)
                  90 TB × 2 = 180 TB (object storage)

Архитектурный вывод

  • 420 GB SQL -- помещается в один Postgres instance
  • 180 TB images -- нужен S3-совместимый storage + CDN

Калькулятор storage

<?php

declare(strict_types=1);

/**
 * Storage sizing calculator: raw data -> total with indexes/replication/backup.
 */
final class StorageSizing
{
    public function __construct(
        private readonly int $objectCount,
        private readonly int $avgSizeBytes,
        private readonly float $indexOverhead = 0.3,   // 30% typical
        private readonly int $replicationFactor = 3,
        private readonly float $backupOverhead = 0.5,  // 50% for weekly+daily backups
        private readonly float $compressionRatio = 1.0, // 1.0 = no compression
    ) {}

    /**
     * @return array{
     *     raw: int, compressed: int, with_indexes: int,
     *     replicated: int, with_backups: int, total: int
     * }
     */
    public function estimate(): array
    {
        $raw = $this->objectCount * $this->avgSizeBytes;
        $compressed = (int) ($raw / $this->compressionRatio);
        $withIndexes = (int) ($compressed * (1 + $this->indexOverhead));
        $replicated = $withIndexes * $this->replicationFactor;
        $withBackups = (int) ($replicated * (1 + $this->backupOverhead));

        return [
            'raw'          => $raw,
            'compressed'   => $compressed,
            'with_indexes' => $withIndexes,
            'replicated'   => $replicated,
            'with_backups' => $withBackups,
            'total'        => $withBackups,
        ];
    }

    /**
     * Project growth over N years with given yearly multiplier.
     */
    public function projectGrowth(int $years, float $yearlyMultiplier = 2.0): int
    {
        $base = $this->estimate()['total'];
        return (int) ($base * ($yearlyMultiplier ** $years));
    }
}

// Usage:
// $s = new StorageSizing(
//     objectCount: 500_000_000,  // 500M tweets/day
//     avgSizeBytes: 580,
//     compressionRatio: 5.0,
//     replicationFactor: 3,
// );
// $daily = $s->estimate();
// $fiveYear = $s->projectGrowth(5, 1.5);
## Частые ошибки
  • Забыть про индексы. "Данные занимают 100 GB" -- а с индексами 140, а с реплика́ми 420.
  • Считать только production. Бэкапы, dev/stage, audit logs -- ещё +50-100%.
  • Не учитывать compression. Text без сжатия -- в 5 раз больше реальности.
  • Проектировать на сегодня. Через 2 года данных в 4 раза больше.
  • Игнорировать small objects overhead. 1M записей по 100 bytes != 100 MB. В Postgres каждая строка имеет 24 bytes header + alignment. Реальность: 250-400 MB.

Выводы

  • Формула: objects × size × RF × (1 + index%) × (1 + backup%) / compression_ratio
  • Индексы +20-40%, репликация x2-x3, бэкапы +50% -- итого x5-6 от raw
  • Hot/warm/cold tiers экономят 70-90% стоимости для logs/events
  • Планируйте под 3-5 лет роста, assume 2x per year
  • Media dominates в 99% случаев -- текстовая БД скромна, object storage огромен