Типичные сбои в продакшне и аудит индексов
Семь самых распространённых причин сбоев с индексами в продакшне — отсутствующий FK-индекс, приведение типов, устаревшая статистика, bloat, неверный порядок composite, JSONB cardinality bomb — плюс квартальный аудит и стратегия по окружениям.
DELETE FROM posts WHERE id = 42 с ON DELETE CASCADE выполняется 7 минут и всё это время удерживает row-level блокировки. Причина: comments(post_id) не проиндексирован. Postgres делает полный скан 200 млн строк комментариев, чтобы найти строки для удаления. Один отсутствующий индекс на внешнем ключе вызвал инцидент в продакшне. Этот урок каталогизирует типичные сбои и плейбук, который позволяет найти их до того, как они найдут вас.
Семь типичных причин сбоев
1. Отсутствующий индекс на FK-столбце
Внешние ключи не индексируются автоматически. Когда вы добавляете REFERENCES posts(id), Postgres создаёт индекс на posts(id) (referenced-столбец), но не на comments(post_id) (referencing-столбец). ON DELETE CASCADE на родительской таблице выполняет полный скан дочерней таблицы для поиска каскадируемых строк.
-- Опасный паттерн:
ALTER TABLE comments ADD CONSTRAINT fk_post FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE;
-- Нет индекса на comments(post_id) — DELETE FROM posts теперь O(n) по comments
-- Исправление:
CREATE INDEX CONCURRENTLY idx_comments_post_id ON comments(post_id);Правило: индексируйте каждый FK-столбец — если только не можете доказать, что родительская таблица никогда не удаляется и ни один запрос не фильтрует дочернюю по FK.
2. Неявное приведение типов отключает индекс
-- orders.user_id имеет тип BIGINT; индекс на orders(user_id) существует
SELECT * FROM orders WHERE user_id = '42'; -- '42' — TEXTPostgres применяет неявное приведение TEXT → BIGINT, но для этого ему нужно применить cast к каждой строке — он не может обойти B-tree (cast применяется к столбцу, не к константе). Результат: Seq Scan. Диагностика: EXPLAIN ANALYZE запроса. Исправление: используйте типизированные параметры ($1::BIGINT или корректный тип в query builder).
Другие распространённые ловушки: created_at::date = '2024-01-01' отключает индекс на created_at; используйте created_at >= '2024-01-01' AND created_at < '2024-01-02'.
3. Устаревшая статистика приводит к неверному плану
Модель стоимости планировщика зависит от данных pg_statistic — распределения значений, количества строк, корреляций. После массовой вставки, большого удаления или изменения схемы autovacuum планирует ANALYZE, но по умолчанию только после изменения 20% строк. До запуска ANALYZE у планировщика неверные оценки.
Симптом: EXPLAIN показывает rows=50, а фактически было rows=500 000 — промах в 10 000 раз. Планировщик выбрал nested-loop с индексом, который катастрофичен при таком количестве строк.
Исправление: запускайте ANALYZE table_name после массовых операций с данными. Для таблиц с сильно скошенными данными (например, 90% заказов принадлежат 5 воркспейсам) используйте CREATE STATISTICS (ndistinct) ON workspace_id, status FROM orders, чтобы дать планировщику лучшую многостолбцовую статистику.
4. Bloat индекса замедляет сканирование
Индексы накапливают мёртвые записи от UPDATE и DELETE. Postgres 14+ выполняет bottom-up удаление в индексах (агрессивная очистка горячих листовых страниц без ожидания vacuum), но у таблиц с частыми UPDATE может накапливаться значительный bloat.
Симптом: размер индекса в 5–10 раз превышает ожидаемый для данного числа строк; запросы медленные несмотря на использование индекса.
-- Пересоздать индекс без блокировки чтения/записи
REINDEX INDEX CONCURRENTLY idx_orders_user_id;REINDEX CONCURRENTLY (Postgres 12+) строит новый индекс в фоне, затем атомарно подменяет старый. Продолжительность аналогична первоначальному CREATE INDEX CONCURRENTLY.
5. Неверный порядок столбцов в composite-индексе
Composite-индекс (status, workspace_id), где большинство запросов фильтруют только по workspace_id, не ускоряет эти запросы. Планировщик падает обратно на Seq Scan.
Это нарушение правила leading-column, рассмотренного в уроке 02. В продакшне оно часто появляется, когда индекс проектировался для одного запроса, а позже добавился второй горячий запрос с другим паттерном фильтрации.
Исправление: добавьте второй индекс с корректным ведущим столбцом или переосмыслите composite, если исходный паттерн запросов менее частый.
6. JSONB GIN — cardinality bomb
JSONB-столбец, где документы содержат миллионы уникальных ключей, создаёт GIN-индекс с миллионами различных записей в posting-листах. Такой индекс вытесняет горячие страницы кучи из shared_buffers и имеет огромную стоимость записи (обновление posting-листа для каждого уникального ключа на каждый INSERT).
Симптом: размер GIN-индекса в 10–50 раз превышает размер столбца; коэффициент попаданий в кэш shared_buffers падает; задержки INSERT растут после добавления GIN.
Исправление: используйте expression B-tree индексы на известных горячих полях вместо GIN на всём столбце:
-- Вместо: CREATE INDEX ON events USING GIN (payload)
-- Используйте:
CREATE INDEX ON events ((payload->>'event_type'));
CREATE INDEX ON events ((payload->>'user_id'));Это создаёт небольшие B-tree индексы только на двух известных горячих полях вместо монолитного GIN.
7. «Индекс есть — но всё равно медленно»
Самый коварный случай: нужный индекс существует, но план всё равно медленный, потому что:
- Индекс не покрывающий (heap-обращения доминируют при масштабе).
- Столбец ORDER BY не входит в индекс (полная сортировка после index scan).
- Ведущий столбец индекса не соответствует наиболее селективному фильтру.
Диагностика — всегда EXPLAIN (ANALYZE, BUFFERS) на точном запросе с реалистичными параметрами, а не на искусственном тестовом запросе и не на staging с в 100 раз меньшим числом строк.
Квартальный плейбук аудита индексов
Если семь сбоев выше описывают проблемы, на которые ты реагируешь, то этот плейбук описывает проблемы, которые ты предотвращаешь. Запускайте его ежеквартально на каждой продакшн-базе Postgres. Обычно это занимает 1–2 часа и возвращает 10–30% экономии хранилища плюс измеримое улучшение write-throughput.
| Шаг | Запрос / инструмент | Действие |
|---|---|---|
| 1. Неиспользуемые индексы | pg_stat_user_indexes WHERE idx_scan = 0 | DROP CONCURRENTLY после проверки, что это не индекс constraint или batch-job |
| 2. Раздутые индексы | pgstattuple_approx() на горячих индексах | REINDEX CONCURRENTLY для индексов с bloat >30% |
| 3. Отсутствующие индексы | pg_stat_statements top-N по total_time + EXPLAIN | CREATE INDEX CONCURRENTLY для Seq Scan на больших таблицах |
| 4. Избыточные индексы | Каталог pg_index — поиск префиксных дубликатов | DROP CONCURRENTLY более короткий, если composite его покрывает |
| 5. Состояние VM | pg_stat_user_tables.n_dead_tup | VACUUM если n_dead_tup >5% живых строк на IOS-зависимых таблицах |
Шаг 1: найти неиспользуемые индексы
SELECT
schemaname,
relname AS table_name,
indexrelname AS index_name,
idx_scan,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
AND indexrelname NOT LIKE '%_pkey'
ORDER BY pg_relation_size(indexrelid) DESC;Предостережения: (a) pg_stat_reset() происходит при рестарте — если сервер недавно перезапускался, счётчик сканирований вводит в заблуждение; используйте систему мониторинга, отслеживающую значения во времени. (b) Некоторые индексы используются только периодическими batch-задачами — сверьтесь с pg_stat_statements за более длительный период. (c) Unique-индексы на constraint-столбцах могут показывать idx_scan = 0, но всё равно проверяют constraint при каждом INSERT.
Шаг 2: найти раздутые индексы
-- Требует расширение pgstattuple
SELECT
indexrelid::regclass AS index_name,
pg_size_pretty(pg_relation_size(indexrelid)) AS total_size,
round(100 * (approx_free_space + dead_tuple_len)::numeric
/ GREATEST(1, pg_relation_size(indexrelid)), 2) AS bloat_pct
FROM (
SELECT indexrelid, (pgstattuple_approx(indexrelid::regclass)).*
FROM pg_stat_user_indexes
) AS s
ORDER BY bloat_pct DESC;Индексы с bloat_pct выше 30% — кандидаты для REINDEX CONCURRENTLY. Планируйте в часы низкой нагрузки.
Шаг 3: найти отсутствующие индексы
Возьмите top-20 запросов из pg_stat_statements по total_exec_time. Для каждого запустите EXPLAIN (ANALYZE, BUFFERS) с реалистичными параметрами. Ищите Seq Scan на таблицах более 10k строк, где фильтр селективный (возвращается малая доля строк). Каждый такой случай — кандидат на создание индекса.
Шаг 4: найти избыточные индексы
Два индекса избыточны, если ключевые столбцы одного являются префиксом другого. idx_on_a и idx_on_a_b — idx_on_a избыточен, если composite idx_on_a_b всегда используется вместо него. Удалите более короткий.
-- Найти потенциальные дубликаты (требует ручной проверки)
SELECT
i.indexrelid::regclass AS index_name,
array_to_string(array_agg(a.attname ORDER BY x.n), ', ') AS columns
FROM pg_index i
JOIN pg_class c ON c.oid = i.indrelid
CROSS JOIN LATERAL unnest(i.indkey) WITH ORDINALITY AS x(attnum, n)
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = x.attnum
GROUP BY i.indexrelid, i.indrelid
ORDER BY i.indrelid, columns;Стратегия индексирования по окружениям
Разные окружения требуют разного подхода:
Development: минимальные индексы — только PK и обязательные unique constraints. Цель — быстрая итерация схемы, а не производительность запросов. Добавление всех продакшн-индексов замедляет миграции и скрывает архитектурные проблемы (не стоит опираться на индексы для компенсации плохой схемы).
Staging: воспроизводите продакшн-индексы при нагрузочном тестировании или бенчмарках; пропускайте при чисто функциональном тестировании. Это помогает поймать «работает в dev, медленно на staging» до продакшна.
Production: полный продуманный набор индексов со всеми инструментами из этого раздела — covering composite, partial indexes, INCLUDE, регулярный аудит. Добавление индексов в продакшн проходит code review вместе с запросом, для которого он создаётся.
Реплики / аналитические копии: могут иметь дополнительные OLAP-специфичные индексы (широкие composites, BRIN на диапазонах дат, GIN для полнотекстового поиска), которые были бы слишком дорогими для поддержки на OLTP-primary. При использовании логической репликации в аналитический Postgres — добавляйте такие индексы только на реплику, не на primary.
Стратегическая позиция
Самый распространённый постмортем на продакшн-масштабе: «отсутствующий индекс на этом горячем запросе». Второй по частоте: «слишком много индексов замедляют запись». Обе проблемы имеют одну причину — индексы не рассматривались как часть дизайна.
Зрелые команды:
- Добавляют индекс в ту же миграцию, что и фичу (запрос + индекс в одном PR).
- Проверяют индексы в code review вместе с SQL.
- Владеют стратегией индексирования на уровне платформы, а не на уровне команды.
- Проводят аудит ежеквартально.
Цена дисциплины: один пункт чеклиста на каждый PR с SQL. Цена её отсутствия: ночная тревога, когда дашборд клиента тормозит из-за FK, добавленного на прошлой неделе без индекса.
- Периодичность аудита
- раз в квартал
- Типичная экономия хранилища после аудита
- 10–30%
- Улучшение write-скорости после удаления неиспользуемых
- 2–5x на write-heavy таблицах
- Продолжительность REINDEX CONCURRENTLY
- как первоначальный CREATE INDEX CONCURRENTLY
- Порог bloat для REINDEX (pgstattuple_approx)
- >30%
- Окно idx_scan = 0 (мониторинг, не только сброс)
- >30 дней
- Top-N pg_stat_statements для поиска отсутствующих индексов
- top 20 по total_exec_time
- FK-индекс: создаёт ли Postgres автоматически?
- Нет — требует ручного создания
Запрос использует индекс — но всё равно медленный. Диагностика
slow_query: SELECT id, total_cents FROM orders WHERE workspace_id = 42 AND status = 'pending' ORDER BY created_at DESC LIMIT 50;
execution_time: 4280 ms
rows_returned: 50
EXPLAIN ANALYZE:
Limit (cost=320..380 rows=50 width=24) (actual time=4271..4280 rows=50 loops=1)
-> Sort (cost=320..18420 rows=180000 width=24) (actual time=4270..4275 rows=50 loops=1)
Sort Key: created_at DESC
Sort Method: top-N heapsort Memory: 32kB
-> Index Scan using idx_orders_workspace_status on orders
(cost=0.42..18000 rows=180000 width=24) (actual time=0.02..3800 rows=178240 loops=1)
Index Cond: ((workspace_id = 42) AND (status = 'pending'::text))
Определения индексов:
idx_orders_workspace_status: btree (workspace_id, status)
Размер idx_orders_workspace_status: 1.2 GB
Размер таблицы: 18 GB, строк: 80M
Статистика:
n_dead_tup: 12.4M (15% от общего числа)
last_autovacuum: 14 дней назад
last_analyze: 30 дней назад Почему запрос выполняется 4280 мс несмотря на использование индекса? Что является полным исправлением?
Провести полный квартальный аудит индексов на 500 ГБ продакшн-базе Postgres.
Запрос выполняется: WHERE LOWER(email) = 'alice@x.com'. На таблице есть индекс на (email). Почему запрос делает Seq Scan?
Индекс 200 МБ на таблице 2 ГБ показывает idx_scan = 0 за последние 45 дней. Что является корректным действием?
▸lesson.inset.warning
Никогда не удаляйте индекс только потому, что idx_scan = 0. Всегда проверяйте: (1) это unique/exclusion constraint-индекс? (2) используется ли он периодической batch-задачей, запускающейся ежемесячно или ежеквартально, за пределами окна мониторинга? (3) был ли сервер недавно перезапущен, сбросив все счётчики? Сверяйтесь с pg_stat_statements за 30+ дней и консультируйтесь с командой разработки перед удалением.
- 01Назовите семь типичных причин сбоев с индексами в продакшне и дайте однострочную диагностику для каждой.
- 02Пройдите квартальный аудит индексов за пять шагов.
- 03Чем различается стратегия индексирования для development, staging и production?
Семь причин сбоев покрывают продакшн-инциденты с индексами: отсутствующий FK-индекс (неиндексированный дочерний столбец вызывает O(n) каскадный скан); неявное приведение типов (функция на столбце отключает индекс — используйте типизированные параметры); устаревшая статистика (неверный план от неактуальных оценок строк — запускайте ANALYZE после массовых операций); bloat индекса (REINDEX CONCURRENTLY на индексах с bloat выше 30%); неверный порядок composite (нарушение leading-column — переосмыслите или добавьте второй индекс); JSONB GIN cardinality bomb (переходите на expression-индексы для известных горячих полей); и «есть индекс — но всё равно медленно» (неверная структура — проверяйте EXPLAIN ANALYZE на Sort-шаги и Heap Fetches).
Квартальный аудит: (1) DROP CONCURRENTLY неиспользуемых (idx_scan=0 за 30+ дней, не constraints). (2) REINDEX CONCURRENTLY раздутых. (3) CREATE CONCURRENTLY отсутствующих найденных через pg_stat_statements. (4) DROP CONCURRENTLY избыточных префиксных дубликатов. (5) Мониторить одну неделю после изменений.
Стратегия по окружениям: минимум в dev (быстрая итерация), зеркалирование продакшна на staging для бенчмарков, полный продуманный набор в production. OLAP-специфичные индексы — только на аналитических репликах, не на OLTP-primary.
Теперь, когда тебя поднимут ночью из-за медленного запроса, у тебя есть структурированная модель: EXPLAIN (ANALYZE, BUFFERS), сопоставление симптома с одним из семи режимов, известное исправление. А когда ничего не горит — запланируй квартальный аудит, чтобы следующего звонка не было.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
встречается в287
- Почему GraphQL получает N+1junior
- Механика DataLoader: батчинг на границе тикаmiddle
- Контракты batch-функции: порядок, формы, ошибкиmiddle
- Federation и lookahead: батчинг за пределами DataLoadermiddle
- Защита сложности запросов: depth, cost, persisted queriesmiddle
- Senior GraphQL API: scheduling-контракт, изоляция арендаторов, наблюдаемостьsenior
- Путь запроса: семь остановок от сокета до ответаjunior
- Accept и парсинг: от очереди ядра до типизированного запросаmiddle
- Маршрутизация и middleware: что выполняется и в каком порядкеmiddle
- Обработчик и ответ: от бизнес-логики до байтов на проводеmiddle
- Стриминг и backpressure: когда клиент читает медленнее, чем вы пишетеsenior
- Таймауты и хвостовая задержка: бюджеты, дедлайны и ловушка fan-outsenior
- Middleware и DI: два паттерна, формирующие любой backendjunior
- Пишем middleware: сигнатуры, next() и три модели фреймворковmiddle
- Инверсия управления: как зависимости добираются до классаmiddle
- Скоупы и время жизни DI: singleton, request, transientmiddle
- DI как шов для тестов: фейки, моки и граница, которая важнаsenior
- DI-контейнеры в продакшене: графы разрешения, циклы и когда не стоитsenior
- Блокирующий vs неблокирующий I/O: два способа ждатьjunior
- Event loop: один поток, упорядоченные фазыmiddle
- Что блокирует цикл: CPU-работа и синхронные вызовыmiddle
- Вынос CPU-работы: worker threads и пул libuvmiddle
- Backpressure и ограниченная конкурентностьsenior
- Пропускная способность под нагрузкой: хвостовая задержка и насыщениеsenior
- Зачем пул: цена создания соединенияjunior
- Размер пула: почему больше не значит быстрееmiddle
- Взятие и таймауты: очередь ожидания — настоящий дроссель задержкиmiddle
- Зачем идемпотентность: безопасные retryjunior
- Серверный state machine: четыре состояния idempotency keymiddle
- Стратегии retry: backoff, jitter и thundering herdmiddle
- Outbox и inbox: effectively-once через dual-write границуmiddle
- Конкурентность и архитектура кеша для идемпотентности на масштабеsenior
- Наблюдаемость, production-инциденты и дизайн для глобального масштабаsenior
- Event loop: один поток, три очередиjunior
- Задачи, микрозадачи и scheduler.yield()middle
- Точность таймеров, троттлинг и фоновая работаmiddle
- Голодание микрозадач, длинные задачи и LoAFsenior
- Event loop Node.js: фазы, nextTick и задержка циклаsenior
- React, Vue и наблюдаемость INP в продакшенеsenior
- Render pipeline: шесть стадий от байтов до пикселейjunior
- Цена стадий и модель процесса рендерераmiddle
- Инвалидация, dirty-биты и containmiddle
- Слои композитора: продвижение, перекрытие и память GPUmiddle
- Флейм-стрип DevTools и жизненный цикл кадраmiddle
- Layout thrash: форсированная синхронная компоновкаsenior
- BeginMainFrame, анимации на потоке compositor и память GPUsenior
- Observability в проде: LoAF, INP и полная поверхность атакиsenior
- Что такое V8 и почему производительность различается в 100 разjunior
- Четырёхуровневый JIT-конвейер V8 и профилированная тиеризацияmiddle
- Hidden classes, деревья переходов и расположение в памятиmiddle
- Inline caches, состояния IC и деоптимизацияmiddle
- Orinoco GC: параллельный scavenger, конкурентная разметка и барьеры записиmiddle
- Спекулятивный движок TurboFan и ловушка deopt-loopsenior
- V8 в production: Isolates, сжатие указателей и реальные аварииsenior
- Жизненный цикл service worker и стратегии кешированияmiddle
- Граничные случаи service worker: version skew, долговременность и ловушка навигацииsenior
- Что делает реконсилер: render vs commitjunior
- Объект fiber и дерево с двойной буферизациейmiddle
- Чистота фазы render и подшаги фазы commitmiddle
- Реконсиляция: эвристики диффа и ловушка ключейmiddle
- Приоритетные lanes, time-slicing и useTransitionmiddle
- Bailout, мемоизация и tearingsenior
- React Profiler, компилятор и продакшн-наблюдаемостьsenior
- Стратегии рендеринга: SSG, SSR, ISR, streaming и гидратацияjunior
- SSG, SSR, ISR, streaming и RSC — как работает каждая стратегияmiddle
- Цена гидратации: selective, progressive, острова, resumabilitymiddle
- Hydration mismatch: причины, обнаружение и правило детерминизмаsenior
- RSC, стратегия на маршрут и production-наблюдаемостьsenior
- Core Web Vitals: что измеряют LCP, INP и CLSjunior
- LCP: четыре фазы, одна доминирующая стоимостьmiddle
- INP: input delay, processing, presentationmiddle
- CLS: почему происходят сдвиги лейаута и как их остановитьmiddle
- Lab vs field: почему они расходятся и как использовать каждыйmiddle
- Трейдоффы метрик, RUM-атрибуция и цикл CI+полеsenior
- Общая картина: от URL до LCP до INP как эстафетаjunior
- Восемь слоёв трассировки: от service worker до второй навигацииmiddle
- Пять канонических поломок: где производство стабильно ломаетсяsenior
- Метод трёх треков: чтение трасс и построение системы мониторингаsenior
- Что такое cache stampede и почему он делает всё хужеjunior
- Лок и single-flight: ограничение параллельных rebuildmiddle
- XFetch: вероятностное раннее истечение без координацииmiddle
- Stale-while-revalidate и CDN request coalescingmiddle
- Детектирование stampede и дизайн TTL для продакшенаmiddle
- Метастабильный сбой, fencing-токены и production-постмортемыsenior
- Роли Raft, term и почему majority-кворум предотвращает split brainjunior
- Как Raft реплицирует log entry и решает, что его безопасно коммититьmiddle
- Выборы лидера в Raft: таймауты, правила голосования и четыре свойства безопасностиmiddle
- Raft в реальном мире: partition, медленный диск и клиентская маршрутизацияmiddle
- Расширения Raft: pre-vote, learner, snapshot и линеаризуемые чтенияsenior
- Raft в production: membership change, Multi-Raft и observabilitysenior
- Где происходит data fetching — и почему это решает LCPjunior
- Fetch waterfall''''ы — диагностика и лечение через Promise.allmiddle
- React Server Components и Suspense streamingmiddle
- Клиентский кэш: TanStack Query, SWR и stale-while-revalidatemiddle
- LCP, prefetch и race conditions в интерактивном fetchingmiddle
- Senior internals: RSC payload, слои кэша и production паденияsenior
- Что такое движок JavaScriptzero
- Ignition и байт-кодmiddle
- Внутри цикла интерпретатораmiddle
- Как V8 представляет значениеmiddle
- Что такое Map на самом деле: вскрываем hidden classmiddle
- Деревья переходов, deprecation и миграцияmiddle
- Быстрые свойства, медленные свойства и slack trackingmiddle
- Inline cache в глубину: feedback-слоты и handlersmiddle
- Monomorphic, polymorphic, megamorphic — и stub cachemiddle
- Четыре яруса и как работает разогревmiddle
- Обратная связь о типах: топливо для оптимизатораsenior
- TurboFan: оптимизатор на море узловsenior
- Спекуляция и guardssenior
- Деоптимизация: падение с обрываsenior
- On-Stack Replacement: подмена фрейма посреди циклаsenior
- Области видимости, контексты и scope chainmiddle
- Что на самом деле удерживает замыканиеmiddle
- Когда V8 аллоцирует Context (и чего это стоит)middle
- Замыкания, feedback-векторы и полиморфизм точки вызоваmiddle
- Зачем нужен GC и достижимостьmiddle
- Поколенческий GC и Scavengermiddle
- Major GC: mark, sweep, compactsenior
- Write barriers: цена инкрементального и поколенческого GCsenior
- Утечки в языке со сборкой мусораsenior
- Движок против циклаmiddle
- Микрозадачи против макрозадач: порядокmiddle
- Внутри Promisemiddle
- async/await, десахаризированныйmiddle
- Капстоун: оптимизируем горячий путьsenior
- Биты в проводеjunior
- Математика задержкиmiddle
- Bufferbloat и перегрузкаsenior
- Граница физического уровняsenior
- Трёхстороннее рукопожатие TCPjunior
- Номера последовательности и состояние соединенияmiddle
- Управление потоком и перегрузкойmiddle
- BBR, производственная наблюдаемость и за пределами TCPsenior
- DNS: что делает и зачем существуетjunior
- Обход резолвера: перенаправления, типы записей и gluemiddle
- TTL, кеширование и распространение DNSmiddle
- Рукопожатие за 1 RTT: key share и ECDHEmiddle
- Возобновление сессии и 0-RTTmiddle
- CDN: контент по соседствуjunior
- Anycast и GeoDNS: маршрутизация к ближайшему edgemiddle
- Многоуровневый кеш и Cache-Controlmiddle
- Заголовок Vary и cache keysmiddle
- Stale-while-revalidate и cache stampedesenior
- Edge workers и edge-side compositionsenior
- CDN: операции и observabilitysenior
- WebSocket: HTTP-апгрейд до постоянного соединенияjunior
- Формат WebSocket-фрейма: opcodes, маскирование, фрагментацияmiddle
- WebSocket vs SSE vs long-polling: выбор правильного транспортаmiddle
- Backpressure в WebSocket: когда клиенты не успеваютmiddle
- Реконнект: jittered backoff, thundering herd, восстановление сообщенийsenior
- WebSocket в масштабе: HTTP/2 мультиплексирование, permessage-deflate, C10Msenior
- WebSocket в production: прокси, безопасность и распределённая архитектураsenior
- Что делают обратные проксиjunior
- Алгоритмы балансировки: от round-robin до power-of-two-choicesmiddle
- L4 vs L7 балансировка и сохранение IP клиентаmiddle
- Health checks, connection draining и slow startmiddle
- Session affinity, consistent hashing и правильное решениеmiddle
- Retry-бури, circuit breakers и load sheddingsenior
- Устойчивая архитектура LB: anycast, zone-aware маршрутизация и observabilitysenior
- Почему QUIC, а не TCP+TLSjunior
- QUIC-потоки и head-of-line blockingjunior
- Объединённое рукопожатие и 1-RTTmiddle
- Connection ID и миграция сетиmiddle
- Обнаружение потерь и управление перегрузкойmiddle
- Возобновление 0-RTT и шифрование пакетовsenior
- Развёртывание и стоимость CPUsenior
- DDoS: что это и почему работаетjunior
- Атаки усиления и истощение состоянияmiddle
- Ограничение скорости: алгоритмы и архитектураmiddle
- WAF, межсетевые экраны, mTLS и HSTSmiddle
- Отравление DNS-кэша и BGP-перехватsenior
- Эшелонированная защита и экономика атакsenior
- Двенадцать слоёв: один URL, семь действующих лицjunior
- DNS, TCP, TLS по очереди: куда уходят миллисекундыmiddle
- Критический путь рендеринга и Core Web Vitalsmiddle
- Перехват прокси и шлюзы безопасности: rate limiter, WAF, mTLSmiddle
- Альтернативные пути: QUIC 0-RTT, WebSocket upgrade, миграция соединенияmiddle
- Наблюдаемость: распределённые трейсы, USE/RED и семплированиеsenior
- Устойчивость: каскадные повторы, circuit breakers и error budgetsenior
- Что такое три сигнала: метрики, логи, трейсыjunior
- Метрики и cardinality: cost-модель time-series databasemiddle
- Логи и объём: cost-модель структурного логированияmiddle
- Трейсы и сэмплирование: cost-модель distributed tracingmiddle
- Join-ключи и exemplar''''ы: как три сигнала становятся компонуемымиmiddle
- Observability 2.0: широкие события и сдвиг стоимостиsenior
- Режимы сбоя и инженерная практика: cardinality budget''''ы, PII и сэмплированиеsenior
- Зачем нужны структурные логи: дневник против таблицыjunior
- Схема продакшн-лога: поля, которые несёт каждая строкаmiddle
- Log levels и маршрутизация алертовmiddle
- Стратегии sampling и стоимость логовmiddle
- PII-редакция и log injectionsenior
- Propagation trace-контекста в логахsenior
- OTel Logs Data Model и audit-логи как подсистемаsenior
- Сигналы OTel, Semantic Conventions и проводной формат OTLPmiddle
- Авто-инструментирование и ручные спаны: правило 80/20 в OTelmiddle
- Collector OTel: receivers, processors, exporters и паттерны развёртыванияmiddle
- Стратегии сэмплирования: head, tail и parent-basedmiddle
- Vendor-нейтральность, eBPF-инструментирование, Operator и OTel в браузере и serverlesssenior
- Эксплуатация OTel Collector: надёжность, version skew, режимы отказа и управлениеsenior
- RED и USE: два чек-листа, одна дисциплина триажаjunior
- Инструментация RED в Prometheus: счётчики, гистограммы и дисциплина cardinalitymiddle
- USE на Linux: CPU, память, диск, сеть и PSImiddle
- Golden signals, структура дашборда и auto-RED в service meshmiddle
- Cardinality как драйвер затрат: label, PII, exemplars и семплированиеmiddle
- Native histograms, SLO и паттерны production-сбоевmiddle
- SLI, SLO и error budget: надёжность в числахjunior
- Выбор SLI и SLO-целей: отношения, не ощущенияmiddle
- Multi-window multi-burn-rate-алертинг: почему AND лучше ORmiddle
- Error budget policy, latency SLO и составные journeysmiddle
- Iceberg SLI, математика составного SLO и SLA vs SLOsenior
- Продакшн-отказы SLO, самонаблюдаемость, безопасность и общая картинаsenior
- Flame graph: читаем картинку, которая показывает, куда ушло времяjunior
- Sampling vs instrumentation profiling: почему 99 Гц побеждает в productionmiddle
- Типы профилей: CPU, память, off-CPU, mutex — какой когда братьmiddle
- Continuous profiling: always-on flame graphs с eBPF и корреляцией trace-idmiddle
- Как flame graph строится из сэмплов и как использовать его в productionmiddle
- Linux perf, внутренности eBPF, PGO и ограничения sampling''''аsenior
- Profiling в production: безопасность, war stories, OTel profiles и дизайн инфраструктурыsenior
- Debugging-воронка: SLO → RED → trace → profilejunior
- Архитектура OTel: один SDK, четыре сигнала, один wire-форматmiddle
- Экономия на observability: удерживаем затраты в пределах 5% inframiddle
- Петля инцидента: от пейджера до постмортема до предотвращенияmiddle
- Масштаб, безопасность и ROI наблюдаемых системsenior
- Сначала профиль: измерь куда реально уходит времяjunior
- Закон Амдала и self-time: потолок любого ускорения, которое ты можешь выпуститьmiddle
- Измерительный цикл: микробенч, макробенч, prod-профиль, эффект наблюдателяmiddle
- Чтение флейм-графов: формы, профайлеры по языкам и 60-секундный сканmiddle
- Статистические baseline''''ы: почему один запуск — не измерениеmiddle
- История профайлеров и ловушки микробенчей: от Кнута до GWPsenior
- Hardware counters, профили холодного старта и безопасность профилейsenior
- Непрерывное профилирование в масштабе: затраты, CI-гейты, корреляция с трейсами и антипаттерныsenior
- Что делает путь горячим: симптом против причиныjunior
- Пять форм hotspot''''а: CPU, аллокации, кэш, лок, syscallmiddle
- Чтение parent и child chains: где применять правкуmiddle
- JIT deopt, цикл fix-and-verify и PR-time профилированиеmiddle
- Аппаратные счётчики и Intel TMA: диагностика подкатегорийsenior
- False sharing и горячие пути нативных мостовsenior
- Горячие пути в production: безопасность, хвостовая латентность и происхождение инструментовsenior
- Иерархия памяти: почему расстояние важнее числа операцийjunior
- Row-major vs column-major: порядок доступа и разрыв в 9xjunior
- Cache lines и false sharing: когда параллелизм замедляет кодmiddle
- Branch prediction: 10–30 циклов штрафа за неожиданный ifmiddle
- SIMD и data layout: AoS vs SoA и разница в 4–8xmiddle
- Hardware prefetcher, TLB и memory-level parallelismsenior
- Cache-oblivious алгоритмы, PGO и production failuressenior
- Основы GC: за что рантайм берёт налогjunior
- Алгоритмы GC: поколенческая гипотеза, concurrent marking и write barriermiddle
- GC tradeoffs: пауза, throughput, память и давление аллокацийmiddle
- Настройка GC: пейсинг, форма кучи и наблюдаемость аллокацийmiddle
- Внутреннее устройство GC: tri-color инвариант, write barriers и глубокое погружение в рантаймыsenior
- GC в production: наблюдаемость, безопасность, edge cases и управление флотомsenior
- N+1: одна логическая операция, много round-trip''''овjunior
- Семейства фиксов: JOIN, IN, preload и DataLoadermiddle
- Обнаружение N+1: query logs, APM traces и CI gatesmiddle
- DataLoader: батчинг по дереву резолверовmiddle
- Кросс-протокольный N+1: HTTP fan-out и Redis MGETmiddle
- N+1 в масштабе: исчерпание пула, изменения планов и денормализацияsenior
- Batching: амортизируй фиксированную цену каждой операцииjunior
- Окно батчинга: размер и время ожиданияmiddle
- Batching в Kafka и Postgresmiddle
- io_uring и наблюдаемость пакетированияmiddle
- От Nagle до io_uring: эволюция пакетированияmiddle
- Backpressure, изоляция сбоев и безопасность батчей в продакшенеsenior
- Что на самом деле стоит bundle: download, parse, compile, executejunior
- Core Web Vitals: LCP, INP и CLSmiddle
- Code splitting: route-level, component-level, vendor splittingmiddle
- Tree shaking и compression: удаляем то, что не используемmiddle
- Third-party scripts: тихий убийца бюджетаmiddle
- CI enforcement и RUM: делаем бюджеты рабочимиmiddle
- V8 JIT-пайплайн, HTTP-приоритеты и безопасность bundlesenior
- Цикл performance: дисциплина, а не проектjunior
- Классификация и исправление: сопоставление family bottleneck с методамиmiddle
- Observability-стек и CI gates: ловить регрессии до выпускаmiddle
- От инцидента к enforcement: SLO burn до верифицированного исправления за 35 минутmiddle
- Культура, экономика и масштаб performancesenior
- At-most-once, at-least-once, exactly-once: три контракта доставкиjunior
- Три ножки сбоя — где реально происходят дубликаты и потериmiddle
- Consumer-side dedup: самый дешёвый путь к exactly-once processingmiddle
- Kafka exactly-once semantics: idempotent producer и транзакцииmiddle
- SQS visibility timeout, DLQ и outbox patternmiddle
- Exactly-once в production: impossibility-доказательство, гибридные паттерны и реальные инцидентыsenior
- Что такое OAuth и почему пароли — не ответjunior
- Authorization code flow с PKCEmiddle
- Валидация ID-токена и управление JWKS-кешемmiddle
- Ротация refresh-токенов и scope-based least privilegemiddle
- Sender-constrained токены: DPoP и mTLSsenior
- OAuth в production: audience атаки, observability и реальные провалыsenior
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.