sql >> Database teknologi >  >> RDS >> Sqlserver

Tre nemme SQL Server Performance-gevinster

Som enhver veteranproduktion DBA ved, er du ofte under betydeligt pres for at diagnosticere og afhjælpe problemer med databasens ydeevne så hurtigt som muligt. Her er tre ting, du muligvis kan drage fordel af, afhængigt af din arbejdsbyrde og infrastruktur, for at have en meget mærkbar positiv indvirkning på din databaseydeevne.

Grundlæggende indstilling af rækkebutiksindeks

De fleste SQL Server-forekomster, som jeg er stødt på i min karriere, har haft nogle relativt nemme muligheder for justering af rækkebutikker. En god ting ved justering af rækkebutiksindeks er, at det oftere er under din direkte kontrol som DBA, især sammenlignet med justering af forespørgsler eller lagrede procedurer, som ofte er under kontrol af udviklere eller 3-partsleverandører.

Nogle DBA'er er tilbageholdende med at foretage nogen indeksjustering (især på 3-parts databaser), fordi de er bekymrede for at bryde noget eller bringe leverandørens support til databasen eller applikationen i fare. Det er klart, at du skal være mere forsigtig med 3-parts databaser og prøve at kontakte leverandøren, før du selv foretager indeksændringer, men i nogle situationer har du muligvis ikke noget andet levedygtigt alternativ (udover at kaste hurtigere hardware og lagring på problemet ).

Du kan køre et par nøgleforespørgsler fra mine SQL Server Diagnostic Information Queries for at få en god idé om, om du måske har nogle nemme muligheder for indeksjustering på din instans eller database. Du bør være på udkig efter manglende indeksanmodninger, manglende indeksadvarsler, underbrugte eller ikke-brugte ikke-klyngede indekser og mulige datakomprimeringsmuligheder.

Det kræver en vis erfaring, god dømmekraft og viden om din arbejdsbyrde for at foretage korrekt indeksjustering. Det er alt for almindeligt at se folk foretage ukorrekt indeksjustering ved overilet at foretage mange indeksændringer uden at foretage den korrekte analyse.

Her er nogle forespørgsler, som jeg kan lide at bruge på databaseniveau:

-- Missing Indexes for current database by Index Advantage  (Query 1) (Missing Indexes)
 
SELECT DISTINCT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
  migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
  mid.equality_columns, mid.inequality_columns, mid.included_columns,
  migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact,
  OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
  FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
  INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
  ON migs.group_handle = mig.index_group_handle
  INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
  ON mig.index_handle = mid.index_handle
  INNER JOIN sys.partitions AS p WITH (NOLOCK)
  ON p.[object_id] = mid.[object_id]
  WHERE mid.database_id = DB_ID()
  AND p.index_id < 2 
  ORDER BY index_advantage DESC OPTION (RECOMPILE);
 
  ------
  -- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
  -- SQL Server is overly eager to add included columns, so beware
  -- Do not just blindly add indexes that show up from this query!!!
 
  -- Find missing index warnings for cached plans in the current database  (Query 2) (Missing Index Warnings)
  -- Note: This query could take some time on a busy instance
 
  SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
                 cp.objtype, cp.usecounts, cp.size_in_bytes, query_plan
  FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
  CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
  WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
  AND dbid = DB_ID()
  ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
 
  ------
  -- Helps you connect missing indexes to specific stored procedures or queries
  -- This can help you decide whether to add them or not
  -- Possible Bad NC Indexes (writes >= reads)  (Query 3) (Bad NC Indexes)
 
  SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
  i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
  s.user_updates AS [Total Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads],
  s.user_updates - (s.user_seeks + s.user_scans + s.user_lookups) AS [Difference]
  FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
  INNER JOIN sys.indexes AS i WITH (NOLOCK)
  ON s.[object_id] = i.[object_id]
  AND i.index_id = s.index_id
  WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
  AND s.database_id = DB_ID()
  AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
  AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
  AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0
  ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
 
  ------
  -- Look for indexes with high numbers of writes and zero or very low numbers of reads
  -- Consider your complete workload, and how long your instance has been running
  -- Investigate further before dropping an index!
  -- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 4) (Buffer Usage)
  -- Note: This query could take some time on a busy instance
  SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
  CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
  COUNT(*) AS [BufferCount], p.[Rows] AS [Row Count],
  p.data_compression_desc AS [Compression Type]
  FROM sys.allocation_units AS a WITH (NOLOCK)
  INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
  ON a.allocation_unit_id = b.allocation_unit_id
  INNER JOIN sys.partitions AS p WITH (NOLOCK)
  ON a.container_id = p.hobt_id
  WHERE b.database_id = CONVERT(int, DB_ID())
  AND p.[object_id] > 100
  AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
  AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
  AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
  GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
  ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
 
  ------
  -- Tells you what tables and indexes are using the most memory in the buffer cache
  -- It can help identify possible candidates for data compression

