В моем офисе ведутся постоянные дебаты о стоимости индекса, а также о том, является ли уникальность выгодной или дорогой (вероятно, и то и другое). Суть проблемы - наши конкурирующие ресурсы.
Фон
Ранее я читал обсуждение, в котором говорилось, что Unique
индекс не требует дополнительных затрат на поддержание, поскольку Insert
операция неявно проверяет, подходит ли она к B-дереву, и, если дубликат найден в неуникальном индексе, добавляет к ним уникальный код конец ключа, но в противном случае вставляется напрямую. В этой последовательности событий Unique
индекс не имеет дополнительных затрат.
Мой коллега борется с этим утверждением, говоря, что он Unique
выполняется как вторая операция после поиска новой позиции в B-дереве и, следовательно, более дорогостоящий в обслуживании, чем неуникальный индекс.
В худшем случае я видел таблицы со столбцом идентификаторов (изначально уникальным), который является ключом кластеризации таблицы, но явно указан как неуникальный. С другой стороны, хуже всего - моя одержимость уникальностью, и все индексы создаются как уникальные, и когда невозможно определить явно уникальную связь с индексом, я добавляю PK таблицы в конец индекса, чтобы обеспечить Уникальность гарантирована.
Я часто участвую в обзорах кода для команды разработчиков, и мне нужно дать общие рекомендации, которым они должны следовать. Да, каждый индекс должен оцениваться, но когда у вас есть пять серверов с тысячами таблиц на каждом и целых двадцать индексов в таблице, вам необходимо иметь возможность применять некоторые простые правила для обеспечения определенного уровня качества.
Вопрос
Есть ли у уникальности дополнительные расходы на бэкэнд по Insert
сравнению со стоимостью поддержки неуникального индекса? Во-вторых, что плохого в добавлении первичного ключа таблицы в конец индекса для обеспечения уникальности?
Пример таблицы определения
create table #test_index
(
id int not null identity(1, 1),
dt datetime not null default(current_timestamp),
val varchar(100) not null,
is_deleted bit not null default(0),
primary key nonclustered(id desc),
unique clustered(dt desc, id desc)
);
create index
[nonunique_nonclustered_example]
on #test_index
(is_deleted)
include
(val);
create unique index
[unique_nonclustered_example]
on #test_index
(is_deleted, dt desc, id desc)
include
(val);
пример
Пример того, почему я хотел бы добавить Unique
ключ в конец индекса, приведен в одной из наших таблиц фактов. Существует , Primary Key
что это Identity
столбец. Однако Clustered Index
вместо этого используется столбец схемы разделения, за которым следуют три измерения внешнего ключа без уникальности. Выбор производительности в этой таблице ужасен, и я часто получаю лучшее время Primary Key
поиска, используя поиск с ключом, а не используя Clustered Index
. Другие таблицы, которые имеют схожий дизайн, но Primary Key
дополнены до конца, имеют значительно лучшую производительность.
-- date_int is equivalent to convert(int, convert(varchar, current_timestamp, 112))
if not exists(select * from sys.partition_functions where [name] = N'pf_date_int')
create partition function
pf_date_int (int)
as range right for values
(19000101, 20180101, 20180401, 20180701, 20181001, 20190101, 20190401, 20190701);
go
if not exists(select * from sys.partition_schemes where [name] = N'ps_date_int')
create partition scheme
ps_date_int
as partition
pf_date_int all
to
([PRIMARY]);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.bad_fact_table'))
create table dbo.bad_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
fk_id int not null,
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_bad_fact_table] clustered (date_int, group_id, group_entity_id, fk_id)
)
on ps_date_int(date_int);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.better_fact_table'))
create table dbo.better_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_better_fact_table] clustered(date_int, group_id, group_entity_id, id)
)
on ps_date_int(date_int);
go
Case
и какIf
структуры ограничены 10 уровнями, имеет смысл, что есть также ограничение для разрешения неуникальных объектов. По вашему утверждению, это звучит так, как будто это относится только к случаям, когда ключ кластеризации не является уникальным. Это проблема дляNonclustered Index
или, если ключ кластеризации,Unique
то нет проблемы дляNonclustered
индексов?Я не собираюсь вдаваться в вопрос о том, должен ли индекс быть уникальным или нет, и есть ли дополнительные издержки в этом подходе или в этом. Но пара вещей беспокоила меня в вашем общем дизайне
WHERE is_deleted = 0
) и рассмотрим использование отфильтрованного индекса. Я бы даже подумал об использовании 2 отфильтрованных индексов, один для,where is_deleted = 0
а другой дляwhere is_deleted = 1
По сути, это больше похоже на упражнение по кодированию, предназначенное для проверки гипотезы, а не на реальную проблему / решение, но эти два шаблона, безусловно, то, что я ищу в обзорах кода.
источник
Nonclustered
Индекс будет иметь ключ кластерного добавляемый к концу строки данных для ключевых операций поиска внутренне. Таким образом, два индекса физически совпадают, что и было вопросом моего вопроса.Похоже, вы просто используете PK для создания альтернативного, меньшего индекса. Следовательно, производительность на нем быстрее.
Вы видите это в компаниях, которые имеют массивные таблицы данных (например, таблицы основных данных). Кто-то решает иметь один массивный кластерный индекс, ожидая, что он удовлетворит потребности различных групп отчетности.
Но одной группе может потребоваться только несколько частей этого индекса, в то время как другой группе нужны другие части ... поэтому индекс, просто шлепающий по каждому столбцу под солнцем, чтобы «оптимизировать производительность», на самом деле не помогает.
Между тем, разбив его на несколько меньших целевых индексов, часто решается проблема.
И это похоже на то, что вы делаете. У вас есть этот массивный кластерный индекс с ужасной производительностью, а затем вы используете PK для создания другого индекса с меньшим количеством столбцов, который (что неудивительно) имеет лучшую производительность.
Итак, просто проведите анализ и выясните, можете ли вы взять один кластеризованный индекс и разбить его на более мелкие, целевые индексы, необходимые для конкретных заданий.
Тогда вам придется анализировать производительность с точки зрения «один индекс против нескольких индексов», поскольку при создании и обновлении индексов возникают накладные расходы. Но вы должны проанализировать это с общей точки зрения.
Например: он может быть менее ресурсоемким для одного массивного кластерного индекса и более ресурсоемким, чтобы иметь несколько меньших целевых индексов. Но если вы тогда сможете выполнять целевые запросы на внутреннем сервере гораздо быстрее, экономя время (и деньги), это может стоить того.
Таким образом, вам придется провести сквозной анализ ... не только посмотреть, как это влияет на ваш собственный мир, но и как это влияет на конечных пользователей.
Я просто чувствую, что вы неправильно используете идентификатор PK. Но вы можете использовать систему баз данных, которая допускает только 1 индекс (?), Но вы можете использовать другую, если вы используете PK (в наши дни каждая реляционная база данных автоматически индексирует PK). Однако большинство современных РСУБД должны допускать создание нескольких индексов; не должно быть никаких ограничений на количество индексов, которые вы можете создать (в отличие от ограничения в 1 PK).
Таким образом, создавая PK, который действует как альтернативный индекс ... вы используете свой PK, который может понадобиться, если впоследствии таблица расширится в своей роли.
Это не значит, что вашему столу не нужен PK. В SOP DB 101 говорится, что «в каждой таблице должен быть PK». Но в ситуации с хранилищем данных или тому подобном ... наличие PK на столе может быть просто дополнительными издержками, которые вам не нужны. Или это может быть просто посыл, чтобы убедиться, что вы не добавляете двойные записи дважды. Это действительно вопрос того, что ты делаешь и почему ты это делаешь.
Но массивные таблицы определенно выигрывают от наличия индексов. Но если предположить, что один массивный кластеризованный индекс будет наилучшим, это просто ... он может быть БЕЗОПАСНЫМ ... но я бы порекомендовал протестировать тестовую среду, разбив ее на несколько меньших индексов, нацеленных на конкретные сценарии использования.
источник