Frage zur Indexoptimierung

8

Ich optimiere einige Indizes und sehe einige Probleme, die Ihren Rat annehmen möchten

Auf 1 Tabelle befinden sich 3 Indizes

dbo.Address.IX_Address_ProfileId 
[1 KEY] ProfileId {int 4}
Reads: 0 Writes:10,519

dbo.Address.IX_Address 
[2 KEYS] ProfileId {int 4}, InstanceId {int 4}
Reads: 0 Writes:10,523

dbo.Address.IX_Address_profile_instance_addresstype
[3 KEYS] ProfileId {int 4}, InstanceId {int 4}, AddressType {int 4}
Reads: 149677 (53,247 seek) Writes:10,523

1- Brauche ich wirklich die ersten 2 Indizes oder sollte ich sie löschen?

2- Es werden Abfragen ausgeführt, die eine Bedingung mit profileid = xxxx und eine andere Verwendungsbedingung mit profileid = xxxx und InstanceID = xxxxxx verwenden. Warum wählt der Optimierer den 3. Index, nicht den 1. oder 2.?

Außerdem führe ich eine Abfrage aus, bei der die Sperre für jeden Index wartet. Was soll ich tun, um diesen Index zu optimieren, wenn ich diese Zählungen erhalte?

Row lock waits: 484; total duration: 59 minutes; avg duration: 7 seconds; 
Page lock waits: 5; total duration: 11 seconds; avg duration: 2 seconds; 
Lock escalation attempts: 36,949; Actual Escalations: 0.

Tabellenstruktur ist

