J'ai eu un débat en cours avec divers développeurs dans mon bureau sur le coût d'un indice, et si oui ou non l'unicité est bénéfique ou coûteuse (probablement les deux). Le nœud du problème réside dans nos ressources concurrentes.
Contexte
J'ai déjà lu une discussion qui a déclaré qu'un Unique
index n'est pas un coût supplémentaire à maintenir, car une Insert
opération vérifie implicitement où il s'inscrit dans l'arbre B et, si un doublon est trouvé dans un index non unique, ajoute un uniquificateur à la fin de la clé, mais sinon insère directement. Dans cette séquence d'événements, un Unique
index n'a aucun coût supplémentaire.
Mon collègue combat cette affirmation en disant qu'elle Unique
est appliquée en tant que deuxième opération après la recherche de la nouvelle position dans l'arbre B, et est donc plus coûteuse à maintenir qu'un index non unique.
Au pire, j'ai vu des tables avec une colonne d'identité (intrinsèquement unique) qui est la clé de clustering de la table, mais explicitement déclarée comme non unique. De l'autre côté du pire est mon obsession de l'unicité, et tous les index sont créés comme uniques, et lorsqu'il n'est pas possible de définir une relation explicitement unique à un index, j'ajoute le PK de la table à la fin de l'index pour garantir la l'unicité est garantie.
Je suis souvent impliqué dans les revues de code pour l'équipe de développement, et je dois être en mesure de donner des directives générales à suivre. Oui, chaque index doit être évalué, mais lorsque vous avez cinq serveurs avec des milliers de tables chacun et jusqu'à vingt index sur une table, vous devez être en mesure d'appliquer des règles simples pour garantir un certain niveau de qualité.
Question
L'unicité a-t-elle un coût supplémentaire sur le back-end d'un Insert
par rapport au coût de maintien d'un indice non unique? Deuxièmement, qu'y a-t-il de mal à ajouter la clé primaire d'une table à la fin d'un index pour garantir l'unicité?
Exemple de définition de table
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);
Exemple
Un exemple de la raison pour laquelle j'ajouterais la Unique
clé à la fin d'un index se trouve dans l'une de nos tables de faits. Il y a un Primary Key
qui est une Identity
colonne. Cependant, Clustered Index
c'est à la place la colonne du schéma de partitionnement, suivie de trois dimensions de clé étrangère sans unicité. Les performances de sélection sur ce tableau sont épouvantables, et j'obtiens souvent de meilleurs temps de recherche en utilisant le Primary Key
avec une recherche de clé plutôt que de tirer parti du Clustered Index
. Les autres tableaux qui suivent une conception similaire, mais qui sont Primary Key
annexés à la fin, ont des performances considérablement meilleures.
-- 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
et lesIf
structures sont limitées à 10 niveaux, il est logique qu'il y ait également une limite à la résolution d'entités non uniques. D'après votre déclaration, cela semble ne s'appliquer qu'aux cas où la clé de clustering n'est pas unique. Est-ce un problème pour unNonclustered Index
ou si la clé de clustering estUnique
alors il n'y a pas de problème pour lesNonclustered
index?Je ne vais pas peser sur la question de savoir si un indice doit être unique ou non, et s'il y a plus de frais généraux dans cette approche ou cela. Mais quelques choses m'ont dérangé dans votre conception générale
WHERE is_deleted = 0
) et regardez à l'aide d'un index filtré. J'envisagerais même d'utiliser 2 index filtrés, l'un pourwhere is_deleted = 0
et l'autre pourwhere is_deleted = 1
Fondamentalement, cela ressemble plus à un exercice de codage conçu pour tester une hypothèse plutôt qu'un vrai problème / solution, mais ces deux modèles sont certainement quelque chose que je recherche dans les revues de code.
la source
Nonclustered
index aura la clé de clustering ajoutée à la fin de la ligne de données pour les recherches de clés en interne. En tant que tels, les deux index sont physiquement les mêmes, ce qui était le point de ma question.Il semble que vous utilisiez simplement PK pour créer un index alternatif plus petit. Par conséquent, les performances sont plus rapides.
Vous voyez cela dans les entreprises qui ont d'énormes tableaux de données (par exemple: tableaux de données de base). Quelqu'un décide d'avoir un index cluster massif sur lui, s'attendant à ce qu'il réponde aux besoins des différents groupes de reporting.
Mais, un groupe peut n'avoir besoin que de quelques parties de cet index alors qu'un autre groupe a besoin d'autres parties.
Pendant ce temps, le décomposer pour créer plusieurs indices plus petits et ciblés résout souvent le problème.
Et cela semble être ce que vous faites. Vous avez cet index cluster massif avec des performances horribles, puis vous utilisez PK pour créer un autre index avec moins de colonnes qui (sans surprise) a de meilleures performances.
Donc, faites une analyse et déterminez si vous pouvez prendre l'index cluster unique et le décomposer en indices plus petits et ciblés dont des emplois spécifiques ont besoin.
Vous devrez alors analyser les performances à partir d'un point de vue "index unique vs index multiple", car il y a des frais généraux dans la création et la mise à jour des index. Mais, vous devez analyser cela dans une perspective globale.
EG: il peut être moins gourmand en ressources pour un seul indice cluster massif, et plus gourmand en ressources pour avoir plusieurs indices ciblés plus petits. Mais, si vous êtes alors en mesure d'exécuter des requêtes ciblées sur le back-end beaucoup plus rapidement, ce qui vous fait gagner du temps (et de l'argent), cela en vaut la peine.
Donc, vous devriez faire une analyse de bout en bout .. non seulement regarder comment cela affecte votre propre monde, mais aussi comment cela affecte les utilisateurs finaux.
J'ai juste l'impression que vous utilisez mal l'identifiant PK. Mais, vous utilisez peut-être un système de base de données qui n'autorise qu'un seul index (?), Mais vous pouvez en introduire un autre si vous PK (b / c chaque système de base de données relationnelle de nos jours semble indexer automatiquement le PK). Cependant, la plupart des SGBDR modernes devraient permettre la création d'index multiples; il ne devrait pas y avoir de limite au nombre d'index que vous pouvez créer (par opposition à une limite de 1 PK).
Donc, en faisant un PK qui agit juste comme un index alt .. vous utilisez votre PK, ce qui peut être nécessaire si la table est développée plus tard dans son rôle.
Cela ne veut pas dire que votre table n'a pas besoin d'un PK. SOP DB 101 dit "chaque table doit avoir un PK". Mais, dans une situation d'entreposage de données ou autre ... avoir un PK sur une table peut être un surcoût supplémentaire dont vous n'avez pas besoin. Ou, cela pourrait être un envoi de Dieu pour vous assurer que vous ne doublez pas les entrées de dupe. C'est vraiment une question de ce que vous faites et pourquoi vous le faites.
Mais, les tables massives bénéficient indéniablement d'avoir des index. Mais, en supposant qu'un seul index massif en cluster sera le meilleur est juste ... il peut être le meilleur .. mais je recommanderais de tester sur un env de test divisant l'index en plusieurs indices plus petits ciblant des scénarios de cas d'utilisation spécifiques.
la source