Benjamin Nevarez er en uafhængig konsulent baseret i Los Angeles, Californien, som har specialiseret sig i tuning og optimering af SQL Server-forespørgsler. Han er forfatter til "SQL Server 2014 Query Tuning &Optimization" og "Inside the SQL Server Query Optimizer" og medforfatter til "SQL Server 2012 Internals". Med mere end 20 års erfaring i relationelle databaser har Benjamin også været foredragsholder ved mange SQL Server-konferencer, herunder PASS Summit, SQL Server Connections og SQLBits. Benjamins blog kan findes på http://www.benjaminnevarez.com, og han kan også nås via e-mail på admin på benjaminnevarez dot com og på twitter på @BenjaminNevarez.
Har du nogensinde fundet en planregression efter en SQL Server-opgradering og ønskede at vide, hvad den tidligere eksekveringsplan var? Har du nogensinde haft et problem med forespørgselsydeevne på grund af det faktum, at en forespørgsel uventet fik en ny eksekveringsplan? Ved det sidste PASS Summit afslørede Conor Cunningham en ny SQL Server-funktion, som kan være nyttig til at løse ydeevneproblemer relateret til disse og andre ændringer i eksekveringsplaner.
Denne funktion, kaldet Query Store, kan hjælpe dig med ydeevneproblemer i forbindelse med planændringer og vil snart være tilgængelig på SQL Azure og senere på den næste version af SQL Server. Selvom det forventes at være tilgængeligt på Enterprise Edition af SQL Server, vides det endnu ikke, om det vil være tilgængeligt på Standard eller andre udgaver. For at forstå fordelene ved Query Store, lad mig tale kort om forespørgselsfejlfindingsprocessen.
Hvorfor er en forespørgsel langsom?
Når du har opdaget, at et ydeevneproblem skyldes, at en forespørgsel er langsom, er næste skridt at finde ud af hvorfor. Det er klart, at ikke alle problemer er relateret til planændringer. Der kan være flere årsager til, at en forespørgsel, der har fungeret godt, pludselig er langsom. Nogle gange kan dette være relateret til blokering eller et problem med andre systemressourcer. Noget andet kan have ændret sig, men udfordringen kan være at finde ud af hvad. Mange gange har vi ikke en baseline om systemressourceforbrug, forespørgselsudførelsesstatistikker eller ydeevnehistorik. Og normalt aner vi ikke, hvad den gamle plan var. Det kan være tilfældet, at nogle ændringer, for eksempel data, skema eller forespørgselsparametre, fik forespørgselsprocessoren til at lave en ny plan.
Planlæg ændringer
Ved sessionen brugte Conor Picasso Database Query Optimizer Visualizer-værktøjet, selvom han ikke nævnte det ved navn, til at vise, hvorfor planerne i den samme forespørgsel ændrede sig, og forklarede det faktum, at forskellige planer kunne vælges til den samme forespørgsel baseret på selektiviteten af deres prædikater. Han nævnte endda, at forespørgselsoptimeringsteamet bruger dette værktøj, som er udviklet af Indian Institute of Science. Et eksempel på visualiseringen (klik for at forstørre):
Picasso Database Query Optimizer Visualizer
Hver farve i diagrammet er en anden plan, og hver plan er valgt baseret på prædikaternes selektivitet. En vigtig kendsgerning er, når en grænse krydses i grafen, og en anden plan vælges, de fleste gange bør omkostningerne og ydeevnen for begge planer være ens, da selektiviteten eller det anslåede antal rækker kun ændrede sig en smule. Dette kan for eksempel ske, når en ny række tilføjes til en tabel, som kvalificerer til det brugte prædikat. Men i nogle tilfælde, for det meste på grund af begrænsninger i omkostningsmodellen for forespørgselsoptimering, hvor den ikke er i stand til at modellere noget korrekt, kan den nye plan have en stor ydeevneforskel i forhold til den forrige, hvilket skaber et problem for din applikation. I øvrigt er planerne vist på diagrammet den endelige plan valgt af forespørgselsoptimeringsværktøjet, forveksle dette ikke med de mange alternativer, som optimereren skal overveje for kun at vælge én.
En vigtig kendsgerning, efter min mening, som Conor ikke dækkede direkte, var ændringen af planer på grund af regressioner efter ændringer på kumulative opdateringer (CU'er), service packs eller versionsopgraderinger. En stor bekymring, der kommer til at tænke på med ændringer i forespørgselsoptimeringsværktøjet, er planregression. Frygten for planregression er blevet betragtet som den største hindring for forbedringer af forespørgselsoptimering. Regressioner er problemer, der introduceres, efter at en rettelse er blevet anvendt på forespørgselsoptimeringsværktøjet, og nogle gange omtales som det klassiske "to eller flere forkerte gør en ret." Dette kan ske, når f.eks. to dårlige estimeringer, den ene overvurderer en værdi og den anden undervurderer den, ophæver hinanden, hvilket heldigvis giver et godt estimat. Korrigering af kun én af disse værdier kan nu føre til et dårligt estimat, som kan have en negativ indvirkning på valget af planvalg og forårsage en regression.
Hvad gør Query Store?
Conor nævnte, at Query Store udfører og kan hjælpe med følgende:
- Gem historikken for forespørgselsplaner i systemet;
- Fang ydeevnen af hver forespørgselsplan over tid;
- Identificer forespørgsler, der er "blevet langsommere for nylig";
- Gør det muligt for dig at gennemtvinge planer hurtigt; og,
- Sørg for, at dette fungerer på tværs af servergenstarter, opgraderinger og genkompilering af forespørgsler.
Så denne funktion gemmer ikke kun planerne og relaterede oplysninger om forespørgselsydeevne, men kan også hjælpe dig med nemt at gennemtvinge en gammel forespørgselsplan, som i mange tilfælde kan løse et ydeevneproblem.
Sådan bruger du Query Store
Du skal aktivere Query Store ved at bruge ALTER DATABASE CURRENT SET QUERY_STORE = ON;
udmelding. Jeg prøvede det i mit nuværende SQL Azure-abonnement, men sætningen returnerede en fejl, da det ser ud til, at funktionen ikke er tilgængelig endnu. Jeg kontaktede Conor, og han fortalte mig, at funktionen snart vil være tilgængelig.
Når Query Store er aktiveret, begynder den at indsamle planerne og forespørgselsydeevnedata, og du kan analysere disse data ved at se på Query Store-tabellerne. Jeg kan i øjeblikket se disse tabeller på SQL Azure, men da jeg ikke var i stand til at aktivere Query Store, returnerede katalogerne ingen data.
Du kan analysere de indsamlede oplysninger enten proaktivt for at forstå ændringerne i forespørgselsydeevnen i din applikation eller med tilbagevirkende kraft, hvis du har et ydeevneproblem. Når du har identificeret problemet, kan du bruge traditionelle forespørgselsindstillingsteknikker til at prøve at løse problemet, eller du kan bruge sp_query_store_force_plan
gemt procedure for at gennemtvinge en tidligere plan. Planen skal fanges i Query Store for at blive tvunget, hvilket naturligvis betyder, at den er en gyldig plan (i det mindste da den blev indsamlet; mere om det senere), og den blev genereret af forespørgselsoptimeringsværktøjet før. For at gennemtvinge en plan skal du bruge plan_id
, tilgængelig i sys.query_store_plan
katalog. Når du ser på de forskellige gemte metrics, som minder meget om det, der er gemt for eksempel i sys.dm_exec_query_stats
, kan du træffe beslutningen om at optimere til en bestemt metrik, såsom CPU, I/O osv. Så kan du blot bruge en erklæring som denne:
EXEC sys.sp_query_store_force_plan @query_id = 1, @plan_id = 1;
Dette fortæller SQL Server om at tvinge plan 1 på forespørgsel 1. Teknisk kunne du gøre det samme ved at bruge en planvejledning, men det ville være mere kompliceret, og du ville være nødt til manuelt at indsamle og finde den nødvendige plan i første omgang.
Hvordan fungerer Query Store?
Faktisk at tvinge en plan bruger planvejledninger i baggrunden. Conor nævnte, at "når du kompilerer en forespørgsel, tilføjer vi implicit et USE PLAN-tip med fragmentet af XML-planen, der er knyttet til den erklæring." Så du behøver ikke længere bruge en planvejledning længere. Husk også på, at det, på samme måde som at bruge en planvejledning, ikke er garanteret at have præcis den tvungne plan, men i det mindste noget der ligner den. For en påmindelse om, hvordan planvejledninger fungerer, tag et kig på denne artikel. Derudover skal du være opmærksom på, at der er nogle tilfælde, hvor det ikke virker at tvinge en plan, et typisk eksempel er, når skemaet er ændret, dvs. hvis en lagret plan bruger et indeks, men indekset ikke længere eksisterer. I dette tilfælde kan SQL Server ikke gennemtvinge planen, vil udføre en normal optimering, og den vil registrere det faktum, at forceringen af planen mislykkedes i sys.query_store_plan
katalog.
Arkitektur
Hver gang SQL Server kompilerer eller udfører en forespørgsel, sendes en besked til Query Store. Dette vises næste gang.
Oversigt over forespørgselsbutiksworkflow
Kompilerings- og udførelsesoplysningerne opbevares først i hukommelsen og gemmes derefter på disken, afhængigt af Query Store-konfigurationen (dataene aggregeres i henhold til INTERVAL_LENGTH_MINUTES
parameter, som som standard er en time, og tømmes til disk i henhold til DATA_FLUSH_INTERVAL_SECONDS
parameter). Dataene kan også skylles til disken, hvis der er hukommelsestryk på systemet. Under alle omstændigheder vil du være i stand til at få adgang til alle data, både i hukommelsen og disken, når du kører sys.query_store_runtime_stats
katalog.
Kataloger
De indsamlede data bevares på disken og gemmes i brugerdatabasen, hvor Query Store er aktiveret (og indstillinger gemmes i sys.database_query_store_options
. Query Store-katalogerne er:
sys.query_store_query_text | Forespørgselstekstoplysninger |
sys.query_store_query | Forespørgselstekst plus den brugte plan, der påvirker SET-indstillingerne |
sys.query_store_plan | Udførelsesplaner, inklusive historik |
sys.query_store_runtime_stats | Forespørg efter runtime-statistik |
sys.query_store_runtime_stats_interval | Start- og sluttidspunkt for intervaller |
sys.query_context_settings | Forespørg efter oplysninger om kontekstindstillinger |
Forespørgselsbutiksvisninger
Runtime-statistikker fanger en hel række af metrikker, inklusive gennemsnit, sidste, min, maks. og standardafvigelse. Her er det fulde sæt af kolonner for sys.query_store_runtime_stats
:
runtime_stats_id | plan_id | runtime_stats_interval_id | ||
execution_type | execution_type_desc | first_execution_time | last_execution_time | count_executions |
avg_duration | last_duration | min_duration | max_duration | stdev_duration |
avg_cpu_time | last_cpu_time | min_cpu_time | max_cpu_time | stdev_cpu_time |
avg_logical_io_reads | last_logical_io_reads | min_logical_io_reads | max_logical_io_reads | stdev_logical_io_reads |
avg_logical_io_writes | last_logical_io_writes | min_logical_io_writes | max_logical_io_writes | stdev_logical_io_writes |
avg_physical_io_reads | last_physical_io_reads | min_physical_io_reads | max_physical_io_reads | stdev_physical_io_reads |
avg_clr_time | last_clr_time | min_clr_time | max_clr_time | stdev_clr_time |
avg_dop | last_dop | min_dop | max_dop | stdev_dop |
avg_query_max_used_memory | last_query_max_used_memory | min_query_max_used_memory | max_query_max_used_memory | stdev_query_max_used_memory |
avg_rowcount | last_rowcount | min_rowcount | max_rowcount | stdev_rowcount |
Kolonner i sys.query_store_runtime_stats
Disse data fanges kun, når forespørgselsudførelsen slutter. Forespørgselsbutikken tager også hensyn til forespørgslens SET
muligheder, som kan påvirke valget af en eksekveringsplan, da de påvirker ting som resultaterne af at evaluere konstante udtryk under optimeringsprocessen. Jeg dækker dette emne i et tidligere indlæg.
Konklusion
Dette vil helt sikkert være en fantastisk funktion og noget, jeg gerne vil prøve så hurtigt som muligt (i øvrigt viser Conors demo "SQL Server 15 CTP1", men disse bits er ikke offentligt tilgængelige). Forespørgselslageret kan være nyttigt til opgraderinger, som kunne være en CU-, servicepakke- eller SQL Server-version, da du kan analysere de oplysninger, der indsamles af forespørgselslageret før og efter for at se, om en forespørgsel er gået tilbage. (Og hvis funktionen er tilgængelig i lavere udgaver, kan du endda gøre dette i et SKU-opgraderingsscenarie.) At vide dette kan hjælpe dig med at tage nogle specifikke handlinger afhængigt af problemet, og en af disse løsninger kunne være at gennemtvinge den tidligere plan som forklaret før.