Eu tenho um debate em andamento com vários desenvolvedores em meu escritório sobre o custo de um índice e se a exclusividade é ou não benéfica ou cara (provavelmente as duas). O cerne da questão são nossos recursos concorrentes.
fundo
Eu li anteriormente uma discussão que afirmava que um Unique
índice não tem custo adicional de manutenção, uma vez que uma Insert
operação verifica implicitamente onde ele se encaixa na árvore B e, se uma duplicata for encontrada em um índice não exclusivo, anexa um unificador final da chave, mas, caso contrário, é inserido diretamente. Nesta sequência de eventos, um Unique
índice não tem custo adicional.
Meu colega de trabalho combate essa afirmação dizendo que Unique
é imposta como uma segunda operação após a busca pela nova posição na árvore B e, portanto, é mais cara de manter do que um índice não exclusivo.
Na pior das hipóteses, vi tabelas com uma coluna de identidade (inerentemente exclusiva) que é a chave de cluster da tabela, mas declarada explicitamente como não exclusiva. Do outro lado do pior, está minha obsessão pela exclusividade, e todos os índices são criados como únicos e, quando não é possível definir uma relação explicitamente exclusiva com um índice, anexo a PK da tabela ao final do índice para garantir que o a exclusividade é garantida.
Estou freqüentemente envolvido em revisões de código para a equipe de desenvolvimento e preciso fornecer diretrizes gerais para que eles sigam. Sim, todos os índices devem ser avaliados, mas quando você tem cinco servidores com milhares de tabelas cada e até vinte índices em uma tabela, precisa aplicar algumas regras simples para garantir um certo nível de qualidade.
Questão
A exclusividade tem um custo adicional no final de uma Insert
comparação com o custo de manutenção de um índice não exclusivo? Em segundo lugar, o que há de errado em acrescentar a Chave Primária de uma tabela ao final de um índice para garantir a exclusividade?
Exemplo de definição de tabela
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);
Exemplo
Um exemplo de por que eu adicionaria a Unique
chave ao final de um índice está em uma de nossas tabelas de fatos. Existe um Primary Key
que é uma Identity
coluna. No entanto, Clustered Index
é a coluna do esquema de particionamento, seguida por três dimensões de chave estrangeira sem exclusividade. O desempenho selecionado nesta tabela é péssimo e, frequentemente, os tempos de busca são melhores usando o Primary Key
com uma pesquisa de chave, em vez de alavancar o Clustered Index
. Outras tabelas que seguem um design semelhante, mas Primary Key
anexadas ao final, têm desempenho consideravelmente melhor.
-- 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
e osIf
limites são limitados a 10 níveis, faz sentido que também haja um limite para a resolução de entidades não exclusivas. Pela sua declaração, isso parece aplicar-se apenas a casos em que a chave de cluster não é exclusiva. Isso é um problema para umNonclustered Index
ou se a chave de cluster éUnique
então não há um problema paraNonclustered
índices?Não vou me aprofundar na questão de saber se um índice deve ser único ou não, e se há mais sobrecarga nessa abordagem ou naquilo. Mas algumas coisas me incomodaram no seu design geral
WHERE is_deleted = 0
) e veja usando um índice filtrado. Eu consideraria até usar dois índices filtrados, um parawhere is_deleted = 0
e outro parawhere is_deleted = 1
Fundamentalmente, isso parece mais um exercício de codificação projetado para testar uma hipótese do que um problema / solução real, mas esses dois padrões são definitivamente algo que procuro nas revisões de código.
fonte
Nonclustered
índice terá a chave de cluster anexada ao final da linha de dados para consultas de chave internamente. Como tal, os dois índices são fisicamente os mesmos, que foi o ponto da minha pergunta.Parece que você está simplesmente usando o PK para criar um índice menor e alternativo. Portanto, o desempenho é mais rápido.
Você vê isso em empresas que possuem grandes tabelas de dados (por exemplo: tabelas de dados mestre). Alguém decide ter um índice agrupado massivo, esperando que ele preencha as necessidades de vários grupos de relatórios.
Porém, um grupo pode precisar de apenas algumas partes desse índice, enquanto outro grupo precisa de outras partes. Portanto, o índice apenas batendo em todas as colunas sob o sol para "otimizar o desempenho" não ajuda muito.
Enquanto isso, decompô-lo para criar vários índices menores e direcionados, geralmente resolve o problema.
E isso parece ser o que você está fazendo. Você tem esse índice em cluster maciço com desempenho terrível e, em seguida, usa o PK para criar outro índice com menos colunas que (sem surpresa) tenham melhor desempenho.
Portanto, basta fazer uma análise e descobrir se você pode pegar o único índice agrupado e dividi-lo em índices menores e direcionados, necessários para tarefas específicas.
Você precisaria analisar o desempenho do ponto de vista do "índice único versus índice múltiplo", porque há uma sobrecarga na criação e atualização de índices. Mas você precisa analisar isso de uma perspectiva geral.
EG: pode ser menos intensivo em recursos para um índice clusterizado massivo e mais intensivo em recursos para ter vários índices direcionados menores. Porém, se você conseguir executar consultas direcionadas no back-end com muito mais rapidez, economizando tempo (e dinheiro) lá, pode valer a pena.
Portanto, você teria que fazer uma análise de ponta a ponta ... não apenas ver como isso afeta seu próprio mundo, mas também como isso afeta os usuários finais.
Eu apenas sinto que você está usando mal o identificador PK. Porém, você pode estar usando um sistema de banco de dados que permite apenas 1 índice (?), Mas pode entrar com outro código se fizer PK (b / c todos os sistemas de banco de dados relacional atualmente parece indexar automaticamente o PK). No entanto, a maioria dos RDBMS modernos deve permitir a criação de vários índices; não deve haver limite para o número de índices que você pode criar (em oposição a um limite de 1 PK).
Portanto, ao criar um PK whicih, apenas atua como um índice alt. Você está usando seu PK, o que pode ser necessário se a tabela for expandida posteriormente em sua função.
Isso não quer dizer que sua mesa não precise de um PK. O SOP DB's 101 diz "toda mesa deve ter um PK". Mas, em uma situação de armazenamento de dados ou algo assim, ter uma PK em uma tabela pode ser apenas uma sobrecarga extra que você não precisa. Ou pode ser um envio divino para garantir que você não esteja adicionando duplamente entradas falsas. É realmente uma questão do que você está fazendo e por que está fazendo.
Mas, tabelas maciças definitivamente se beneficiam de ter índices. Mas, supondo que um único índice clusterizado maciço seja o melhor é apenas ... pode ser o melhor .. mas eu recomendo testar em um ambiente de teste dividindo o índice em vários índices menores, visando cenários de casos de uso específicos.
fonte