sql >> Database >  >> RDS >> Database

Hoe gefilterde indexen een krachtigere functie kunnen zijn

Begrijp me niet verkeerd; Ik ben dol op gefilterde indexen. Ze creëren mogelijkheden voor een veel efficiënter gebruik van I/O en stellen ons uiteindelijk in staat om de juiste ANSI-conforme unieke beperkingen te implementeren (waar meer dan één NULL is toegestaan). Ze zijn echter verre van perfect. Ik wilde enkele gebieden aanwijzen waar gefilterde indexen kunnen worden verbeterd en ze veel nuttiger en praktischer kunnen worden voor een groot deel van de werkbelastingen die er zijn.

Ten eerste het goede nieuws

Gefilterde indexen kunnen zeer snel werk maken van voorheen dure zoekopdrachten, en doen dit met minder ruimte (en dus verminderde I/O, zelfs wanneer ze worden gescand).

Een snel voorbeeld met Sales.SalesOrderDetailEnlarged (gebouwd met behulp van dit script door Jonathan Kehayias (@SQLPoolBoy)). Deze tabel heeft rijen van 4,8 mm, met 587 MB aan gegevens en 363 MB aan indexen. Er is slechts één kolom met nulling, CarrierTrackingNumber , dus laten we daar mee spelen. Zoals het is, heeft de tabel momenteel ongeveer de helft van deze waarden (2,4 MM) als NULL. Ik ga dat terugbrengen tot ongeveer 240K om een ​​scenario te simuleren waarin een klein percentage van de rijen in de tabel daadwerkelijk in aanmerking komt voor een index, om de voordelen van een gefilterde index het beste te benadrukken. De volgende query is van invloed op 2,17 MM rijen, waardoor er 241.507 rijen overblijven met een NULL-waarde voor CarrierTrackingNumber :

UPDATE Sales.SalesOrderDetailEnlarged 
    SET CarrierTrackingNumber = 'x'
      WHERE CarrierTrackingNumber IS NULL
      AND SalesOrderID % 10 <> 3;

