Sedm tříd implementace řazení SQL Server je:
- CQScanSortNew
- CQScanTopSortNew
- CQScanIndexSortNew
- CQScanPartitionSortNew (pouze SQL Server 2014)
- CQScanInMemSortNew
- In-Memory OLTP (Hekaton) nativně kompilovaná procedura Top N Sort (pouze SQL Server 2014)
- In-Memory OLTP (Hekaton) nativně kompilovaná procedura General Sort (pouze SQL Server 2014)
První čtyři typy byly popsány v první části tohoto článku.
5. CQScanInMemSortNew
Tato třída třída má řadu zajímavých funkcí, některé z nich jsou jedinečné:
- Jak název napovídá, vždy se třídí zcela v paměti; nikdy se nerozlije do tempdb
- Řazení se vždy provádí pomocí quicksort qsort_s ve standardní C runtime knihovně MSVCR100
- Může provádět všechny tři logické typy řazení:Obecné, Nejlepší N a Odlišné řazení
- Lze jej použít pro klastrovaná měkká řazení pro jednotlivé oddíly (viz část 4 v části 1)
- Paměť, kterou používá, může být uložena do mezipaměti s plánem, místo aby byla rezervována těsně před provedením
- V prováděcích plánech jej lze identifikovat jako řazení v paměti
- Lze seřadit maximálně 500 hodnot
- Nikdy se nepoužívá pro druhy vytváření indexů (viz část 3 v části 1)
CQScanInMemSortNew je třída, se kterou se často nesetkáte. Vzhledem k tomu, že vždy třídí v paměti pomocí standardního algoritmu rychlého třídění v knihovně, nebyla by to dobrá volba pro obecné úlohy třídění databází. Ve skutečnosti se tato třída třída používá pouze tehdy, když jsou všechny její vstupy běhové konstanty (včetně odkazů @proměnných). Z pohledu prováděcího plánu to znamená, že vstupem pro operátor Sort musí být Konstantní skenování operátor, jak ukazují příklady níže:
-- Regular Sort on system scalar functions SELECT X.i FROM ( SELECT @@TIMETICKS UNION ALL SELECT @@TOTAL_ERRORS UNION ALL SELECT @@TOTAL_READ UNION ALL SELECT @@TOTAL_WRITE ) AS X (i) ORDER BY X.i; -- Distinct Sort on constant literals WITH X (i) AS ( SELECT 3 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 2 ) SELECT DISTINCT X.i FROM X ORDER BY X.i; -- Top N Sort on variables, constants, and functions DECLARE @x integer = 1, @y integer = 2; SELECT TOP (1) X.i FROM ( VALUES (@x), (@y), (123), (@@CONNECTIONS) ) AS X (i) ORDER BY X.i;
Prováděcí plány jsou:
Typický zásobník volání během třídění je uveden níže. Všimněte si volání qsort_s v knihovně MSVCR100:
Všechny tři výše uvedené prováděcí plány jsou tříděny v paměti pomocí CQScanInMemSortNew se vstupy dostatečně malými na to, aby třídicí paměť byla uložena do mezipaměti. Tyto informace nejsou ve výchozím nastavení v plánech provádění vystaveny, ale lze je odhalit pomocí nezdokumentovaného příznaku trasování 8666. Když je tento příznak aktivní, zobrazí se operátoru řazení další vlastnosti:
Mezipaměť je v tomto příkladu omezena na 62 řádků, jak je ukázáno níže:
-- Cache buffer limited to 62 rows SELECT X.i FROM ( VALUES (001),(002),(003),(004),(005),(006),(007),(008),(009),(010), (011),(012),(013),(014),(015),(016),(017),(018),(019),(020), (021),(022),(023),(024),(025),(026),(027),(028),(029),(030), (031),(032),(033),(034),(035),(036),(037),(038),(039),(040), (041),(042),(043),(044),(045),(046),(047),(048),(049),(050), (051),(052),(053),(054),(055),(056),(057),(058),(059),(060), (061),(062)--, (063) ) AS X (i) ORDER BY X.i;
Odkomentujte poslední položku v tomto skriptu, abyste viděli změnu vlastnosti vyrovnávací paměti pro řazení z 1 na 0:
Když není vyrovnávací paměť uložena do mezipaměti, musí řazení v paměti alokovat paměť při inicializaci a podle potřeby při čtení řádků ze svého vstupu. Když lze použít vyrovnávací paměť uloženou v mezipaměti, tato práce s alokací paměti se vyhne.
Následující skript lze použít k prokázání, že maximální počet položek pro CQScanInMemSortNew rychlé třídění v paměti je 500:
SELECT X.i FROM ( VALUES (001),(002),(003),(004),(005),(006),(007),(008),(009),(010), (011),(012),(013),(014),(015),(016),(017),(018),(019),(020), (021),(022),(023),(024),(025),(026),(027),(028),(029),(030), (031),(032),(033),(034),(035),(036),(037),(038),(039),(040), (041),(042),(043),(044),(045),(046),(047),(048),(049),(050), (051),(052),(053),(054),(055),(056),(057),(058),(059),(060), (061),(062),(063),(064),(065),(066),(067),(068),(069),(070), (071),(072),(073),(074),(075),(076),(077),(078),(079),(080), (081),(082),(083),(084),(085),(086),(087),(088),(089),(090), (091),(092),(093),(094),(095),(096),(097),(098),(099),(100), (101),(102),(103),(104),(105),(106),(107),(108),(109),(110), (111),(112),(113),(114),(115),(116),(117),(118),(119),(120), (121),(122),(123),(124),(125),(126),(127),(128),(129),(130), (131),(132),(133),(134),(135),(136),(137),(138),(139),(140), (141),(142),(143),(144),(145),(146),(147),(148),(149),(150), (151),(152),(153),(154),(155),(156),(157),(158),(159),(160), (161),(162),(163),(164),(165),(166),(167),(168),(169),(170), (171),(172),(173),(174),(175),(176),(177),(178),(179),(180), (181),(182),(183),(184),(185),(186),(187),(188),(189),(190), (191),(192),(193),(194),(195),(196),(197),(198),(199),(200), (201),(202),(203),(204),(205),(206),(207),(208),(209),(210), (211),(212),(213),(214),(215),(216),(217),(218),(219),(220), (221),(222),(223),(224),(225),(226),(227),(228),(229),(230), (231),(232),(233),(234),(235),(236),(237),(238),(239),(240), (241),(242),(243),(244),(245),(246),(247),(248),(249),(250), (251),(252),(253),(254),(255),(256),(257),(258),(259),(260), (261),(262),(263),(264),(265),(266),(267),(268),(269),(270), (271),(272),(273),(274),(275),(276),(277),(278),(279),(280), (281),(282),(283),(284),(285),(286),(287),(288),(289),(290), (291),(292),(293),(294),(295),(296),(297),(298),(299),(300), (301),(302),(303),(304),(305),(306),(307),(308),(309),(310), (311),(312),(313),(314),(315),(316),(317),(318),(319),(320), (321),(322),(323),(324),(325),(326),(327),(328),(329),(330), (331),(332),(333),(334),(335),(336),(337),(338),(339),(340), (341),(342),(343),(344),(345),(346),(347),(348),(349),(350), (351),(352),(353),(354),(355),(356),(357),(358),(359),(360), (361),(362),(363),(364),(365),(366),(367),(368),(369),(370), (371),(372),(373),(374),(375),(376),(377),(378),(379),(380), (381),(382),(383),(384),(385),(386),(387),(388),(389),(390), (391),(392),(393),(394),(395),(396),(397),(398),(399),(400), (401),(402),(403),(404),(405),(406),(407),(408),(409),(410), (411),(412),(413),(414),(415),(416),(417),(418),(419),(420), (421),(422),(423),(424),(425),(426),(427),(428),(429),(430), (431),(432),(433),(434),(435),(436),(437),(438),(439),(440), (441),(442),(443),(444),(445),(446),(447),(448),(449),(450), (451),(452),(453),(454),(455),(456),(457),(458),(459),(460), (461),(462),(463),(464),(465),(466),(467),(468),(469),(470), (471),(472),(473),(474),(475),(476),(477),(478),(479),(480), (481),(482),(483),(484),(485),(486),(487),(488),(489),(490), (491),(492),(493),(494),(495),(496),(497),(498),(499),(500) --, (501) ) AS X (i) ORDER BY X.i;
Znovu odkomentujte poslední položku, abyste viděli InMemory Seřadit změnu vlastnosti z 1 na 0. Když k tomu dojde, CQScanInMemSortNew je nahrazeno buď CQScanSortNew (viz část 1) nebo CQScanTopSortNew (sekce 2). NeCQScanInMemSortNew řazení lze samozřejmě stále provádět v paměti, jen používá jiný algoritmus a může se přelévat do tempdb v případě potřeby.
6. In-Memory OLTP nativně zkompilovaná uložená procedura Top N Sort
Současná implementace nativně kompilovaných uložených procedur In-Memory OLTP (dříve s kódovým označením Hekaton) používá frontu priority následovanou qsort_s pro Top N Sorts, pokud jsou splněny následující podmínky:
- Dotaz obsahuje TOP (N) s klauzulí ORDER BY
- Hodnota N je konstantní literál (nikoli proměnná)
- N má maximální hodnotu 8192; Ačkoli
- Přítomnost spojení nebo agregací může snížit hodnotu 8192, jak je zdokumentováno zde
Následující kód vytvoří tabulku Hekaton obsahující 4000 řádků:
CREATE DATABASE InMemoryOLTP; GO -- Add memory optimized filegroup ALTER DATABASE InMemoryOLTP ADD FILEGROUP InMemoryOLTPFileGroup CONTAINS MEMORY_OPTIMIZED_DATA; GO -- Add file (adjust path if necessary) ALTER DATABASE InMemoryOLTP ADD FILE ( NAME = N'IMOLTP', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.SQL2014\MSSQL\DATA\IMOLTP.hkf' ) TO FILEGROUP InMemoryOLTPFileGroup; GO USE InMemoryOLTP; GO CREATE TABLE dbo.Test ( col1 integer NOT NULL, col2 integer NOT NULL, col3 integer NOT NULL, CONSTRAINT PK_dbo_Test PRIMARY KEY NONCLUSTERED HASH (col1) WITH (BUCKET_COUNT = 8192) ) WITH ( MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY ); GO -- Add numbers from 1-4000 using -- Itzik Ben-Gan's number generator WITH L0 AS (SELECT 1 AS c UNION ALL SELECT 1), L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B), L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B), L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B), L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B), L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B), Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5) INSERT dbo.Test (col1, col2, col3) SELECT N.n, ABS(CHECKSUM(NEWID())), ABS(CHECKSUM(NEWID())) FROM Nums AS N WHERE N.n BETWEEN 1 AND 4000;
Další skript vytvoří vhodné řazení Top N v nativně zkompilované uložené proceduře:
-- Natively-compiled Top N Sort stored procedure CREATE PROCEDURE dbo.TestP WITH EXECUTE AS OWNER, SCHEMABINDING, NATIVE_COMPILATION AS BEGIN ATOMIC WITH ( TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english' ) SELECT TOP (2) T.col2 FROM dbo.Test AS T ORDER BY T.col2 END; GO EXECUTE dbo.TestP;
Odhadovaný plán provádění je:
Zásobník volání zachycený během provádění ukazuje probíhající vkládání do prioritní fronty:
Po dokončení sestavení prioritní fronty zobrazí další zásobník volání poslední průchod standardním rychlým řazením knihovny:
xtp_p_* knihovna zobrazená v těchto hromadách volání je nativně zkompilovaná knihovna dll pro uloženou proceduru se zdrojovým kódem uloženým v místní instanci serveru SQL Server. Zdrojový kód je automaticky generován z definice uložené procedury. Například soubor C pro tuto nativní uloženou proceduru obsahuje následující fragment:
To je tak blízko, jak se můžeme dostat k přístupu ke zdrojovému kódu SQL Server.
7. In-Memory OLTP nativně zkompilovaná uložená procedura Sort
Nativně kompilované procedury v současné době nepodporují Distinct Sort, ale je podporováno nerozlišující obecné řazení bez jakýchkoli omezení velikosti sady. Abychom demonstrovali, nejprve do testovací tabulky přidáme 6 000 řádků, takže celkem 10 000 řádků:
WITH L0 AS (SELECT 1 AS c UNION ALL SELECT 1), L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B), L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B), L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B), L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B), L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B), Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5) INSERT dbo.Test (col1, col2, col3) SELECT N.n, ABS(CHECKSUM(NEWID())), ABS(CHECKSUM(NEWID())) FROM Nums AS N WHERE N.n BETWEEN 4001 AND 10000;
Nyní můžeme zrušit předchozí testovací proceduru (nativně kompilované procedury nelze aktuálně měnit) a vytvořit novou, která provede běžné (nikoli top-n) řazení 10 000 řádků:
DROP PROCEDURE dbo.TestP; GO CREATE PROCEDURE dbo.TestP WITH EXECUTE AS OWNER, SCHEMABINDING, NATIVE_COMPILATION AS BEGIN ATOMIC WITH ( TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english' ) SELECT T.col2 FROM dbo.Test AS T ORDER BY T.col2 END; GO EXECUTE dbo.TestP;
Odhadovaný plán provádění je:
Sledování provádění tohoto řazení ukazuje, že začíná generováním několika malých setříděných běhů opět pomocí standardního rychlého třídění:
Jakmile je tento proces dokončen, setříděné běhy jsou sloučeny pomocí schématu prioritní fronty:
Zdrojový kód procedury v C opět ukazuje některé podrobnosti:
Shrnutí části 2
- CQScanInMemSortNew je vždy rychlé třídění v paměti. Je omezena na 500 řádků z konstantního skenování a může ukládat do mezipaměti třídicí paměť pro malé vstupy. Řazení lze identifikovat jako CQScanInMemSortNew řazení pomocí vlastností prováděcího plánu vystavených příznakem trasování 8666.
- Nativní zkompilované řazení podle Hekaton Top N vyžaduje konstantní doslovnou hodnotu pro N <=8192 a řadí se pomocí prioritní fronty následované standardním rychlým řazením
- Nativní kompilované obecné třídění Hekaton může třídit libovolný počet řádků pomocí rychlého třídění standardní knihovny ke generování běhů řazení a třídění slučováním podle priority fronty ke kombinování běhů. Nepodporuje Distinct Sort.