В предыдущих статьях мы сравнили фильтрованные индексы и индексированные представления, поняв, в каких случаях каждый из них проявляет себя наилучшим образом.
Если вы пропустили Часть 2, вы можете прочитать её здесь:
👉 Фильтрованные индексы: сравнение производительности с примерами (Часть 2)
Сегодня мы добавляем в игру нового игрока.
Потому что, когда объём данных начинает расти… когда миллионы строк превращаются в десятки или сотни миллионов… мы получаем мощного нового союзника:
Колоночные индексы (Columnstore Indexes).
И это меняет всё.
Когда строчного хранения (Rowstore) недостаточно
До сих пор мы работали с традиционными строчными структурами:
- Фильтрованный индекс → уменьшает ввод-вывод для избирательных предикатов
- Индексированное представление → предварительно вычисляет агрегаты
Оба чрезвычайно мощны в сценариях OLTP. Но когда наборы данных значительно увеличиваются, построчная обработка становится узким местом.
Вот тут-то и появляется колоночное хранение.
Что делает колоночное хранение особенным?
Традиционные индексы хранят данные построчно (структура B-дерева).
Колоночный индекс:
- Хранит данные по колонкам
- Применяет сильное сжатие
- Включает пакетный режим выполнения (Batch Mode)
- Обрабатывает данные векторами вместо отдельных строк
- Кардинально улучшает выполнение больших агрегатов
Существует два основных типа:
- Кластерный колоночный индекс (CCI) → заменяет хранение таблицы
- Некластерный колоночный индекс (NCCI) → добавляется поверх строчных таблиц
Колоночное хранение предназначено для отчётных нагрузок, сценариев хранилищ данных, больших сканирований и аналитических запросов к миллионам строк.
Сценарий 3 – Крупномасштабные агрегаты
Давайте повторно используем нашу таблицу dbo.Sales с 1 миллионом строк. Теперь представим часто выполняемый отчётный запрос:
SELECT
YEAR(SaleDate) AS SaleYear,
MONTH(SaleDate) AS SaleMonth,
SUM(Amount) AS TotalAmount,
COUNT(*) AS TotalSales
FROM dbo.Sales
GROUP BY
YEAR(SaleDate),
MONTH(SaleDate);
Этот запрос сканирует всю таблицу, агрегирует всё, потребляет ресурсы CPU в строчном режиме и может запрашивать большие объёмы памяти.
Вариант 1 – Фильтрованный индекс
Здесь он не даёт никакой выгоды. Нет избирательного фильтра и нет уменьшенного подмножества. Фильтрованные индексы не предназначены для аналитики по всей таблице.
Вариант 2 – Индексированное представление
CREATE VIEW dbo.vw_MonthlySales
WITH SCHEMABINDING
AS
SELECT
YEAR(SaleDate) AS SaleYear,
MONTH(SaleDate) AS SaleMonth,
SUM(Amount) AS TotalAmount,
COUNT_BIG(*) AS TotalSales
FROM dbo.Sales
GROUP BY
YEAR(SaleDate),
MONTH(SaleDate);
GO
CREATE UNIQUE CLUSTERED INDEX IX_vw_MonthlySales
ON dbo.vw_MonthlySales (SaleYear, SaleMonth);
Результат:
- Агрегаты предварительно вычислены
- Минимальное использование CPU во время выполнения
- Чрезвычайно быстрый SELECT
- Но с высокими накладными расходами на DML, дополнительным хранилищем и ограничениями привязки к схеме (schema binding).
Вариант 3 – Некластеризованный колоночный индекс
CREATE NONCLUSTERED COLUMNSTORE INDEX IX_Sales_Columnstore
ON dbo.Sales (SaleDate, Amount);
План выполнения переключается на:
- Пакетный режим (Batch Mode)
- Элиминацию колонок
- Векторизованную агрегацию
- Сильно сжатые сегменты
Вместо обработки строки за строкой SQL Server обрабатывает тысячи строк за пакет. Это то, что меняет правила игры.
Заключительные мысли
Фильтрованный индекс, индексированное представление и колоночный индекс — не конкуренты. Они решают разные проблемы производительности.
Когда данные растут, сложность растёт. Но, к счастью, когда данные растут…
…мы также получаем мощного союзника: колоночное хранение.
И в крупномасштабных системах этот союзник может стать решающим фактором между минутами и миллисекундами.

Комментариев нет:
Отправить комментарий