MidПрактика6 min

Расчёт QPS и RPS

Формулы QPS/RPS: DAU → peak, read/write ratio, burst patterns, p50 vs p99. Worked пример на 10M DAU

Терминология

  • QPS (Queries Per Second) -- запросы к БД/кэшу. Может быть 10-100x больше RPS (один HTTP request делает много SQL-запросов).
  • RPS (Requests Per Second) -- HTTP-запросы к API.
  • TPS (Transactions Per Second) -- транзакции в финансовых системах.

На практике термины смешивают. Важно при обсуждении уточнять уровень: web, app, DB.

Базовая формула DAU → QPS

Это основа всех расчётов нагрузки.

QPS_avg = DAU × actions_per_user / seconds_per_day

seconds_per_day = 86 400 ≈ 10^5

Обычно округляют до 10^5 для BOTE.

Пример: 10M DAU, 10 actions per user.

QPS_avg = 10^7 × 10 / 10^5 = 10^3 = 1 000 QPS

Peak vs Average

Пользователи не распределены равномерно по суткам. Есть "рабочие часы" и пики.

Тип сервиса Peak / Average
B2B enterprise 2x (9-18 в будни)
Consumer web (соцсеть) 3-4x (вечер в часовом поясе)
E-commerce 5-10x (чёрная пятница, Cyber Monday)
Streaming (Netflix) 3-4x (вечер пятницы)
Gaming 5-10x (премьера новой игры)
Delivery (food) 8-10x (обед + ужин)

Стандартное допущение BOTE: peak = 3 × average. В реальности смотрите на свой паттерн.

Visualization: daily pattern

QPS
  │                      ╭──╮
  │                   ╭──╯  ╰╮       ← evening peak
  │        ╭──╮    ╭──╯      ╰─╮
  │     ╭──╯  ╰────╯           ╰╮
  │   ╭─╯                       ╰──╮
  │ ╭─╯                             ╰──╮
  │─╯                                   ╰───
  └──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬─→ hour
     0  2  4  6  8 10 12 14 16 18 20 22 24

Average = площадь / 24
Peak    = ~3x average (для consumer web)

Read / Write ratio

Большинство систем read-heavy. Знание соотношения критично для выбора стратегии (master-replica, caching, CQRS).

Тип сервиса Read : Write Пример
Social feed 100:1 Twitter читают в 100 раз чаще, чем пишут
E-commerce catalog 100:1 Просмотры товаров vs покупки
Messenger 10:1 Сообщение пишут раз, читают получатели
Blog platform 1000:1 Пост публикуется раз, читается тысячи
Analytics / logs 1:100 Пишут все события, читают редко
Payment system 1:1 Каждая транзакция читается (для баланса)

Применение. Если read:write = 100:1, то read-реплики и кэши решают 99% нагрузки. Master с writes остаётся слабо нагружен.

p50 vs p99 vs p99.9

Middle latency обманчива -- нагрузку задают хвосты.

Метрика Описание Что показывает
p50 (median) 50% запросов быстрее "Обычный" пользователь
p95 5% медленнее Заметная часть пользователей страдает
p99 1% медленнее Каждый 100-й запрос -- долгий
p99.9 0.1% медленнее Amazon говорит: каждый 1000-й запрос определяет впечатление

Правило. При 10 запросах на страницу, если p99 = 1s, вероятность хотя бы одного медленного запроса = 1 - 0.99^10 ≈ 10%. Каждый 10-й пользователь видит тормоза. Поэтому в SLA фиксируют p99, не p50.

Burst patterns

Обычный peak/average 3x -- это "гладкий" peak. Есть ещё burst -- резкие всплески.

Thundering herd

Все клиенты бьют одновременно:

  • Cron-задачи на :00 каждый час
  • Истечение JWT у миллионов пользователей в одно время
  • Запуск рекламной кампании ("ссылка в prime-time шоу")

Решения: jitter (+ random(0, 60 sec)), rate limiting, exponential backoff.

Product-specific bursts

  • E-commerce: распродажа в 00:00 -- 50x peak за минуты
  • Бронирование билетов: открытие продаж -- 100x
  • Live streaming: старт трансляции -- 20x
  • Новостной пик: breaking news -- 10x для home page

Планируйте под product peak, не под usual peak.

Worked пример: 10M DAU сервис

Разберём полный расчёт на реалистичном сервисе (типа Habr, Medium).

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

  • DAU = 10 000 000
  • Actions per user per day = 10 (открыл 5 статей, поставил 2 лайка, написал 1 коммент и т.д.)
  • Read : Write = 100 : 1
  • Peak / Average = 3 (consumer web)

Средний QPS