Laten we nu zeggen dat er een zakelijke vereiste is waarbij we voortdurend bestellingen willen beoordelen met producten waaraan nog een trackingnummer moet worden toegewezen (denk aan bestellingen die worden opgesplitst en afzonderlijk worden verzonden). Op de huidige tabel zouden we deze queries uitvoeren (en ik heb de DBCC-commando's toegevoegd om in elk geval koude cache te garanderen):

DBCC DROPCLEANBUFFERS;
DBCC FREEPROCCACHE;
 
SELECT COUNT(*)
  FROM Sales.SalesOrderDetailEnlarged 
  WHERE CarrierTrackingNumber IS NULL;
 
SELECT ProductID, SalesOrderID
  FROM Sales.SalesOrderDetailEnlarged
  WHERE CarrierTrackingNumber IS NULL;

Die geclusterde indexscans vereisen en de volgende runtime-statistieken opleveren (zoals vastgelegd met SQL Sentry Plan Explorer):

In de "oude" tijd (dat wil zeggen sinds SQL Server 2005), zouden we deze index hebben gemaakt (en zelfs in SQL Server 2012 is dit de index die SQL Server aanbeveelt):

CREATE INDEX IX_NotVeryHelpful
ON [Sales].[SalesOrderDetailEnlarged] ([CarrierTrackingNumber])
INCLUDE ([SalesOrderID],[ProductID]);

Met die index op zijn plaats en het opnieuw uitvoeren van de bovenstaande zoekopdrachten, zijn hier de statistieken, waarbij beide zoekopdrachten een indexzoekopdracht gebruiken zoals je zou verwachten:

En dan die index laten vallen en een iets andere maken, gewoon een WHERE . toevoegen clausule:

CREATE INDEX IX_Filtered_CTNisNULL
ON [Sales].[SalesOrderDetailEnlarged] ([CarrierTrackingNumber])
INCLUDE ([SalesOrderID],[ProductID])
WHERE CarrierTrackingNumber IS NULL;

We krijgen deze resultaten en beide zoekopdrachten gebruiken de gefilterde index voor hun zoekopdrachten:

Dit is de extra ruimte die elke index nodig heeft, vergeleken met de vermindering van runtime en I/O van de bovenstaande query's:

Index Indexruimte Ruimte toegevoegd Duur Leest
Geen speciale index 363 MB 15.700ms ~164.000
Niet-gefilterde index 530 MB 167 MB (+46%) 169ms 1.084
Gefilterde index 367 MB 4 MB (+1%) 170ms 1.084


Dus, zoals u kunt zien, levert de gefilterde index prestatieverbeteringen die bijna identiek zijn aan de niet-gefilterde index (aangezien beide hun gegevens kunnen verkrijgen met hetzelfde aantal leesbewerkingen), maar met een veel lagere opslagcapaciteit kosten, aangezien de gefilterde index alleen de rijen hoeft op te slaan en te onderhouden die overeenkomen met het filterpredikaat.

Laten we de tafel nu terugzetten in de oorspronkelijke staat:

UPDATE Sales.SalesOrderDetailEnlarged
  SET CarrierTrackingNumber = NULL
  WHERE CarrierTrackingNumber = 'x';
 
DROP INDEX IX_NotVeryHelpful ON Sales.SalesOrderDetailEnlarged;
DROP INDEX IX_Filtered_CTNisNULL ON Sales.SalesOrderDetailEnlarged;

Tim Chapman (@chapmandew) en Michelle Ufford (@sqltool) hebben fantastisch werk geleverd door de prestatievoordelen van gefilterde indexen op hun eigen manier te schetsen, en je zou ook hun berichten moeten bekijken:

  • Michelle Ufford:gefilterde indexen:wat u moet weten
  • Tim Chapman:De geneugten van gefilterde indexen

Ook ANSI-conforme unieke beperkingen (soort van)

Ik dacht dat ik ook kort ANSI-conforme unieke beperkingen zou noemen. In SQL Server 2005 zouden we een unieke beperking als deze creëren:

CREATE TABLE dbo.Personnel
(
  EmployeeID INT PRIMARY KEY,
  SSN CHAR(9) NULL,
  -- ... other columns ...
  CONSTRAINT UQ_SSN UNIQUE(SSN)
);

(We kunnen ook een unieke niet-geclusterde index maken in plaats van een beperking; de onderliggende implementatie is in wezen hetzelfde.)

Dit is geen probleem als SSN's bekend zijn op het moment van invoer:

INSERT dbo.Personnel(EmployeeID, SSN)
VALUES(1,'111111111'),(2,'111111112');

Het is ook prima als we af en toe een BSN hebben dat niet bekend is op het moment van binnenkomst (denk aan een visumaanvrager of misschien zelfs een buitenlandse werknemer die geen BSN heeft en nooit zal hebben):

INSERT dbo.Personnel(EmployeeID, SSN)
VALUES(3,NULL);

Tot nu toe, zo goed. Maar wat gebeurt er als we een seconde . hebben werknemer met een onbekend BSN?

INSERT dbo.Personnel(EmployeeID, SSN)
VALUES(4,NULL);

Resultaat:

Msg 2627, niveau 14, staat 1, regel 1
Overtreding van UNIQUE KEY-beperking 'UQ_SSN'. Kan geen dubbele sleutel invoegen in object 'dbo.Personnel'. De dubbele sleutelwaarde is ().
De instructie is beëindigd.

Er kan dus altijd maar één NULL-waarde in deze kolom staan. In tegenstelling tot de meeste scenario's is dit een geval waarin SQL Server twee NULL-waarden als gelijk behandelt (in plaats van te bepalen dat gelijkheid simpelweg onbekend is en op zijn beurt onwaar). Mensen klagen al jaren over deze inconsistentie.

Als dit een vereiste is, kunnen we dit nu omzeilen met behulp van gefilterde indexen:

ALTER TABLE dbo.Personnel DROP CONSTRAINT UQ_SSN;
GO
 
CREATE UNIQUE INDEX UQ_SSN ON dbo.Personnel(SSN)
  WHERE SSN IS NOT NULL;

Nu werkt onze 4e invoeging prima, omdat uniciteit alleen wordt afgedwongen op de niet-NULL-waarden. Dit is een beetje vals spelen, maar het voldoet wel aan de basisvereisten die de ANSI-standaard bedoeld heeft (ook al staat SQL Server ons niet toe om ALTER TABLE ... ADD CONSTRAINT te gebruiken syntaxis om een ​​gefilterde unieke beperking te maken).

Maar houd de telefoon vast

Dit zijn geweldige voorbeelden van wat we kunnen doen met gefilterde indexen, maar er zijn veel dingen die we nog steeds niet kunnen doen, met als gevolg verschillende beperkingen en problemen.

Statistische updates

Dit is een van de belangrijkste beperkingen IMHO. Gefilterde indexen profiteren niet van het automatisch bijwerken van statistieken op basis van een procentuele verandering van de subset van de tabel die wordt geïdentificeerd door het filterpredikaat; het is gebaseerd (zoals alle niet-gefilterde indexen) op verloop ten opzichte van de hele tabel. Dit betekent dat, afhankelijk van welk percentage van de tabel zich in de gefilterde index bevindt, het aantal rijen in de index kan verviervoudigen of halveren en dat de statistieken niet worden bijgewerkt, tenzij u dit handmatig doet. Kimberly Tripp heeft hier geweldige informatie over gegeven (en Gail Shaw noemt een voorbeeld waarbij er 257.000 updates nodig waren voordat de statistieken werden bijgewerkt voor een gefilterde index die slechts 10.000 rijen bevatte):

http://www.sqlskills.com/blogs/kimberly/filtered-indexes-and-filtered-stats-might-become-seriously-out-of-date/
http://www.sqlskills.com/ blogs/kimberly/category/filtered-indexes/

Ook heeft Kimberly's collega, Joe Sack (@JosephSack), een Connect-item ingediend waarin wordt voorgesteld dit gedrag te corrigeren voor zowel gefilterde indexen als gefilterde statistieken.

Uitdrukkingsbeperkingen filteren

Er zijn verschillende constructies die u niet kunt gebruiken in een filterpredikaat, zoals NOT IN , OR en dynamische / niet-deterministische predikaten zoals WHERE col >= DATEADD(DAY, -1, GETDATE()) . Het is ook mogelijk dat de optimizer een gefilterde index niet herkent als het predikaat niet exact overeenkomt met de WHERE clausule in de indexdefinitie. Hier zijn een paar Connect-items die hier enige ondersteuning proberen te krijgen voor een betere dekking:

Gefilterde index staat geen filters op disjuncties toe (gesloten:door ontwerp)
Gefilterde index maken mislukt met NOT IN-clausule (gesloten:door ontwerp)
Ondersteuning voor complexere WHERE-clausule in gefilterde indexen (actief)

Andere mogelijke toepassingen momenteel niet mogelijk

We kunnen momenteel geen gefilterde index maken voor een persistente berekende kolom, zelfs niet als deze deterministisch is. We kunnen een externe sleutel niet naar een unieke gefilterde index verwijzen; als we willen dat een index de externe sleutel ondersteunt naast de query's die door de gefilterde index worden ondersteund, moeten we een tweede, redundante, niet-gefilterde index maken. En hier zijn een paar andere soortgelijke beperkingen die over het hoofd zijn gezien of nog niet zijn overwogen:

Moet mogelijk zijn om een ​​gefilterde index te maken op een deterministische persistente berekende kolom (actief)
Sta gefilterde unieke index toe als kandidaat-sleutel voor een externe sleutel (actief)
mogelijkheid om filterindexen te maken op geïndexeerde weergaven (gesloten:lost niet op)
Partitioneringsfout 1908 – Partitionering verbeteren (gesloten:lost niet op)
CREER "GEFILTERDE" KOLOMSTORE INDEX (actief)

Problemen met MERGE

En MERGE maakt weer een verschijning op mijn "let op" lijst:

MERGE evalueert gefilterde index per rij, niet na bewerking, wat een schending van de gefilterde index veroorzaakt (gesloten:lost niet op)
MERGE kan niet worden bijgewerkt met gefilterde index op zijn plaats (gesloten:vast)
MERGE instructiefout wanneer INSERT/DELETE gebruikte en gefilterde index (actief)
MERGE rapporteert onjuist unieke sleutelovertredingen (actief)


Hoewel een van deze (schijnbaar nauw verwante) bugs zegt dat het is opgelost in SQL Server 2012, moet je mogelijk contact opnemen met PSS als je een variatie op dit probleem tegenkomt, met name bij eerdere versies (of stop met het gebruik van MERGE , zoals ik eerder heb voorgesteld).

Tool / DMV / ingebouwde beperkingen

Er zijn veel DMV's, DBCC-opdrachten, systeemprocedures en clienttools waarop we na verloop van tijd beginnen te vertrouwen. Niet al deze dingen worden echter bijgewerkt om te profiteren van nieuwe functies; gefilterde indexen zijn geen uitzondering. De volgende Connect-items wijzen op enkele problemen die u kunnen laten struikelen als u verwacht dat ze werken met gefilterde indexen:

Er is geen manier om een ​​gefilterde index van SSMS te maken tijdens het ontwerpen van een nieuwe tabel (gesloten:lost niet op)
De filterexpressie van een gefilterde index gaat verloren wanneer een tabel wordt gewijzigd door de Table Designer (gesloten:lost niet op)
Tabelontwerper schrijft geen WHERE-clausule in gefilterde indexen (actief)
SSMS-tabelontwerper bewaart de expressie van het indexfilter niet bij het opnieuw opbouwen van een tabel (gesloten:lost niet op)
DBCC PAGE onjuiste uitvoer met gefilterde indexen (actief)
SQL 2008 gefilterde indexsuggesties van DM-weergaven en DTA (gesloten:lost niet op)
Verbeteringen aan de ontbrekende indexen DMV's voor gefilterde indexen (gesloten:lost niet op)
Syntaxisfout bij het repliceren van gecomprimeerde gefilterde indexen (gesloten:lost niet op)
Agent:taken gebruiken niet-standaard opties bij het uitvoeren van een T-SQL-script (gesloten:lost niet op)
Bekijk afhankelijkheden mislukt met Transact-SQL-fout 515 (actief)
Afhankelijkheden bekijken mislukt op bepaalde objecten (gesloten:lost niet op)
Verschillen in indexopties worden niet gedetecteerd in de schemavergelijking voor twee databases (gesloten:extern)
Suggereer de voorwaarde van het indexfilter bloot te stellen in alle weergaven van indexinformatie (gesloten:lost niet op)
sp_helpIndex-resultaten moeten de filterexpressie van filterindexen bevatten (actief)
Sp_help, sp_columns, sp_helpindex voor 2008-functies overbelasten (gesloten:lost niet op)


Voor de laatste drie, houd je adem niet in - Microsoft zal waarschijnlijk geen tijd investeren in de sp_-procedures, DMV's, INFORMATION_SCHEMA-weergaven, enz. Bekijk in plaats daarvan Kimberly Tripp's sp_helpindex-herschrijvingen, die informatie bevatten over gefilterde indexen met andere nieuwe functies die Microsoft heeft achtergelaten.

Optimizerbeperkingen

Er zijn verschillende Connect-items die gevallen beschrijven waarin gefilterde indexen *kunnen* worden gebruikt door de optimizer, maar in plaats daarvan worden genegeerd. In sommige gevallen worden deze niet als "bugs" beschouwd, maar eerder als "lacunes in functionaliteit"…

SQL gebruikt geen gefilterde index voor een eenvoudige zoekopdracht (gesloten:door ontwerp)
Gefilterd uitvoeringsplan voor index is niet geoptimaliseerd (gesloten:lost niet op)
Gefilterde index niet gebruikt en sleutel opzoeken zonder uitvoer (gesloten:lost niet op)
Gebruik van gefilterde index op BIT-kolom hangt af van de exacte SQL-expressie die wordt gebruikt in de WHERE-clausule (actief)
Gekoppelde serverquery wordt niet goed geoptimaliseerd als er een gefilterde unieke index bestaat (gesloten:lost niet op)
Row_Number() geeft onvoorspelbare resultaten over gekoppelde servers waar gefilterde indexen worden gebruikt (gesloten:geen repro)
Voor de hand liggende gefilterde index niet gebruikt door QP (gesloten:door ontwerp)
Herken unieke gefilterde indexen als uniek (actief)


Paul White (@SQL_Kiwi) plaatste onlangs hier op SQLPerformance.com een ​​bericht dat uitgebreid ingaat op een aantal van de bovenstaande optimalisatiebeperkingen.

En Tim Chapman schreef een geweldige post waarin hij enkele andere beperkingen van gefilterde indexen uiteenzette - zoals het onvermogen om het predikaat te matchen met een lokale variabele (vastgesteld in 2008 R2 SP1) en het onvermogen om een ​​gefilterde index op te geven in een indexhint.

Conclusie

Gefilterde indexen hebben een groot potentieel en ik had er zeer hoge verwachtingen van toen ze voor het eerst werden geïntroduceerd in SQL Server 2008. De meeste beperkingen die bij hun eerste versie werden geleverd, bestaan ​​echter nog steeds, anderhalf (of twee, afhankelijk van uw perspectief) grote releases later. Bovenstaande lijkt een behoorlijk uitgebreide waslijst van zaken die aangepakt moeten worden, maar het was niet mijn bedoeling dat het zo over zou komen. Ik wil gewoon dat mensen zich bewust zijn van het enorme aantal potentiële problemen waarmee ze rekening moeten houden wanneer ze profiteren van gefilterde indexen.


  1. Ik kijk uit naar PGConf India 2017

  2. Beste DBaaS-oplossing voor MySQL

  3. Een tabel bijwerken in Oracle als een veldwaarde Null is en bepalen of de update succesvol is

  4. Komma's binnen CSV-gegevens