TABLE [dbo].[Address](
[Id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[AddressType] [int] NULL,
[isPreferredAddress] [bit] NULL,
[StreetAddress1] [nvarchar](255) NULL,
[StreetAddress2] [nvarchar](255) NULL,
[City] [nvarchar](50) NULL,
[State_Id] [int] NOT NULL,
[Zip] [varchar](20) NULL,
[Country_Id] [int] NOT NULL,
[CurrentUntil] [date] NULL,
[CreatedDate] [datetime] NOT NULL,
[UpdatedDate] [datetime] NOT NULL,
[ProfileId] [int] NOT NULL,
[InstanceId] [int] NOT NULL,
[County_id] [int] NULL,
 CONSTRAINT [PK__Address__3214EC075E4BE276] PRIMARY KEY CLUSTERED 
(
   [Id] ASC
 )

Dies ist ein Beispiel (diese Abfrage, die vom Ruhezustand erstellt wurde, sieht also seltsam aus)

(@P0 bigint)select addresses0_.ProfileId as Profile15_109_1_
, addresses0_.Id as Id1_20_1_
, addresses0_.Id as Id1_20_0_
, addresses0_.AddressType as AddressT2_20_0_
, addresses0_.City as City3_20_0_
, addresses0_.Country_Id as Country_4_20_0_
, addresses0_.County_id as County_i5_20_0_
, addresses0_.CreatedDate as CreatedD6_20_0_
, addresses0_.CurrentUntil as CurrentU7_20_0_
, addresses0_.InstanceId as Instance8_20_0_
, addresses0_.isPreferredAddress as isPrefer9_20_0_
, addresses0_.ProfileId as Profile15_20_0_
, addresses0_.State_Id as State_I10_20_0_
, addresses0_.StreetAddress1 as StreetA11_20_0_
, addresses0_.StreetAddress2 as StreetA12_20_0_
, addresses0_.UpdatedDate as Updated13_20_0_
, addresses0_.Zip as Zip14_20_0_ 
from dbo.Address addresses0_ 
where addresses0_.ProfileId=@P0 

Geben Sie hier die Bildbeschreibung ein

(@P0 bigint,@P1 bigint)
select addressdmo0_.Id as Id1_20_
, addressdmo0_.AddressType as AddressT2_20_
, addressdmo0_.City as City3_20_
, addressdmo0_.Country_Id as Country_4_20_
, addressdmo0_.County_id as County_i5_20_
, addressdmo0_.CreatedDate as CreatedD6_20_
, addressdmo0_.CurrentUntil as CurrentU7_20_
, addressdmo0_.InstanceId as Instance8_20_
, addressdmo0_.isPreferredAddress as isPrefer9_20_
, addressdmo0_.ProfileId as Profile15_20_
, addressdmo0_.State_Id as State_I10_20_
, addressdmo0_.StreetAddress1 as StreetA11_20_
, addressdmo0_.StreetAddress2 as StreetA12_20_
, addressdmo0_.UpdatedDate as Updated13_20_
, addressdmo0_.Zip as Zip14_20_ 
from dbo.Address addressdmo0_ 
left outer join dbo.Profile profiledmo1_ 
on addressdmo0_.ProfileId=profiledmo1_.Id 
where profiledmo1_.Id=@P0 and addressdmo0_.InstanceId=@P1

Geben Sie hier die Bildbeschreibung ein

sebeid
quelle
Jede Möglichkeit, die Sie zur vollständigen Tabellenstruktur, zum Clustering-Schlüssel und zu den anderen Spalten hinzufügen können, die in den Abfragen enthalten sind, die nach profileid = xxxx und der Abfrage mit profilerid = xxxx und instanceid = xxxx suchen. In diesen Antworten steckt viel "es kommt darauf an", und diese Informationen würden definitiv helfen, zu erklären, wovon es abhängt.
mskinner
Weitere Informationen zu den Daten wären hilfreich. Wenn beispielsweise Statistiken für die Tabelle aktualisiert wurden, wie viele Datensätze sich in der Tabelle befinden, zusammen mit der Eindeutigkeit usw.
Glen Swan
@GlenSwan, es gibt 567644 Datensatz in dieser Tabelle. Die Statistiken wurden zweimal pro Woche aktualisiert. Dienstag und Samstag
20.
2
Bitte recherchieren Sie selbst über Funktionsvergleiche. Obwohl es einige Überschneidungen gibt, haben Failover-Cluster und Verfügbarkeitsgruppen unterschiedliche Funktionen und erfüllen unterschiedliche Anforderungen, sodass Sie nicht generisch fragen können, welche besser ist. Sie müssen die jeweiligen Funktionen mit Ihren tatsächlichen Geschäftsanforderungen vergleichen. Auch Lizenz- / Kostenfragen sind hier nicht thematisch. Bitte lesen Sie diesen Meta-Beitrag vollständig .
Aaron Bertrand

Antworten:

6

Antwort auf Frage 1:

Nach dem, was Sie veröffentlicht haben, können Sie die ersten beiden Indizes löschen, da der dritte alle von Ihnen erwähnten Abfragen abdeckt und das Abfrageoptimierungsprogramm dies auch beim Erstellen des Abfrageplans sieht (basierend auf dem von Ihnen veröffentlichten Plan).

Antwort auf Frage 2:

Es wird immer der dritte Index verwendet, da der Index mit den zwei zusätzlichen Indexschlüsseln ( InstanceId and AddressType) bereits mehr Daten enthält . Dies verhindert, dass SQL InstanceId und AddressType vom Primärschlüssel (dem Schlüssel-Lookup-Teil des Ausführungsplans) abrufen muss, um die Abfrage zu erfüllen.

Ich würde vorschlagen, die ersten beiden Indizes zu löschen und den dritten mit Include-Spalten neu zu erstellen, um die anderen Spalten abzudecken, die in der Abfrage angefordert werden

Create index IX_Address_profile_instance_addresstype 
on dbo.address  (ProfileId, InstanceId, AddressType) 
include(<put in the remaining columns comma delimited>) 
with (drop_existing=on,sort_in_tempdb=on)

Dies sollte bei den Abfragen helfen und die Schlüsselsuche aus dem Abfrageplan entfernen.

Sehen Sie, ob die Schlösser nach diesen Änderungen abfallen und wenn nicht, können wir etwas tiefer graben.

Aaron
quelle
Ich habe getan, was Sie empfehlen, und es gibt keine Schlüsselsuche mehr. Ich überwache Sperren. Ich brauche noch einige Klarstellungen für diese Zählungen. Zeilensperre wartet: 484; Gesamtdauer: 59 Minuten; durchschnittliche Dauer: 7 Sekunden; Seitensperre wartet: 5; Gesamtdauer: 11 Sekunden; durchschnittliche Dauer: 2 Sekunden; Lock-Eskalationsversuche: 36.949; Tatsächliche Eskalationen: 0.
Sebeid
@sebeid Werden beim Einfügen / Aktualisieren / Löschen in die Adresstabelle auch andere Tabellen durch dieselbe Stapelanforderung geändert? Wenn ja, beginnt der Stapel explizit eine Transaktion für alle Änderungsanweisungen im Stapel und schreibt oder rollt sie erst am Ende des Stapels zurück?
Aaron
Ich weiß nicht, woher ich diese Informationen bekommen kann. Danke
sebeid
2
@sebeid Wenn alles eine generierte Anweisung aus dem Ruhezustand ist, würde ich Ihre Entwickler fragen, da dies möglicherweise der einfachste Ansatz ist. Andernfalls müssen Sie ein erweitertes Ereignis verfolgen oder einrichten, um zu versuchen, die Anrufe abzufangen, damit Sie sehen können, was alles passiert die Batch-Anweisung. Beispiel für einen Profiler für erweiterte Ereignisse
Aaron,
3

Nicht die angegebene Frage, aber möglicherweise bessere Abfragepläne mit besseren Abfragen.
Sie beenden das linke Äußere mit dem
Punkt, an dem profiledmo1_.Id=@P0 dies in einen Join umwandelt

Auf Indizes nur die ersten beiden

select addressdmo0_.Id as Id1_20_
     , ...
     , addressdmo0_.Zip as Zip14_20_ 
  from dbo.Address addressdmo0_ 
  join dbo.Profile profiledmo1_ 
    on addressdmo0_.ProfileId = profiledmo1_.Id 
   and profiledmo1_.Id = @P0 
   and addressdmo0_.InstanceId = @P1

Alles, was der Join tut, ist sicherzustellen, dass er sich im Profil befindet, aber Sie melden nichts vom Profil
und wie ist das nicht?

select addressdmo0_.Id as Id1_20_
     , ...
     , addressdmo0_.Zip as Zip14_20_ 
  from dbo.Address addressdmo0_ 
 where addressdmo0_.ProfileId  = @P0 
   and addressdmo0_.InstanceId = @P1
Paparazzo
quelle
2

Es scheint, als könnten Sie Index 1 und 2 löschen, da Index 3 alle Informationen (Spalten) enthält, die Sie benötigen. Möglicherweise ist ein anderer Index als Clustered-Index zur Darstellung des Primärschlüssels sinnvoll.

Mit diesen begrenzten Informationen können wir nur raten. Wenn Sie weitere Hinweise benötigen, veröffentlichen Sie bitte detailliertere Informationen wie Ihre vollständige Tabellenstruktur (Tabellen, Index, Schlüssel, ...), Ihre Abfragen und den Ausführungsplan.

Josh Alvo
quelle
1
Oder ein Clustered-Index, der nicht den Primärschlüssel darstellt. Während diese beiden Dinge oft eng miteinander verbunden sind und ein Primärschlüssel standardmäßig als Cluster erstellt wird, sind sie nicht dasselbe (und müssen es auch nicht sein).
Aaron Bertrand
Das stimmt natürlich. :-)
Josh Alvo