Brug af forsinket holdbarhed

Funktionen for forsinket holdbarhed blev tilføjet til produktet i SQL Server 2014, så den har været tilgængelig i et stykke tid. Forsinkede varige transaktionsforpligtelser er asynkrone og rapporterer en transaktionsforpligtelse som vellykket før logposterne for transaktionen skrives faktisk til lagerundersystemet. Forsinkede varige transaktioner bliver faktisk ikke holdbare, før transaktionslogposterne er tømt til disken.

Denne funktion er tilgængelig i alle udgaver af SQL Server. På trods af dette ser jeg sjældent, at det bliver brugt, når jeg ser på klientdatabaser. Forsinket holdbarhed åbner op for muligheden for noget datatab, op til en hel logbuffer i et worst case scenario (som forklaret af Paul Randal her), så det er bestemt ikke passende for et RPO-scenarie, hvor absolut intet datatab er acceptabelt.

Forsinket holdbarhed reducerer transaktionsforsinkelsen, fordi den ikke venter på, at log IO afsluttes og returnerer kontrollen tilbage til klienten, og den reducerer også låsning og diskkonflikt for samtidige transaktioner. Disse to fordele kan ofte have en meget positiv effekt på din forespørgsel og applikationsydelse med den passende meget skrivetunge arbejdsbyrde.

Forsinket holdbarhed vil oftest hjælpe tunge OLTP-type arbejdsbelastninger, der har meget hyppige, små skrivetransaktioner, hvor du ser høj skrivelatens på filniveau fra sys.dm_io_virtual_file_stats på transaktionslogfilen, og/eller du ser høje WRITELOG-ventinger fra sys. dm_os_wait_stats.

Du kan nemt tvinge SQL Server 2014 eller nyere til at bruge forsinket holdbarhed for alle transaktioner (uden kodeændringer) ved at køre følgende kommando:

ALTER DATABASE AdventureWorks2014 SET DELAYED_DURABILITY = FORCED;

Jeg har haft klienter, der programmæssigt slår forsinket holdbarhed til og fra på forskellige tidspunkter af dagen (såsom under planlagt ETL eller vedligeholdelsesaktivitet). Jeg har også haft kunder, der til enhver tid bruger forsinket holdbarhed, da de har en passende arbejdsbyrde og risikotolerance for datatab.

Endelig har jeg haft kunder, der aldrig ville overveje at bruge forsinket holdbarhed, eller simpelthen ikke har brug for det med deres arbejdsbyrde. Hvis du har mistanke om, at din arbejdsbyrde kan drage fordel af at bruge forsinket holdbarhed, men du er bekymret over det mulige datatab, så er der andre alternativer, du kan overveje.

