Калькулятор размера SQL индекса позволяет быстро оценить размер индекса в PostgreSQL и MySQL по количеству строк, типу данных и структуре индекса. Выберите тип данных, укажите количество строк и получите результат за 10 секунд без формул.
Оцените примерный размер индекса в PostgreSQL/MySQL на основе количества строк, типа данных и структуры индекса — за 10 секунд без формул.
Калькулятор использует следующую математическую модель, основанную на внутреннем устройстве страниц PostgreSQL (8 КБ):
размер_ключа = размер_столбца × количество_столбцов
размер_записи = размер_ключа + overhead_типа_индекса + 4 (указатель CTID)
полезное_место_на_странице = (8192 − 28) × fillfactor / 100
записей_на_странице = ⌊полезное_место_на_странице / размер_записи⌋ (минимум 1)
количество_страниц = ⌈количество_строк / записей_на_странице⌉
размер_индекса = количество_страниц × 8192 байт
Overhead по типам индексов: B-tree — 8 байт, Hash — 4 байта, GiST — 16 байт, GIN — 20 байт, BRIN — 8 байт (плюс 4 байта CTID-указателя для каждого). Заголовок страницы — 28 байт (PageHeaderData).
Расчёт проходит в четыре этапа. Сначала определяется размер ключа — сумма байтовых размеров всех столбцов индекса. Для VARCHAR(N) это N + 4 байта (4 байта хранят фактическую длину строки), для INT — ровно 4 байта.
Затем добавляется overhead типа индекса. B-tree требует 8 байт на внутренние указатели дерева плюс 4 байта на CTID (физический адрес строки в куче). GiST и GIN имеют больший overhead из-за сложной внутренней структуры.
На третьем этапе вычисляется ёмкость страницы. Стандартный блок PostgreSQL — 8192 байта. Вычитаем 28 байт заголовка, умножаем на fillfactor. Получаем полезное пространство, доступное для записей индекса.
Наконец, определяется количество страниц делением общего числа строк на количество записей на странице с округлением вверх. Умножение на 8192 даёт итоговый размер индекса в байтах.
Почему мой реальный индекс больше расчётного? Скорее всего, в таблице много «мёртвых» строк после UPDATE/DELETE, и индекс раздут. Выполните VACUUM или REINDEX для восстановления размера. Также проверьте актуальность статистики через ANALYZE.
Какой fillfactor выбрать для таблицы с частыми вставками? Для активно пополняемых таблиц (логи, события) рекомендуем fillfactor 70-80%. Это замедлит рост индекса и уменьшит фрагментацию. Для статических архивных таблиц можно использовать 100%.
BRIN или B-tree для временных рядов? Для данных, где строки вставляются последовательно по времени (события, метрики), BRIN даёт выигрыш по размеру в 50-200 раз при сопоставимой скорости чтения диапазонов. B-tree оправдан, если нужны точечные запросы.
Сколько столбцов можно включать в составной индекс? Технически — до 32, практически — не более 5-7. Каждый дополнительный столбец увеличивает размер индекса и замедляет вставки. Составной индекс на 10 столбцов размером с таблицу — это антипаттерн.
Влияет ли порядок столбцов в составном индексе на размер? На размер — нет. Сумма байтов та же. Но порядок критичен для производительности: индекс используется только если условия в WHERE начинаются с первого столбца индекса (правило левого префикса).
Можно ли использовать калькулятор для MySQL? Да, с оговорками. MySQL/InnoDB использует страницы по 16 КБ (вместо 8 КБ) и кластерный индекс (PRIMARY KEY включён во все вторичные индексы). Для MySQL умножайте результат на 1,5-2 для вторичных индексов.
Расчёт основан на официальной документации PostgreSQL (раздел «Database Page Layout», статьи 68.1-68.4), исходном коде PostgreSQL (файл bufpage.h, структура PageHeaderData — 24 байта + 4 байта выравнивания = 28 байт), а также исследованиях производительности индексов (Use The Index, Luke!). Overhead для B-tree взят из статьи «B-tree index internals» (PostgreSQL Wiki), для GiST/GIN — из документации PostgreSQL, раздел «GiST Indexes» и «GIN Indexes». Размеры типов данных соответствуют pg_type.typlen и документации по типам данных PostgreSQL 16.
Индекс — это отдельная структура данных, которая физически хранится на диске рядом с таблицей. Каждый созданный индекс занимает место, иногда сопоставимое с размером самой таблицы. На проекте с 50 таблицами и 200 индексами суммарный размер индексов может превышать 500 ГБ — и это напрямую влияет на стоимость хранения в облаке и время бэкапов.
Знание примерного размера индекса до его создания помогает ответить на три вопроса: хватит ли дискового пространства, поместится ли индекс в оперативную память сервера и стоит ли вообще его создавать или лучше обойтись частичным индексом. Практика показывает, что 15-20% индексов в продуктовых базах либо не используются, либо дублируют друг друга.
PostgreSQL хранит индексы в виде страниц фиксированного размера — 8192 байта (8 КБ). Это минимальная единица чтения и записи. Каждая страница содержит заголовок (28 байт), массив указателей на записи и сами записи индекса. Свободное пространство между записями и концом страницы используется для будущих вставок.
Когда новая строка попадает в индекс, PostgreSQL ищет подходящую страницу и размещает запись там. Если свободного места недостаточно, страница расщепляется на две — это дорогая операция, которая блокирует запись и увеличивает размер индекса. Fillfactor как раз управляет тем, сколько места резервируется под будущие вставки.
Для B-tree индекса каждая запись содержит: значение ключа (ваши данные), CTID — указатель на физическое расположение строки в таблице (6 байт: номер блока + смещение), и служебные биты для навигации по дереву (ещё 2-4 байта). Суммарно overhead составляет 8-12 байт на запись.
B-tree — универсальный тип, покрывающий 90% потребностей. Поддерживает операции =, <, >, BETWEEN, ORDER BY. Размер линейно зависит от количества строк. Overhead: ~12 байт на запись.
Hash — работает только для точного сравнения (=). Компактнее B-tree на 15-20% за счёт меньшего overhead (~8 байт). Хорош для первичных ключей-суррогатов, если не нужна сортировка. В PostgreSQL до версии 10 Hash-индексы не журналировались и считались небезопасными — сейчас это полноценный тип.
GiST — обобщённое поисковое дерево. Используется для геоданных (PostGIS), полнотекстового поиска и пользовательских типов. Overhead: ~20 байт на запись. Размер может быть в 1,5-2 раза больше B-tree на тех же данных из-за сложных внутренних структур.
GIN — инвертированный индекс. Идеален для массивов, JSONB и полнотекстового поиска. Каждый элемент массива или каждый токен документа порождает отдельную запись в индексе, поэтому размер GIN-индекса может многократно превышать размер таблицы. Overhead: ~24 байта на запись.
BRIN — блочный диапазонный индекс. Хранит не отдельные строки, а диапазоны значений для групп страниц (по умолчанию 128 страниц в группе). Чрезвычайно компактный: индекс на 100 млн строк может занимать 3-5 МБ. Подходит только для данных с физической корреляцией (временные ряды, автоинкрементные ID).
Всегда проверяйте, используется ли индекс. Запрос SELECT * FROM pg_stat_user_indexes WHERE idx_scan = 0 показывает индексы, которые ни разу не использовались с момента сброса статистики. Такие индексы — кандидаты на удаление.
Рассмотрите частичные индексы. Вместо индекса на всю таблицу создайте индекс с WHERE-условием: CREATE INDEX idx_active ON orders(status) WHERE status = 'active'. Если активных заказов 5% от общего числа, индекс будет в 20 раз меньше полного.
Для TIMESTAMP и автоинкрементных ID используйте BRIN. Таблица логов с 500 млн строк и B-tree индексом на timestamp требует ~12 ГБ диска. BRIN-индекс на том же столбце — всего 40-60 МБ, а производительность выборки за конкретный день сопоставима.
Мониторьте раздувание (bloat). После интенсивных UPDATE и DELETE индекс может занимать в 2-3 раза больше своего «чистого» размера. Расширение pgstattuple и запросы на основе pg_class помогают выявить раздутые индексы. Регулярный REINDEX CONCURRENTLY возвращает размер к норме без блокировки записи.
Средний интернет-магазин имеет таблицу orders на 5 млн строк. Индексы: PRIMARY KEY (id) — 214 МБ, индекс на customer_id — 168 МБ, индекс на created_at — 156 МБ, составной индекс на (status, created_at) — 245 МБ. Итого: 783 МБ только на индексы одной таблицы.
После аудита выяснилось, что индекс на customer_id используется только в админке (0,1% запросов), а составной индекс дублирует created_at на 80%. Решение: удалить индекс на customer_id, заменить составной на частичный WHERE status IN ('pending','processing'). Итог: 783 → 420 МБ, экономия 363 МБ на одной таблице.
Размер индекса — это не абстрактная цифра, а прямой фактор стоимости владения базой данных. Один непродуманный индекс на большую таблицу может стоить десятки гигабайт диска и тысячи рублей ежемесячно в облачном тарифе. Используйте калькулятор на этой странице для быстрой оценки, сравнивайте типы индексов и не создавайте индексы «на всякий случай». Помните: каждый индекс ускоряет чтение, но замедляет запись и занимает место. Баланс — ключ к эффективной работе с SQL.
Задайте вопрос по этому калькулятору
Осталось вопросов: 5. Только по этому инструменту.
Нужен другой инструмент?
Все инструменты в категории