Выбор кластерного индекса - ПК или ФК?

11

У меня есть таблица SQL Server 2014, которая выглядит следующим образом:

OrderId     int           not null IDENTITY --this is the primary key column
OrderDate   datetime2     not null
CustomerId  int           not null
Description nvarchar(255) null

Некоторые члены моей команды предложили включить кластерный индекс OrderId, но я думаю, что CustomerId+ OrderIdбудет лучшим выбором по следующим причинам:

  • Почти все запросы будут искать WHERE CustomerId = @param, а неOrderId
  • CustomerIdявляется внешним ключом Customerтаблицы, поэтому наличие кластеризованного индекса с CustomerIdускоряет соединения
  • Хотя CustomerIdэто и не уникально, наличие дополнительного OrderIdстолбца, указанного в индексе, обеспечит уникальность (мы можем использовать UNIQUEключевое слово при создании кластеризованного индекса для этих двух столбцов, чтобы избежать издержек, связанных с отсутствием уникальности)
  • После того как данные вставлено, CustomerIdи OrderIdникогда не изменится, так что эти строки не будут двигаться вокруг после первоначальной записи.
  • Доступ к данным происходит через ORM, который запрашивает все столбцы по умолчанию, поэтому, когда CustomerIdпоступает запрос на основе , кластерный индекс сможет предоставить все столбцы без какой-либо дополнительной работы.

Ли CustomerIdи OrderIdданный подход звучит как лучший вариант выше? Или OrderIdлучше сам по себе, поскольку это один столбец, который сам по себе гарантирует уникальность?

В настоящее время для таблицы включен кластеризованный индекс OrderIdи некластеризованный индекс CustomerId, но он не охватывает, поэтому, поскольку мы используем ORM и запрашиваем все столбцы, их дополнительная работа требует дополнительной работы. Итак, в этом посте я пытаюсь рассмотреть вопрос об улучшении производительности с помощью лучшего CI.

Активность в нашей БД составляет около 85% операций чтения и 15% операций записи.

Энди
источник

Ответы:

5

Ответ сообщества вики :

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

Может быть больше разделений по сравнению с инкрементным ключом (или, скорее всего, неоптимальная плотность страниц на время, если вы управляете и сохраняете коэффициент заполнения, чтобы избежать «неправильных» разделений). Тем не менее, общее улучшение производительности для запросов клиентов является существенным, потому что поиск ключа избегается.

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

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

Если вы выберете OrderID в качестве кластеризованного индекса с некластеризованным индексом для CustomerID , вы все равно получите разделения и фрагментацию только в некластеризованном индексе.

user126897
источник
3

Если эта таблица интенсивно пишет (например INSERT, происходит гораздо больше SELECTутверждений, чем утверждений против нее), я не согласен с ответом в вики .

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

Если вы считаете, что CustomerID должен быть ведущим столбцом составного кластерного индекса, вы можете уменьшить влияние разбиений в середине страницы, настроив FILLFACTORвсе индексы для этой таблицы. Это уменьшит количество разделений в середине страницы за счет увеличения размера таблицы / индекса. Если вы хотите пойти по этому пути, я бы посоветовал провести тестирование со значением 80 и уменьшить его, если анализ покажет, что разделение на середине страницы все еще снижает производительность.

Я предлагаю использовать OrderId. OrderID, естественно, должен быть последовательным и генерировать больше разбиений на конечных страницах, которые хороши и ожидаемы с ростом таблицы. Кроме того, этот подход будет лучше работать с разделением таблиц, если вы решите использовать столбец OrderDate в качестве ключа раздела. Что касается запросов, которые постоянно используют поле CustomerID, создайте некластеризованный индекс для обработки этих запросов. Этот индекс должен быть определен надлежащим образом, так FILLFACTORкак он будет страдать от разбиений в середине страницы, о которых я упоминал выше, хотя в целом они не будут такими плохими, в отличие от того, происходили ли разделения по кластерному индексу.

Активность в нашей БД составляет около 85% операций чтения и 15% операций записи.

CustomerID+ OrderID(и указание фактора заполнения, обеспечивающего рост без разбиений), вероятно, будет лучше, если эта оценка верна. Просто убедитесь , что оценка точна. Тест Тест Тест.

Джон Айсбренер
источник
1
Обратите внимание, что вставка заказа для последнего (или единственного) Клиента на странице не является "разделением на середину страницы". Таким образом, если количество заказов на одного клиента велико или ширина строки велика, для меньшего количества вставок заказов потребуется «промежуточное разбиение страницы».
Дэвид Браун - Microsoft