Et alternativ er den vedvarende logbufferfunktion i SQL Server 2016 SP1, hvor du kan oprette en anden transaktionslogfil på 20 MB på en lagervolumen med direkte adgangstilstand (DAX), der er hostet på en NV-DIMM persistent hukommelsesenhed. Denne ekstra transaktionslogfil bruges til at cache loggens hale med adgang på byteniveau, der omgår den konventionelle lagerstak på blokniveau.

Hvis du tror, ​​at din arbejdsbyrde kan drage fordel af at bruge den vedvarende logbufferfunktion, kan du eksperimentere med midlertidigt at bruge forsinket holdbarhed for at se, om der er en reel ydeevnefordel med din arbejdsbyrde, før du bruger pengene på den NV-DIMM persistente hukommelse, som du skal bruge den vedvarende logbufferfunktion.

Flytter tempdb til Intel Optane DC P4800X Storage

Jeg har haft stor succes med adskillige nylige klienter, der flyttede deres tempdb-databasefiler fra en anden type lager til et logisk drev, der blev understøttet af et par Intel Optane DC P4800X PCIe NVMe-lagerkort (i et software-RAID 1-array).

Disse lagerkort er tilgængelige i 375 GB, 750 GB og 1,5 TB kapacitet (selvom 1,5 TB kapaciteten er helt ny og stadig svær at finde). De har ekstremt lav latenstid (meget lavere end nogen form for NAND-flash-lagring), fremragende tilfældig I/O-ydeevne ved lave kødybder (meget bedre end NAND-flash-lagring) med ensartede læsesvartider under en meget stor skrivebelastning.

De har også højere skriveudholdenhed end "skrive-intensive" enterprise NAND flash-lagring, og deres ydeevne forringes ikke, da de er tæt på at være fulde. Disse egenskaber gør disse kort ekstremt velegnede til mange tunge tempdb-arbejdsbelastninger, især tunge OLTP-arbejdsbelastninger og situationer, hvor du bruger RCSI i dine brugerdatabaser (hvilket sætter den resulterende version Store-arbejdsbelastning på tempdb).

Det er også meget almindeligt at se høj skriveforsinkelse på filniveau på tempdb-datafiler fra sys.dm_io_virtual_file_stats DMV, så flytning af dine tempdb-datafiler til Optane-lagring er en måde at løse dette problem direkte på, som kan være hurtigere og nemmere end konventionel justering af arbejdsbyrden.

En anden mulig anvendelse af Optane-lagerkort er som hjemsted for dine transaktionslogfiler. Du kan også bruge Optane-lagring med ældre versioner af SQL Server (så længe dit OS og hardware understøtter det). Det er et muligt alternativ til at bruge forsinket holdbarhed (som kræver SQL Server 2014) eller at bruge den vedvarende logbufferfunktion (som kræver SQL Server 2016 SP1).

Konklusion

Jeg diskuterede tre teknikker til at opnå en hurtig præstationssejr med SQL Server:

  • Konventionel rækkelagerindeksjustering er anvendelig til alle versioner af SQL Server, og det er et af de bedste værktøjer i dit arsenal.
  • Forsinket holdbarhed er tilgængelig i SQL Server 2014 og nyere, og det kan være meget fordelagtigt med nogle arbejdsbelastningstyper (og RPO-krav). Vedvarende logbuffer er tilgængelig i SQL Server 2016 SP1, og den giver lignende fordele som forsinket holdbarhed uden fare for tab af data.
  • Ved at flytte visse typer databasefiler til Intel Optane-lagring kan det hjælpe med at afhjælpe ydeevneproblemer med tempdb eller med brugerdatabasetransaktionslogfiler. Du kan bruge Optane-lagerplads med ældre versioner af SQL Server, og der kræves ingen kode- eller konfigurationsændringer.

  1. Brug af pt-pg-summary Percona Toolkit til PostgreSQL

  2. Håndtering af fejlhåndtering, mens du kører sqlplus fra shell-scripts

  3. Fremskynder rækkeoptællingen i MySQL

  4. Bruger forskellige databaser forskellige navnecitater?