Проектирование некластерного индекса SQL Server - PullRequest
6 голосов
/ 18 июля 2011

Этот вопрос касается проектирования некластеризованных индексов в SQL Server 2005.

У меня большая таблица с несколькими миллионами строк.Строки только когда-либо читаются или вставляются.Большинство операций чтения.Я просматривал различные SELECT запросы, которые обращаются к таблице с целью повышения скорости доступа для чтения.Дисковое пространство на самом деле не проблема.(Каждая строка имеет уникальный идентификатор, и я использую его в качестве единого поля в кластеризованном индексе.)

Мой вопрос: если некластеризованный индекс индексирует больше столбцов, чем используется запросом,что приводит к более медленному выполнению запроса, чем индекс, который точно соответствует запросу?

По мере увеличения числа различных запросов увеличивается и количество перестановок столбцов, используемых в их предложениях WHERE.Я не уверен относительно компромисса между наличием множества индексов с небольшим количеством столбцов (по одному на каждый запрос) и меньшим количеством индексов для большего числа столбцов.

Например, скажем, у меня два запроса SELECT.Первый использует столбцы A, B, C и D в своем предложении WHERE, а второй использует A, B, E и F. В этом случае лучше всего определить два индекса, один для A / B / C /D и другой на A / B / E / F;или один индекс на A / B / C / D / E / F?

Ответы [ 4 ]

3 голосов
/ 18 июля 2011

Перво-наперво, порядок столбцов в индексах имеет значение. Поэтому создание / настройка ваших запросов позволит вам эффективно использовать созданные вами индексы.

Наличие двух индексов по отдельности или одного индекса зависит от зависимостей конкурирующих столбцов и типа выполняемых запросов. В вашем примере, если столбцы E и F относятся или зависят от столбцов C и D, то имеет смысл иметь один индекс, охватывающий все столбцы.

1 голос
/ 15 апреля 2012

Дисковое пространство на самом деле не проблема.

Пожалуйста, не думайте так.Неважно, если у вас есть 500 ГБ свободного места.Чем больше таблица или индекс, тем больше времени требуется для чтения с диска И чем больше места занимает память (т. Е. Буферный пул) И тем больше логических чтений потребуется для удовлетворения запроса.Подробнее об этом см. Здесь: http://www.sqlservercentral.com/articles/data-modeling/71725/

(каждая строка имеет уникальный идентификатор, и я использую его в качестве отдельного поля в кластерном индексе.)

Использует ли большинство ваших запросов этот идентификатор в предложении WHERE?Если нет, то это может быть не лучшим выбором для кластеризованного индекса.

Мой вопрос: если некластеризованный индекс индексирует больше столбцов, чем используется запросом, это приводит к медленному запросувыполнение, чем индекс, который точно соответствует запросу?

Это зависит от нескольких факторов.Сколько еще полей вы говорите?Одно поле TINYINT, которое составляет 1 байт?Или несколько полей по 300 байт?Если вы не используете отфильтрованные индексы, вам нужно умножить размер вашего индекса плюс размер вашего кластеризованного индекса (для не-УНИКАЛЬНЫХ индексов) на количество строк.Как я упоминал выше, увеличение занимаемого пространства означает более медленное, но реально дополнительные 5 МБ на 100 МБ, вероятно, не будут иметь заметного различия.

Имейте в виду, что создание индекса - это и искусство, и наука.Вам необходимо учитывать, какие запросы будут выполняться чаще всего и какие ORDER BY используются, а также предложения WHERE.Необходимо помнить, что индекс не будет использоваться, если его главный столбец отсутствует в запросе, даже если остальные поля индекса находятся в запросе.

Вообще говоря,Вы НЕ хотите индексировать каждое поле отдельно, потому что:

  1. слишком много индексов замедляют операции DML, что является проблемой, даже если большинство операций SELECT для этой таблицы
  2. слишком много индексовувеличивает вероятность мертвых блокировок
  3. запрос с 4 полями не будет использовать 4 отдельных индекса.Большую часть времени оптимизатор выбирает тот, который, по его мнению, будет работать лучше всего, и иногда он может захотеть объединить два из них вместе, особенно если у вас есть условие ИЛИ

