Суть back-of-envelope
Back-of-envelope (BOTE) -- это быстрая прикидка "на салфетке": вы не собираетесь получить точный ответ, вы ищете правильный порядок величины. Вопрос не "1243 QPS или 1587 QPS?", а "сотни, тысячи или миллионы?". Разница между 10^3 и 10^6 меняет архитектуру: single node vs шардированный кластер, Postgres vs Cassandra, in-memory cache vs CDN.
Два случая, где BOTE критичен
- System design interview. Интервьюер хочет увидеть, что вы не боитесь цифр и умеете привязать решение к нагрузке. "Нужен Redis" без обоснования -- слабый ответ. "При 50k QPS чтения и working set 20 GB Redis оправдан" -- сильный.
- Реальное проектирование. Решение "взять Kafka" стоит команде месяцы работы. Если peak нагрузка 100 msg/sec -- Kafka избыточен, хватит RabbitMQ или даже Postgres. Если 1M msg/sec -- Kafka минимум, но нужно планировать партиционирование.
Когда оценки НЕ нужны
- Прототип, MVP, внутренний инструмент на 10 пользователей -- любая разумная технология справится
- Задача чисто алгоритмическая (правильность > производительности)
- Уже есть production-метрики реальной системы -- используйте их, а не прикидки
Power of 2 vs Power of 10
Инженеры путают KB/KiB, MB/MiB. На BOTE это не критично (погрешность 2.4% на KB, 7.4% на GB), но важно понимать обе системы.
| Префикс SI (10^n) | Значение | Префикс IEC (2^n) | Значение | Разница |
|---|---|---|---|---|
| KB (kilobyte) | 10^3 = 1 000 | KiB (kibibyte) | 2^10 = 1 024 | +2.4% |
| MB (megabyte) | 10^6 = 1 000 000 | MiB (mebibyte) | 2^20 = 1 048 576 | +4.9% |
| GB (gigabyte) | 10^9 | GiB (gibibyte) | 2^30 ≈ 1.07×10^9 | +7.4% |
| TB (terabyte) | 10^12 | TiB (tebibyte) | 2^40 ≈ 1.10×10^12 | +10.0% |
| PB (petabyte) | 10^15 | PiB (pebibyte) | 2^50 ≈ 1.13×10^15 | +12.6% |
Практическое правило BOTE. Используйте power of 2 для памяти/storage и power of 10 для QPS/bandwidth. На уровне PB разница уже 12% -- округляйте в "плохую" сторону (больше storage, выше latency).
Таблица мощностей двойки
Это маст-хэв для BOTE. Запомните хотя бы до 2^40.
| Степень | Значение | Человеческое название |
|---|---|---|
| 2^10 | 1 024 | ~1 тысяча (Ki) |
| 2^16 | 65 536 | ~65 тысяч (max uint16, порт) |
| 2^20 | 1 048 576 | ~1 миллион (Mi) |
| 2^24 | 16 777 216 | ~16 миллионов (RGB cube) |
| 2^30 | 1 073 741 824 | ~1 миллиард (Gi) |
| 2^32 | 4 294 967 296 | ~4.3 миллиарда (IPv4, max uint32) |
| 2^40 | 1.1×10^12 | ~1 триллион (Ti) |
| 2^50 | 1.13×10^15 | ~1 квадриллион (Pi) |
| 2^64 | 1.84×10^19 | ~18 квинтиллионов (max uint64) |
Typical scales систем
Держите в голове порядки величин, с которыми работают реальные системы.
| Масштаб | Примеры | QPS | Storage |
|---|---|---|---|
| Small | Внутренний tool, startup MVP | 1-100 | < 10 GB |
| Medium | B2B SaaS, региональный сервис | 100-10k | 10 GB - 1 TB |
| Large | Крупный e-commerce, национальный сервис | 10k-100k | 1-100 TB |
| Huge | Топ-100 сайт мира | 100k-1M | 100 TB - 10 PB |
| Hyperscale | Google, Meta, Amazon | > 1M | > 10 PB |
Округление и точность
Правила BOTE:
- Округляйте агрессивно. 86400 секунд в сутках ≈ 100k (или 10^5). 365 дней ≈ 400 (10^2.6). Погрешность 15-20% не важна на уровне "нужен или нет шардинг".
- Округляйте в "плохую" сторону. Если считаете нагрузку -- вверх. Если считаете бюджет -- вниз. Это даёт запас.
- Считайте peak, не average. Peak/average обычно 2-4x для web, 10x для retail в чёрную пятницу. Проектируйте под peak.
- Запас x2-x3. Нагрузка растёт, хостинг падает, метрики ошибаются. Всегда добавляйте запас.
Полезные приближения
- 1 день ≈ 10^5 секунд (точно 86 400)
- 1 месяц ≈ 30 дней ≈ 2.6×10^6 секунд
- 1 год ≈ 400 дней ≈ 3×10^7 секунд
- 1 Gbps ≈ 125 MB/s (точно 125 MB/s для power of 10)
- ASCII-символ = 1 byte, UTF-8 кириллица = ~2 bytes
- UUID в hex = 36 bytes, в bin = 16 bytes
Хелпер: вычисление степеней
Простые утилиты для человекочитаемых размеров.
<?php
declare(strict_types=1);
/**
* Binary size helper: converts bytes to human-readable form and back.
* Uses power-of-2 (IEC) units: KiB, MiB, GiB, TiB, PiB.
*/
final class BinarySize
{
private const UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
/**
* Format bytes into the largest unit where value >= 1.
* Example: format(1536) => "1.5 KiB"
*/
public static function format(int|float $bytes, int $precision = 2): string
{
if ($bytes < 1) {
return '0 B';
}
$exp = (int) floor(log($bytes, 1024));
$exp = min($exp, count(self::UNITS) - 1);
$value = $bytes / (1024 ** $exp);
return sprintf("%.{$precision}f %s", $value, self::UNITS[$exp]);
}
/**
* Parse "2.5 GiB" back into bytes.
*/
public static function parse(string $input): int
{
if (!preg_match('/^\s*(\d+(?:\.\d+)?)\s*([KMGTPE]?i?B)\s*$/i', $input, $m)) {
throw new \InvalidArgumentException("Invalid size format: {$input}");
}
$value = (float) $m[1];
$unit = strtoupper($m[2]);
$idx = array_search($unit, array_map('strtoupper', self::UNITS), true);
if ($idx === false) {
throw new \InvalidArgumentException("Unknown unit: {$unit}");
}
return (int) round($value * (1024 ** $idx));
}
/**
* Raw power of 2: pow2(30) => 1073741824.
*/
public static function pow2(int $exp): int
{
return 1 << $exp;
}
}
// Usage:
// BinarySize::format(5 * 1024 ** 3) => "5.00 GiB"
// BinarySize::parse("500 MiB") => 524288000
Перед расчётом задайте 5 вопросов:
- DAU / MAU? Сколько реальных пользователей в день/месяц?
- Actions per user? Сколько действий на пользователя в день?
- Read/write ratio? Соцсеть -- 100:1 read. Аналитика -- 1:100.
- Object size? Tweet -- 280 байт. Photo -- 2 MB. Video -- 50 MB.
- Retention? Хранить месяц, год, вечно?
Ответив на них, получите QPS, storage, bandwidth за 2-3 минуты.
Выводы
- BOTE -- правильный порядок величины, не точная цифра
- Power of 2 для памяти/storage, power of 10 для QPS/bandwidth -- погрешность до 12.6% на PB
- Запомните мощности двойки до 2^40: 10^3, 10^6, 10^9, 10^12
- Считайте peak, округляйте вверх, добавляйте x2-x3 запас
- Без оценок архитектурное решение -- гадание; с оценками -- инженерия