He tenido un debate en curso con varios desarrolladores en mi oficina sobre el costo de un índice y si la unicidad es beneficiosa o costosa (probablemente ambas). El quid de la cuestión son nuestros recursos competidores.
Antecedentes
Anteriormente leí una discusión que decía que un Unique
índice no tiene costo adicional para mantener, ya que una Insert
operación verifica implícitamente dónde encaja en el árbol B y, si se encuentra un duplicado en un índice no único, agrega un uniquifier a el final de la clave, pero por lo demás se inserta directamente. En esta secuencia de eventos, un Unique
índice no tiene costo adicional.
Mi compañero de trabajo combate esta afirmación diciendo que Unique
se aplica como una segunda operación después de buscar la nueva posición en el árbol B y, por lo tanto, es más costoso de mantener que un índice no único.
En el peor de los casos, he visto tablas con una columna de identidad (inherentemente única) que es la clave de agrupación de la tabla, pero explícitamente declarada como no única. En el otro lado de lo peor está mi obsesión con la unicidad, y todos los índices se crean como únicos, y cuando no es posible definir una relación explícitamente única con un índice, agrego el PK de la tabla al final del índice para asegurar que La unicidad está garantizada.
Con frecuencia participo en revisiones de código para el equipo de desarrollo, y necesito poder dar pautas generales para que sigan. Sí, cada índice debe evaluarse, pero cuando tiene cinco servidores con miles de tablas cada uno y hasta veinte índices en una tabla, debe poder aplicar algunas reglas simples para garantizar un cierto nivel de calidad.
Pregunta
¿La unicidad tiene un costo adicional en el back-end de una Insert
comparación con el costo de mantener un índice no único? En segundo lugar, ¿qué tiene de malo agregar la clave primaria de una tabla al final de un índice para garantizar la unicidad?
Definición de tabla de ejemplo
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);
Ejemplo
Un ejemplo de por qué agregaría la Unique
clave al final de un índice está en una de nuestras tablas de hechos. Hay una Primary Key
que es una Identity
columna. Sin embargo, Clustered Index
es la columna del esquema de partición, seguida de tres dimensiones de clave externa sin unicidad. Seleccionar el rendimiento en esta tabla es abismal, y frecuencia me buscan mejores tiempos con el Primary Key
con una búsqueda de claves en lugar de aprovechar el Clustered Index
. Otras tablas que siguen un diseño similar, pero que se han Primary Key
agregado al final tienen un rendimiento considerablemente mejor.
-- 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
y cómoIf
se limitan a 10 niveles, tiene sentido que también haya un límite para resolver entidades no únicas. Según su declaración, parece que solo se aplica a casos en los que la clave de agrupación no es única. ¿Es esto un problema paraNonclustered Index
o si la clave de agrupación esUnique
entonces no hay un problema para losNonclustered
índices?No voy a analizar la cuestión de si un índice debe ser único o no, y si hay más gastos generales en este enfoque o en ese. Pero un par de cosas me molestaron en tu diseño general
WHERE is_deleted = 0
) y mire usando un índice filtrado. Incluso consideraría usar 2 índices filtrados, uno parawhere is_deleted = 0
y el otro parawhere is_deleted = 1
Básicamente, esto se parece más a un ejercicio de codificación diseñado para probar una hipótesis que a un problema / solución real, pero esos dos patrones son definitivamente algo que busco en las revisiones de código.
fuente
Nonclustered
índice tendrá la clave de agrupación agregada al final de la fila de datos para búsquedas de clave internamente. Como tal, los dos índices son físicamente iguales, que fue el punto de mi pregunta.Parece que simplemente estás usando PK para hacer un índice alternativo más pequeño. Por lo tanto, el rendimiento es más rápido.
Puede ver esto en empresas que tienen tablas de datos masivas (por ejemplo, tablas de datos maestros). Alguien decide tener un índice agrupado masivo esperando que satisfaga las necesidades de varios grupos de informes.
Pero, un grupo puede necesitar solo unas pocas partes de ese índice, mientras que otro grupo necesita otras partes ... por lo que el índice simplemente golpeando en cada columna bajo el sol para "optimizar el rendimiento" realmente no ayuda.
Mientras tanto, desglosarlo para crear índices múltiples, más pequeños y específicos, a menudo resuelve el problema.
Y, eso parece ser lo que estás haciendo. Tiene este índice agrupado masivo con un rendimiento horrible, luego está usando PK para crear otro índice con menos columnas que (no es de extrañar) tiene un mejor rendimiento.
Entonces, solo haga un análisis y descubra si puede tomar el índice agrupado único y dividirlo en índices más pequeños y específicos que necesitan trabajos específicos.
Tendría que analizar el rendimiento desde un punto de vista de "índice único versus índice múltiple", porque hay gastos generales en la creación y actualización de índices. Pero, tienes que analizar esto desde una perspectiva general.
EG: puede ser menos intensivo en recursos para un índice agrupado masivo, y más intensivo en recursos para tener varios índices específicos más pequeños. Pero, si puede ejecutar consultas específicas en el back-end mucho más rápido, ahorrando tiempo (y dinero) allí, podría valer la pena.
Por lo tanto, tendría que hacer un análisis de extremo a extremo ... no solo ver cómo afecta a su propio mundo, sino también cómo afecta a los usuarios finales.
Siento que estás usando mal el identificador PK. Pero, puede estar utilizando un sistema de base de datos que solo permite 1 índice (?), Pero puede introducir otro si su PK (b / c cada sistema de base de datos relacional en estos días parece indexar automáticamente el PK). Sin embargo, la mayoría de los RDBMS modernos deberían permitir la creación de múltiples índices; no debe haber límite para la cantidad de índices que puede hacer (en oposición a un límite de 1 PK).
Entonces, al hacer un PK que solo actúa como un índice alternativo ... estás usando tu PK, que puede ser necesario si la tabla se expande más tarde en su rol.
Eso no quiere decir que su mesa no necesita un PK .. SOP DB's 101 dice "cada mesa debe tener un PK". Pero, en una situación de almacenamiento de datos o similar ... tener una PK en una tabla puede ser una carga adicional que no necesita. O bien, podría ser un envío de Dios para asegurarse de que no está agregando doblemente entradas engañosas. Realmente es una cuestión de lo que estás haciendo y por qué lo estás haciendo.
Pero, las tablas masivas definitivamente se benefician de tener índices. Pero, suponiendo que un solo índice agrupado masivo sea lo mejor es solo ... puede ser el mejor ... pero recomendaría probar en un entorno de prueba dividiendo el índice en múltiples índices más pequeños dirigidos a escenarios de casos de uso específicos.
fuente