Total actions / day = 10M × 10 = 100M = 10^8
Seconds per day     = 86 400 ≈ 10^5
QPS_avg             = 10^8 / 10^5 = 10^3 = 1 000 QPS

Peak QPS

QPS_peak = 1 000 × 3 = 3 000 QPS

Разбивка read / write

Read_avg  = 1 000 × (100/101) ≈ 990 QPS
Write_avg = 1 000 × (1/101)   ≈ 10  QPS

Read_peak  = 990 × 3  ≈ 3 000 QPS
Write_peak = 10 × 3   ≈ 30   QPS

QPS на уровне БД

Обычно один HTTP-запрос делает 3-10 DB-запросов (одна странница статьи: post + user + comments + tags).

DB QPS_peak = 3 000 × 5 = 15 000 QPS reads
              30 × 3     = 90 QPS writes

Что это значит архитектурно

  • 3 000 peak RPS -- 3-5 application серверов (Go/PHP FastCGI), каждый держит 1-2k RPS
  • 15 000 DB reads/sec -- в голую Postgres не влезет, нужен кэш (cache hit 95%+ → 750 QPS в БД)
  • 30 writes/sec -- легко для single Postgres master
  • Replica factor: 1 master + 2-3 replicas для read offload

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

Хелпер для быстрой оценки.

<?php

declare(strict_types=1);

/**
 * QPS estimator: DAU + actions -> average/peak QPS with read/write split.
 */
final class QpsEstimator
{
    private const SECONDS_PER_DAY = 86_400;

    public function __construct(
        private readonly int $dau,
        private readonly int $actionsPerUser,
        private readonly int $readWriteRatio = 100, // reads per 1 write
        private readonly float $peakMultiplier = 3.0,
    ) {}

    /**
     * @return array{
     *     avg: float, peak: float,
     *     read_avg: float, read_peak: float,
     *     write_avg: float, write_peak: float
     * }
     */
    public function estimate(): array
    {
        $totalActions = $this->dau * $this->actionsPerUser;
        $avg = $totalActions / self::SECONDS_PER_DAY;
        $peak = $avg * $this->peakMultiplier;

        $readShare  = $this->readWriteRatio / ($this->readWriteRatio + 1);
        $writeShare = 1 / ($this->readWriteRatio + 1);

        return [
            'avg'        => round($avg, 1),
            'peak'       => round($peak, 1),
            'read_avg'   => round($avg  * $readShare, 1),
            'read_peak'  => round($peak * $readShare, 1),
            'write_avg'  => round($avg  * $writeShare, 1),
            'write_peak' => round($peak * $writeShare, 1),
        ];
    }

    /**
     * DB-level QPS given amplification factor (1 HTTP req -> N DB queries).
     */
    public function dbQps(int $amplificationRead, int $amplificationWrite): array
    {
        $e = $this->estimate();
        return [
            'db_read_peak'  => round($e['read_peak']  * $amplificationRead, 1),
            'db_write_peak' => round($e['write_peak'] * $amplificationWrite, 1),
        ];
    }
}

// Usage:
// $est = new QpsEstimator(dau: 10_000_000, actionsPerUser: 10);
// $est->estimate();
// => ['avg' => 1157, 'peak' => 3472, 'read_peak' => 3437, 'write_peak' => 34, ...]
## Чек-лист для QPS расчёта
  1. Получите DAU (от продакта, аналитики или оценки из MAU × 0.1-0.3)
  2. Оцените actions per user per day (feed: 20-50, чат: 100+, покупки: 5-10)
  3. Разделите на 10^5 -- получите avg QPS
  4. Умножьте на 3 (consumer) или 10 (e-commerce sale) -- получите peak
  5. Разбейте по read/write (обычно 100:1 или 10:1)
  6. Умножьте на amplification factor для DB (3-10 queries per request)
  7. Добавьте запас x2

Частые ошибки

  • Считать MAU вместо DAU. MAU может быть 3-10x DAU. Всегда уточняйте.
  • Забыть peak. Проектирование под average = падение в peak.
  • Игнорировать amplification. 1 RPS на API ≠ 1 QPS на БД.
  • Единичное число без p99. "Средняя latency 50 ms" ничего не говорит -- нужен p99.
  • Забыть про batch-операции. Cron может за минуту запросить миллион записей -- это не QPS, но грузит БД.

Выводы

  • Базовая формула: QPS_avg = DAU × actions / 10^5, peak = 3× average
  • Read/write ratio 100:1 -- типичное для social/e-commerce, 1:100 -- для аналитики
  • Peak determines capacity, p99 determines SLO
  • Amplification factor 3-10 превращает RPS в QPS для БД
  • Burst-паттерны (sales, breaking news) могут давать 10-100x от normal peak