Например,скажем, у меня есть два запроса SELECT.Первый использует столбцы A, B, C и D в своем предложении WHERE, а второй использует A, B, E и F.

Вы могли бы лучше всего индексировать только A и B ивидя, как это работает.Если эта комбинация уникальна, тогда рассмотрите возможность для составного кластерного индекса.Если они не уникальны, но все еще используются большинством запросов, рассмотрите возможность создания кластеризованного индекса: A, B, IDfield.Включение последнего в поле ID дает уникальность комбинации.Это важно, потому что если ваш кластеризованный индекс не является первичным ключом, то вам ДЕЙСТВИТЕЛЬНО нужно объявить кластеризованный индекс как UNIQUE, чтобы в нем не было скрытого поля уникализатора.Первичный ключ по определению уникален.

Также обратите внимание на параметр INCLUDE для индексов.

И да, порядок столбцов имеет значение, поскольку он определяет, как организован индекс.Подумайте о разнице между наличием ActionDate, CustomerID и CustomerID, ActionDate.Если ActionDate является первым, тогда легче найти все CustomerID в пределах определенного диапазона дат.Но если вы заботитесь только об одном клиенте и хотите получить несколько разных дат его информации, вам придется пропустить весь этот индекс, так как его данные будут распределены по всему.В этом случае вам лучше сначала воспользоваться CustomerID, так как вы сможете быстрее сузить место, где начинаются их данные, а затем просто получить нужные данные на основе дат.

Но нет, порядок вашего условия WHERE НЕ имеет отношения к тому, будет ли использоваться индекс.SQL Server использует оптимизатор на основе затрат, который сканирует все условия и использует статистику индексов (начальный столбец), чтобы определить, каким должен быть наиболее подходящий план.

Наконец, обязательно ПРОВЕРЬТЕ различные стратегии.Не просто попробуй одну вещь и двигайся дальше.Вы были очень общими в своем описании - даже не указав типы данных для полей или то, как эти поля используются - поэтому любые рекомендации, которые здесь очень специфичны, сомнительны.Используйте SET STATISTICS IO ON и ищите логические чтения.Чем ниже это число, тем лучше!

1 голос
/ 14 апреля 2012

Существующие ответы уже очень хороши.Вот новая мысль: поиск оптимального набора индексов при определенной рабочей нагрузке и доступности памяти - сложная проблема, требующая исчерпывающего поиска большого пространства поиска.

Советник по настройке ядра СУБД (DTA) реализует именно это!Я рекомендую вам записать репрезентативную рабочую нагрузку (включая записи!) И позволить DTA дать вам рекомендации.Также будет учитываться дисковое пространство.

1 голос
/ 14 апреля 2012

У меня такой вопрос: если некластеризованный индекс индексирует больше столбцов, чем используется запросом, означает ли это медленное выполнение запроса, чем индекс, который точно соответствует запросу?

Нет, наличие большего количества столбцов не замедляет время запроса для запросов, которые используют первые 1, 2, n столбцов в индексе.При этом, если вы ограничены в памяти, загрузка индекса в память может вытолкнуть другие вещи из памяти и замедлить запрос, но если у вас достаточно памяти, это не должно быть проблемой.

По мере увеличения числа отдельных запросов увеличивается и количество перестановок столбцов, используемых в их предложениях WHERE.Я не уверен относительно компромисса между наличием множества индексов с небольшим количеством столбцов (по одному на каждый запрос) и меньшим количеством индексов для большего количества столбцов.

Вы должны добавить наиболее часто запрашиваемые уникальные поляв индексы в первую очередь.Меньшее количество индексов с большим количеством столбцов может не дать вам того, что вы хотите.

