Query Timeout? Možná děláš těch TOP 1 000 „SELECT *“ příliš webseidon MSSQL db Lukáš Adámek Klokočov Vítkov Fulnek Odry

Query Timeout? Možná děláš těch TOP 1 000 „SELECT *“ příliš

Query Timeout? Možná děláš těch TOP 1 000 „SELECT *“ příliš

Query Timeout? Možná děláš těch TOP 1 000 „SELECT *“ příliš

Devátá hodina, uživatel klikne na „Report za Q1“ a SQL Server mu po 30 s vyplivne Timeout expired. Zní povědomě? V 90 % případů nejde o nedostatek CPU, ale o příliš velké SELECT *. V tomto článku rozlouskneme, jak tajně škodí hvězdička, proč TOP 1000 není alibi, jak odhalit pomalé plány a zkrátit dotazy z minut na milisekundy.



1 · Co je query timeout a kde se nastavuje

Query timeout je ochranná lhůta klienta (application, driver), po které ukončí čekání na response z SQL Serveru. Typicky:

  • ADO.NET SqlCommand.CommandTimeout default 30 s
  • ODBC SQL_ATTR_QUERY_TIMEOUT
  • SSMS Options → Execution → Query timeout

Server dotaz často zpracovává dál; jen klient kapituloval. Cíl není navyšovat timeout na 300 s, ale zkrátit dotaz na < 2 s.


2 · Syndrom „SELECT *“: proč je to tichý zabiják

2.1 Přetékající kolony

Hvězdička vytáhne všechny sloupce, i když aplikace potřebuje dva. Více dat → více I/O → pomalejší síť → větší paměť → déle se zamyká.

2.2 Columnstore penalty

Columnstore index je super pro analytiku, ale SELECT * na 100 sloupců vyčistí delta rowgroup a zahodí kompresi.

2.3 Fix

SELECT OrderID, OrderDate, TotalPrice
FROM dbo.Orders
WHERE CustomerID = 123;

Ušetříš I/O i síť. Připni COVERING index a plán změní Key Lookup → Index Seek.


3 · TOP N – lék, ale i past

SELECT TOP 1000 * FROM AuditLog ORDER BY TimeStamp DESC; vypadá nevinně, ale:

  • SQL Server nejčastěji sortuje všechno a pak vezme 1000 – pokud není index na TimeStamp DESC.
  • Chybí OFFSET 0 FETCH NEXT → nelze použít pager friendly plan.

3.1 Správný pattern

CREATE INDEX IX_Audit_TimeStamp_DESC ON AuditLog(TimeStamp DESC) INCLUDE (UserID, Action);

SELECT TOP 1000 UserID, Action, TimeStamp
FROM dbo.AuditLog
ORDER BY TimeStamp DESC;

4 · Index seek vs. scan: B‑tree detektivka

Seek = klíč přímo k listům, Scan = čtení celé tabulky.

  • Fragmentace > 30 % → méně stránek v cache, seek se mění na range scan.
  • Kompozitní index: pořadí sloupců nejselektivnější → méně selektivní.
  • Filtered index na Status='Active' zmenší velikost o 80 %.

5 · Statistiky & kardinalita: věštírna plánovače

Plánovač dotazu předem odhaduje, kolik řádků se vrátí. Špatný odhad = špatný plán.

UPDATE STATISTICS dbo.Orders WITH FULLSCAN;
ALTER DATABASE CURRENT SET AUTO_UPDATE_STATISTICS ON;

Po velké dávce INSERT/DELETE raději sp_updatestats.


6 · Plan Cache: sniffing, recompile, fix

6.1 Parameter Sniffing

První provedení uloží plán vhodný pro vstup @ID = 1; další dotaz s @ID = 100000 může špatně škálovat.

6.2 Řešení

  • OPTION(RECOMPILE) – dražší compile, ale jistota.
  • OPTIMIZE FOR (@ID UNKNOWN) – neutrální odhad.
  • sp_create_plan_guide – přinutit plán.

7 · Diagnostika: DMV, Query Store, Extended Events

7.1 DMV – Top timeout queries

SELECT TOP 15
   qs.total_worker_time/1000 AS CPU_ms,
   qs.total_logical_reads    AS LReads,
   qs.execution_count        AS Runs,
   qs.total_elapsed_time/qs.execution_count/1000 AS Avg_ms,
   SUBSTRING(qt.text,1,200)  AS QueryText
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
WHERE qs.last_elapsed_time/1000 > 5000 -- >5 s
ORDER BY Avg_ms DESC;

7.2 Query Store

Zapni ReadWrite. Report Regressed queries ukáže dotazy, které loni běžely 50 ms a teď 5 s.

7.3 Extended Events template

CREATE EVENT SESSION SlowQueries ON SERVER
ADD EVENT sqlserver.rpc_completed (
  ACTION(sqlserver.sql_text)
  WHERE duration > 5000000) -- >5 s
ADD TARGET package0.ring_buffer;

8 · Skripty na hledání TOP timeoutů

8.1 Chybové hlášky v SQL Error Logu

EXEC xp_readerrorlog 0, 1, N'timeout' ;

8.2 Počet timeoutů za den

SELECT CAST(ErrorLogTime AS date) AS Day,
       COUNT(*)                AS Timeouts
FROM sys.fn_get_audit_file('C:\MSSQL\Log\*.sqlaudit', NULL, NULL)
WHERE statement LIKE '%Timeout expired%'
GROUP BY CAST(ErrorLogTime AS date);

9 · Case study: 12 s → 120 ms bez nového HW

KrokČasZměnaLatency
Původní dotazSELECT * + sort12 000 ms
Odebráno *10 minJen 4 sloupce5 800 ms
Index na Date DESC15 minCOVERING680 ms
Updated stats3 minFULLSCAN320 ms
Parameter Hint2 minOPTION RECOMPILE120 ms

Zero Kč, 50 min práce, 100× zrychlení.


10 · Desatero prevence timeoutu

  1. Nikdy SELECT * v produkci.
  2. Používej TOP N s INDEXED ORDER BY.
  3. Update statistics po bulk loadu > 20 % dat.
  4. Pravidelný IndexOptimize (rebuild/reorganize).
  5. Zapni Query Store a monitoruj regrese.
  6. Loguj Duration>5 s do Central Logging.
  7. Nastav MAXDOP pro OLTP na <= 4.
  8. Zákaz kartézských joinů (NO_JOIN_PREDICATE alert).
  9. Archivu­j stará data do history tabulek/partition.
  10. Rehearsuj prod deploy na kopii DB se stejným objemem.

11 · FAQ: limit, deadlock, či latence?

Navýšit timeout na klientu?
Nouzovka pro reporty > 120 s; jinak řeš root cause.
Timeout vs. deadlock?
Deadlock = 1205, trvá ms; timeout hlásí 0 – 3642 a trvá desítky s.
Pomůže více RAM?
Až po optimalizaci dotazů; jinak jen dražší špatný plán.

12 · Závěr

Timeouty jsou SOS světlice databáze: volají o pomoc nadměrnému SELECT *, neaktuálním statistikám a lenivým indexům. Hvězdičku vyměň za seznam sloupců, přidej SEEK‑friendly index, aktualizuj statistiky a udej přísný TOP N. Pak můžeš sledovat, jak se graf průměrné latence noří z červené do zelené – ještě než se klient stihne nadechnout ke stížnosti.


Klíčová slova

query timeout, mssql slow query, select star, top clause, query optimize, index seek, exec plan, perf tuning, sql tips, t-sql best, database dev, dba

↥ Zpět nahoru

Sdílejte na sítích

Webseidon
Webseidon
Articles: 32