Например, если у вас есть индекс со следующими столбцами:

  • СтолбецA
  • Столбец B
  • ColumnC
  • ColumnD
  • ColumnE
  • ColumnF

в таком порядке, запрашивает фильтрацию по ColumnA, ColumnB, ColumnC, ColumnD... будет использовать индекс, но если вы просто запрашиваете у ColumnE или ColumnF, он не будет использовать индекс.

Используйте другой подход, если у вас есть шесть индексов для одной таблицы с одним столбцом

  • Index1 - столбец A
  • Index2 - столбец B
  • Index3 - столбец C
  • Index4 - столбец D
  • Index5 - столбец E
  • Index6 - ColumnF

, в этом случае только один из этих 6 индексов будет использоваться для любого запроса.

Кроме того, если индекс содержит значение, которое не очень избирательно,тогда это может не помочь вам.Например, если у вас есть столбец с именем GENDER, который может содержать следующие значения (Мужской, Женский и Неизвестный), то, вероятно, он не поможет вам включить этот столбец в индекс.Когда запрос выполняется, SQL Server может определить, что столбец недостаточно селективен, и просто предположить, что полное сканирование таблицы будет быстрее.

Существует множество способов выяснить, какие индексы используются вашим запросом,но один из подходов, который я использую, - это посмотреть на индексы, которые никогда не используются.Запустите следующий запрос в своей базе данных и выясните, действительно ли используются используемые вами индексы.

SELECT iv.table_name, 
        i.name                           AS index_name, 
        iv.seeks + iv.scans + iv.lookups AS total_accesses, 
        iv.seeks, 
        iv.scans, 
        iv.lookups, 
        t.indextype, 
        t.indexsizemb 
FROM   (SELECT i.object_id, 
                Object_name(i.object_id) AS table_name, 
                i.index_id, 
                SUM(i.user_seeks)        AS seeks, 
                SUM(i.user_scans)        AS scans, 
                SUM(i.user_lookups)      AS lookups 
        FROM   sys.tables t 
                INNER JOIN sys.dm_db_index_usage_stats i 
                    ON t.object_id = i.object_id 
        GROUP  BY i.object_id, 
                    i.index_id) AS iv 
        INNER JOIN sys.indexes i 
            ON iv.object_id = i.object_id 
            AND iv.index_id = i.index_id 
        INNER JOIN (SELECT sys_schemas.name AS schemaname, 
                            sys_objects.name AS tablename, 
                            sys_indexes.name AS indexname , 
                            sys_indexes.type_desc AS indextype , 
    CAST(partition_stats.used_page_count * 8 / 1024.00 AS DECIMAL(10, 3)) AS indexsizemb 
FROM   sys.dm_db_partition_stats partition_stats 
INNER JOIN sys.indexes sys_indexes 
    ON partition_stats.[object_id] = sys_indexes.[object_id] 
        AND partition_stats.index_id = sys_indexes.index_id 
        AND sys_indexes.type_desc <> 'HEAP' 
INNER JOIN sys.objects sys_objects 
    ON sys_objects.[object_id] = partition_stats.[object_id] 
INNER JOIN sys.schemas sys_schemas 
    ON sys_objects.[schema_id] = sys_schemas.[schema_id] 
        AND sys_schemas.name <> 'SYS') AS t 
ON t.indexname = i.name 
AND t.tablename = iv.table_name 
--WHERE t.IndexSizeMB > 200 
WHERE  iv.seeks + iv.scans + iv.lookups = 0 
ORDER  BY total_accesses ASC; 

Обычно я отслеживаю индексы, которые никогда не использовались или не использовались несколько раз.месяцев после перезагрузки SQL Server, и определите, следует ли их удалить или нет.Иногда слишком много индексов может замедлить работу SQL Server, чтобы определить оптимальный путь для выполнения запроса, а удаление неиспользуемых индексов может ускорить этот процесс.

Надеюсь, это поможет понять ваши индексы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...