เหตุใดการใช้ตัวแปรตารางจึงเร็วกว่าตาราง #temp มากกว่าสองเท่าในกรณีเฉพาะนี้


37

ฉันอ่านบทความที่นี่ Temporary Tables vs. Variables Table และผลกระทบต่อประสิทธิภาพของ SQL Serverและ SQL Server 2008 ก็สามารถสร้างผลลัพธ์ที่คล้ายกันกับผลลัพธ์ที่แสดงในปี 2005

เมื่อดำเนินการตามขั้นตอนที่เก็บไว้ (คำจำกัดความด้านล่าง) โดยมีเพียง 10 แถวรุ่นตัวแปรตารางจะดำเนินการเวอร์ชันตารางชั่วคราวมากกว่าสองครั้ง

ฉันเคลียร์แคชโพรซีเดอร์และรันทั้งโพรซีเดอร์ที่เก็บไว้ 10,000 ครั้งจากนั้นทำซ้ำกระบวนการสำหรับการรันอีก 4 ครั้ง ผลลัพธ์ด้านล่าง (เวลาเป็น ms ต่อชุด)

T2_Time     V2_Time
----------- -----------
8578        2718      
6641        2781    
6469        2813   
6766        2797
6156        2719

คำถามของฉันคือเหตุผลที่ทำให้ประสิทธิภาพที่ดีขึ้นของรุ่นตัวแปรตารางคืออะไร?

ฉันได้ทำการสอบสวนแล้ว เช่นการดูเคาน์เตอร์วัดประสิทธิภาพด้วย

SELECT cntr_value
from sys.dm_os_performance_counters
where counter_name = 'Temp Tables Creation Rate';

ยืนยันว่าในทั้งสองกรณีวัตถุชั่วคราวจะถูกแคชหลังจากเรียกใช้ครั้งแรกตามที่คาดไว้แทนที่จะสร้างตั้งแต่เริ่มต้นอีกครั้งสำหรับการเรียกใช้ทุกครั้ง

ในทำนองเดียวกันการติดตามAuto Stats, SP:Recompile, SQL:StmtRecompileเหตุการณ์ใน Profiler (ภาพด้านล่าง) แสดงให้เห็นว่าเหตุการณ์เหล่านี้เกิดขึ้นเพียงครั้งเดียว (ในการภาวนาแรกของ#tempขั้นตอนการจัดเก็บไว้ในตาราง) และอื่น ๆ 9,999 ประหารชีวิตไม่ได้เพิ่มใด ๆ ของเหตุการณ์เหล่านี้ (รุ่นตัวแปรตารางไม่ได้รับเหตุการณ์เหล่านี้)

ติดตาม

ค่าใช้จ่ายที่สูงกว่าเล็กน้อยของการเรียกใช้ครั้งแรกของกระบวนงานที่เก็บไว้ไม่สามารถอธิบายถึงความแตกต่างโดยรวมขนาดใหญ่ได้อย่างไรก็ตามเนื่องจากยังใช้เวลาเพียงไม่กี่มิลลิวินาทีในการล้างแคชขั้นตอนและเรียกใช้กระบวนการทั้งสองครั้ง การคอมไพล์ซ้ำอาจเป็นสาเหตุได้

สร้างวัตถุฐานข้อมูลที่ต้องการ

CREATE DATABASE TESTDB_18Feb2012;

GO

USE TESTDB_18Feb2012;

CREATE TABLE NUM 
  ( 
     n INT PRIMARY KEY, 
     s VARCHAR(128) 
  ); 

WITH NUMS(N) 
     AS (SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY $/0) 
         FROM   master..spt_values v1, 
                master..spt_values v2) 
INSERT INTO NUM 
SELECT N, 
       'Value: ' + CONVERT(VARCHAR, N) 
FROM   NUMS 

GO

CREATE PROCEDURE [dbo].[T2] @total INT 
AS 
  CREATE TABLE #T 
    ( 
       n INT PRIMARY KEY, 
       s VARCHAR(128) 
    ) 

  INSERT INTO #T 
  SELECT n, 
         s 
  FROM   NUM 
  WHERE  n%100 > 0 
         AND n <= @total 

  DECLARE @res VARCHAR(128) 

  SELECT @res = MAX(s) 
  FROM   NUM 
  WHERE  n <= @total 
         AND NOT EXISTS(SELECT * 
                        FROM   #T 
                        WHERE  #T.n = NUM.n) 
GO

CREATE PROCEDURE [dbo].[V2] @total INT 
AS 
  DECLARE @V TABLE ( 
    n INT PRIMARY KEY, 
    s VARCHAR(128)) 

  INSERT INTO @V 
  SELECT n, 
         s 
  FROM   NUM 
  WHERE  n%100 > 0 
         AND n <= @total 

  DECLARE @res VARCHAR(128) 

  SELECT @res = MAX(s) 
  FROM   NUM 
  WHERE  n <= @total 
         AND NOT EXISTS(SELECT * 
                        FROM   @V V 
                        WHERE  V.n = NUM.n) 


GO

สคริปต์ทดสอบ

SET NOCOUNT ON;

DECLARE @T1 DATETIME2,
        @T2 DATETIME2,
        @T3 DATETIME2,  
        @Counter INT = 0

SET @T1 = SYSDATETIME()

WHILE ( @Counter < 10000)
BEGIN
EXEC dbo.T2 10
SET @Counter += 1
END

SET @T2 = SYSDATETIME()
SET @Counter = 0

WHILE ( @Counter < 10000)
BEGIN
EXEC dbo.V2 10
SET @Counter += 1
END

SET @T3 = SYSDATETIME()

SELECT DATEDIFF(MILLISECOND,@T1,@T2) AS T2_Time,
       DATEDIFF(MILLISECOND,@T2,@T3) AS V2_Time

การติดตาม profiler บ่งชี้ว่าสถิติถูกสร้างขึ้นบน#tempตารางเพียงครั้งเดียวแม้ว่าจะได้รับการเคลียร์แล้วและมีการเติมข้อมูลอีกครั้ง 9,999 ครั้งหลังจากนั้น
Martin Smith

คำตอบ:


31

ผลลัพธ์ของSET STATISTICS IO ONทั้งคู่ดูเหมือนกัน

SET STATISTICS IO ON;
PRINT 'V2'
EXEC dbo.V2 10
PRINT 'T2'
EXEC dbo.T2 10

จะช่วยให้

V2
Table '#58B62A60'. Scan count 0, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

Table '#58B62A60'. Scan count 10, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

T2
Table '#T__ ... __00000000E2FE'. Scan count 0, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

Table '#T__ ... __00000000E2FE'. Scan count 0, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

และในขณะที่แอรอนชี้ให้เห็นในความคิดเห็นแผนสำหรับรุ่นตัวแปรตารางที่เป็นจริงที่มีประสิทธิภาพน้อยลงในขณะที่ทั้งสองมีลูปซ้อนกันวางแผนการขับเคลื่อนโดยดัชนีแสวงหาในดำเนินการรุ่นตารางแสวงหาเข้าไปดัชนีบนกับกริยาที่เหลือในขณะที่ตัวแปรตาราง เวอร์ชันทำการค้นหาดัชนีโดยใช้เพรดิเคตที่เหลือและประมวลผลแถวเพิ่มเติม (ซึ่งเป็นสาเหตุที่แผนนี้ทำงานได้ไม่ดีสำหรับจำนวนแถวที่มากขึ้น)dbo.NUM#temp[#T].n = [dbo].[NUM].[n][#T].[n]<=[@total]@V.n <= [@total]@V.[n]=[dbo].[NUM].[n]

การใช้Extended Eventsเพื่อดูประเภทการรอสำหรับ spid ที่ระบุจะให้ผลลัพธ์เหล่านี้สำหรับการประมวลผล 10,000 ครั้งEXEC dbo.T2 10

+---------------------+------------+----------------+----------------+----------------+
|                     |            |     Total      | Total Resource |  Total Signal  |
| Wait Type           | Wait Count | Wait Time (ms) | Wait Time (ms) | Wait Time (ms) |
+---------------------+------------+----------------+----------------+----------------+
| SOS_SCHEDULER_YIELD | 16         | 19             | 19             | 0              |
| PAGELATCH_SH        | 39998      | 14             | 0              | 14             |
| PAGELATCH_EX        | 1          | 0              | 0              | 0              |
+---------------------+------------+----------------+----------------+----------------+

และผลลัพธ์เหล่านี้มีการประหารชีวิต 10,000 ครั้ง EXEC dbo.V2 10

+---------------------+------------+----------------+----------------+----------------+
|                     |            |     Total      | Total Resource |  Total Signal  |
| Wait Type           | Wait Count | Wait Time (ms) | Wait Time (ms) | Wait Time (ms) |
+---------------------+------------+----------------+----------------+----------------+
| PAGELATCH_EX        | 2          | 0              | 0              | 0              |
| PAGELATCH_SH        | 1          | 0              | 0              | 0              |
| SOS_SCHEDULER_YIELD | 676        | 0              | 0              | 0              |
+---------------------+------------+----------------+----------------+----------------+

ดังนั้นจึงเป็นที่ชัดเจนว่าจำนวนการPAGELATCH_SHรอจะสูงขึ้นมากใน#tempกรณีตาราง ฉันไม่ได้ตระหนักถึงวิธีการเพิ่มทรัพยากรการรอลงในการติดตามเหตุการณ์เพิ่มเติมเพื่อตรวจสอบเรื่องนี้ต่อไปฉันวิ่ง

WHILE 1=1
EXEC dbo.T2 10

ขณะที่อยู่ในหน่วยเลือกตั้งอื่น ๆ sys.dm_os_waiting_tasks

CREATE TABLE #T(resource_description NVARCHAR(2048))

WHILE 1=1
INSERT INTO #T
SELECT resource_description
FROM sys.dm_os_waiting_tasks
WHERE session_id=<spid_of_other_session> and wait_type='PAGELATCH_SH'

หลังจากออกจากที่ทำงานประมาณ 15 วินาทีมันได้รวบรวมผลลัพธ์ต่อไปนี้

+-------+----------------------+
| Count | resource_description |
+-------+----------------------+
|  1098 | 2:1:150              |
|  1689 | 2:1:146              |
+-------+----------------------+

ทั้งสองหน้าเว็บเหล่านี้ถูกยึดเป็นของ (แตกต่างกัน) ดัชนีที่ไม่ใช่คลัสเตอร์ในtempdb.sys.sysschobjsตารางฐานชื่อและ'nc1''nc2'

การสอบถามtempdb.sys.fn_dblogในระหว่างการทดสอบแสดงว่าจำนวนของบันทึกการทำงานที่เพิ่มขึ้นจากการประมวลผลครั้งแรกของแต่ละกระบวนงานที่เก็บไว้นั้นค่อนข้างผันแปร แต่สำหรับการประมวลผลครั้งต่อมาหมายเลขที่เพิ่มโดยการทำซ้ำแต่ละครั้งนั้นมีความสอดคล้องกันมาก เมื่อแผนขั้นตอนถูกแคชจำนวนรายการบันทึกจะประมาณครึ่งหนึ่งที่จำเป็นสำหรับ#tempรุ่น

+-----------------+----------------+------------+
|                 | Table Variable | Temp Table |
+-----------------+----------------+------------+
| First Run       |            126 | 72 or 136  |
| Subsequent Runs |             17 | 32         |
+-----------------+----------------+------------+

การดูรายการบันทึกธุรกรรมโดยละเอียดเพิ่มเติมสำหรับ#tempเวอร์ชันตารางของ SP แต่ละการเรียกใช้โพรซีเดอร์ที่เก็บไว้ในภายหลังจะสร้างธุรกรรมสามรายการและตัวแปรตารางหนึ่งเพียงสองรายการเท่านั้น

+---------------------------------+----+---------------------------------+----+
|           #Temp Table                |         @Table Variable              |
+---------------------------------+----+---------------------------------+----+
| CREATE TABLE                    |  9 |                                 |    |
| INSERT                          | 12 | TVQuery                         | 12 |
| FCheckAndCleanupCachedTempTable | 11 | FCheckAndCleanupCachedTempTable |  5 |
+---------------------------------+----+---------------------------------+----+

กระบวนการINSERT/ TVQUERYธุรกรรมเหมือนกันยกเว้นชื่อ สิ่งนี้มีเร็กคอร์ดบันทึกการทำงานสำหรับแต่ละ 10 แถวที่แทรกไปยังตารางชั่วคราวหรือตัวแปรตารางพร้อมกับรายการLOP_BEGIN_XACT/LOP_COMMIT_XACT

CREATE TABLEการทำธุรกรรมจะปรากฏเฉพาะใน#Tempรุ่นและมีลักษณะดังต่อไปนี้

+-----------------+-------------------+---------------------+
|    Operation    |      Context      |    AllocUnitName    |
+-----------------+-------------------+---------------------+
| LOP_BEGIN_XACT  | LCX_NULL          |                     |
| LOP_SHRINK_NOOP | LCX_NULL          |                     |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc1  |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc1  |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc2  |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc2  |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst |
| LOP_COMMIT_XACT | LCX_NULL          |                     |
+-----------------+-------------------+---------------------+

FCheckAndCleanupCachedTempTableการทำธุรกรรมที่ปรากฏอยู่ในทั้งสอง แต่มี 6 รายการเพิ่มเติมใน#tempรุ่น นี่คือแถว 6 แถวที่อ้างถึงsys.sysschobjsและมีรูปแบบเหมือนกันกับด้านบน

+-----------------+-------------------+----------------------------------------------+
|    Operation    |      Context      |                AllocUnitName                 |
+-----------------+-------------------+----------------------------------------------+
| LOP_BEGIN_XACT  | LCX_NULL          |                                              |
| LOP_DELETE_ROWS | LCX_NONSYS_SPLIT  | dbo.#7240F239.PK__#T________3BD0199374293AAB |
| LOP_HOBT_DELTA  | LCX_NULL          |                                              |
| LOP_HOBT_DELTA  | LCX_NULL          |                                              |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst                          |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc1                           |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc1                           |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc2                           |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc2                           |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst                          |
| LOP_COMMIT_XACT | LCX_NULL          |                                              |
+-----------------+-------------------+----------------------------------------------+

เมื่อดูที่ 6 แถวเหล่านี้ในธุรกรรมทั้งคู่ เป็นครั้งแรกที่LOP_MODIFY_ROW, LCX_CLUSTEREDมีการปรับปรุงไปในคอลัมน์modify_date sys.objectsส่วนที่เหลืออีกห้าแถวเกี่ยวข้องกับการเปลี่ยนชื่อวัตถุ เนื่องจากnameเป็นคอลัมน์สำคัญของทั้ง NCIs ที่ได้รับผลกระทบ ( nc1และnc2) สิ่งนี้ถูกดำเนินการเป็นการลบ / แทรกสำหรับสิ่งเหล่านั้นจากนั้นจะกลับไปที่ดัชนีคลัสเตอร์และการปรับปรุงด้วยเช่นกัน

ปรากฏว่าสำหรับ#tempรุ่นตารางเมื่อขั้นตอนการจัดเก็บสิ้นสุดส่วนหนึ่งของการทำความสะอาดที่ดำเนินการโดยการFCheckAndCleanupCachedTempTableทำธุรกรรมคือการเปลี่ยนชื่อตาราง temp จากสิ่งที่ต้องการ#T__________________________________________________________________________________________________________________00000000E316เป็นชื่อภายในที่แตกต่างกันเช่น#2F4A0079และเมื่อมีการป้อนCREATE TABLEธุรกรรมเปลี่ยนชื่อมันกลับมา ชื่อฟลิปพลิกนี้สามารถเห็นได้ในการเชื่อมต่อหนึ่งดำเนินการdbo.T2ในวงขณะที่ในอีก

WHILE 1=1
SELECT name, object_id, create_date, modify_date
FROM tempdb.sys.objects 
WHERE name LIKE '#%'

ตัวอย่างผลลัพธ์

ภาพหน้าจอ

ดังนั้นคำอธิบายหนึ่งที่เป็นไปได้สำหรับผลการปฏิบัติงานที่สังเกตได้ตามที่อเล็กซ์กล่าวคือมันเป็นงานเพิ่มเติมที่รักษาตารางระบบในtempdbที่รับผิดชอบ


การรันทั้งสองโพรซีเดอร์ในลูปนั้น Visual Studio Code profiler จะแสดงรายการต่อไปนี้

+-------------------------------+--------------------+-------+-----------+
|           Function            |    Explanation     | Temp  | Table Var |
+-------------------------------+--------------------+-------+-----------+
| CXStmtDML::XretExecute        | Insert ... Select  | 16.93 | 37.31     |
| CXStmtQuery::ErsqExecuteQuery | Select Max         | 8.77  | 23.19     |
+-------------------------------+--------------------+-------+-----------+
| Total                         |                    | 25.7  | 60.5      |
+-------------------------------+--------------------+-------+-----------+

รุ่นตัวแปรตารางใช้เวลาประมาณ 60% ของเวลาในการดำเนินการคำสั่งแทรกและการเลือกที่ตามมาในขณะที่ตารางชั่วคราวน้อยกว่าครึ่ง สิ่งนี้สอดคล้องกับการกำหนดเวลาที่แสดงใน OP และด้วยข้อสรุปข้างต้นว่าความแตกต่างของประสิทธิภาพการทำงานลดลงตามเวลาที่ใช้ในการทำงานเสริมไม่ได้เกิดจากเวลาที่ใช้ในการประมวลผลแบบสอบถาม

ฟังก์ชั่นที่สำคัญที่สุดที่มีผลต่อ "หายไป" 75% ในรุ่นตารางชั่วคราวคือ

+------------------------------------+-------------------+
|              Function              | Inclusive Samples |
+------------------------------------+-------------------+
| CXStmtCreateTableDDL::XretExecute  | 26.26%            |
| CXStmtDDL::FinishNormalImp         | 4.17%             |
| TmpObject::Release                 | 27.77%            |
+------------------------------------+-------------------+
| Total                              | 58.20%            |
+------------------------------------+-------------------+

ภายใต้ทั้งสร้างและปล่อยฟังก์ชั่นการทำงานจะแสดงด้วยค่าตัวอย่างรวมCMEDProxyObject::SetName 19.6%จากที่ฉันสรุปว่า 39.2% ของเวลาในกรณีตารางชั่วคราวถูกนำขึ้นกับการเปลี่ยนชื่ออธิบายไว้ก่อนหน้านี้

และตัวที่ใหญ่ที่สุดในตารางตัวแปรรุ่นที่เอื้อต่อการอีก 40% คือ

+-----------------------------------+-------------------+
|             Function              | Inclusive Samples |
+-----------------------------------+-------------------+
| CTableCreate::LCreate             | 7.41%             |
| TmpObject::Release                | 12.87%            |
+-----------------------------------+-------------------+
| Total                             | 20.28%            |
+-----------------------------------+-------------------+

โปรไฟล์ตารางชั่วคราว

ป้อนคำอธิบายรูปภาพที่นี่

โปรไฟล์ตัวแปรตาราง

ป้อนคำอธิบายรูปภาพที่นี่


10

ดิสโก้ไฟนรก

เนื่องจากนี่เป็นคำถามที่เก่ากว่าฉันตัดสินใจที่จะทบทวนปัญหาใน SQL Server รุ่นที่ใหม่กว่าเพื่อดูว่ายังคงมีโปรไฟล์ประสิทธิภาพเดียวกันหรือไม่หรือมีการเปลี่ยนแปลงคุณลักษณะเลย

โดยเฉพาะการเพิ่มตารางระบบในหน่วยความจำสำหรับ SQL Server 2019ดูเหมือนจะเป็นโอกาสที่คุ้มค่าสำหรับการทดสอบอีกครั้ง

ฉันใช้สายรัดทดสอบที่ต่างออกไปเล็กน้อยเนื่องจากฉันพบปัญหานี้ในขณะที่ทำงานอย่างอื่น

การทดสอบการทดสอบ

การใช้Stack Overflow เวอร์ชัน 2013ฉันมีดัชนีนี้และโพรซีเดอร์ทั้งสองนี้:

ดัชนี:

CREATE INDEX ix_whatever 
    ON dbo.Posts(OwnerUserId) INCLUDE(Score);
GO

ตารางชั่วคราว:

    CREATE OR ALTER PROCEDURE dbo.TempTableTest(@Id INT)
    AS
    BEGIN
    SET NOCOUNT ON;

        CREATE TABLE #t(i INT NOT NULL);
        DECLARE @i INT;

        INSERT #t ( i )
        SELECT p.Score
        FROM dbo.Posts AS p
        WHERE p.OwnerUserId = @Id;

        SELECT @i = AVG(t.i)
        FROM #t AS t;

    END;
    GO 

ตัวแปรตาราง:

    CREATE OR ALTER PROCEDURE dbo.TableVariableTest(@Id INT)
    AS
    BEGIN
    SET NOCOUNT ON;

        DECLARE @t TABLE (i INT NOT NULL);
        DECLARE @i INT;

        INSERT @t ( i )
        SELECT p.Score
        FROM dbo.Posts AS p
        WHERE p.OwnerUserId = @Id;

        SELECT @i = AVG(t.i)
        FROM @t AS t;

    END;
    GO 

เพื่อป้องกันASYNC_NETWORK_IO ที่อาจเกิดขึ้นรอฉันใช้ขั้นตอนการหุ้ม

CREATE PROCEDURE #TT AS
SET NOCOUNT ON;
    DECLARE @i INT = 1;
    DECLARE @StartDate DATETIME2(7) = SYSDATETIME();

    WHILE @i <= 50000
        BEGIN
            EXEC dbo.TempTableTest @Id = @i;
            SET @i += 1;
        END;
    SELECT DATEDIFF(MILLISECOND, @StartDate, SYSDATETIME()) AS [ElapsedTimeMilliseconds];
GO

CREATE PROCEDURE #TV AS
SET NOCOUNT ON;
    DECLARE @i INT = 1;
    DECLARE @StartDate DATETIME2(7) = SYSDATETIME();

    WHILE @i <= 50000
        BEGIN
            EXEC dbo.TableVariableTest @Id = @i;
            SET @i += 1;
        END;
    SELECT DATEDIFF(MILLISECOND, @StartDate, SYSDATETIME()) AS [ElapsedTimeMilliseconds];
GO

SQL Server 2017

ตั้งแต่ปี 2014 และ 2016 มีพื้นธาตุที่จุดนี้ฉันเริ่มการทดสอบของฉันกับปี 2017 นอกจากนี้สำหรับความกะทัดรัดผมกระโดดไปทางขวาเพื่อ profiling รหัสกับPerfview ในชีวิตจริงฉันมองไปที่รอ, สลัก, สปิลล็อค, ธงติดตามบ้าและสิ่งอื่น ๆ

การทำโปรไฟล์รหัสเป็นสิ่งเดียวที่เปิดเผยสิ่งที่น่าสนใจ

เวลาต่างกัน:

  • ตารางชั่วคราว: 17891 ms
  • ตัวแปรตาราง: 5891 ms

ยังแตกต่างอย่างชัดเจนใช่มั้ย แต่ SQL Server กำลังกดปุ่มอะไรอยู่ตอนนี้

ถั่ว

เมื่อดูที่การเพิ่มขึ้นสองอันดับแรกในตัวอย่างที่แตกต่างกันเราจะเห็นsqlminและsqlsqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucketเป็นผู้กระทำผิดที่ใหญ่ที่สุดสองคน

ถั่ว

เมื่อพิจารณาจากชื่อในสแตกการโทรการล้างและการเปลี่ยนชื่อตาราง temp ภายในเป็นเวลาที่ใหญ่ที่สุดในการเรียกใช้ตาราง temp เทียบกับการเรียกตัวแปรตาราง

แม้ว่าตัวแปรตารางจะได้รับการสนับสนุนจากตาราง temp แต่มันก็ไม่ได้เป็นปัญหา

SET STATISTICS IO ON;
DECLARE @t TABLE(id INT);
SELECT * FROM @t AS t;

ตาราง '# B98CE339' สแกนจำนวน 1

การดูสายการโทรสำหรับการทดสอบตัวแปรตารางไม่แสดงผู้กระทำผิดหลักอย่างใดอย่างหนึ่ง:

ถั่ว

SQL Server 2019 (วานิลลา)

เอาล่ะดังนั้นนี่ยังคงเป็นปัญหาใน SQL Server 2017 มีอะไรแตกต่างในปี 2019 หรือไม่

ก่อนอื่นเพื่อแสดงให้เห็นว่าไม่มีอะไรติดแขนของฉัน:

SELECT c.name,
       c.value_in_use,
       c.description
FROM sys.configurations AS c
WHERE c.name = 'tempdb metadata memory-optimized';

ถั่ว

เวลาต่างกัน:

  • ตารางชั่วคราว: 15765 ms
  • ตัวแปรตาราง: 7250 ms

ขั้นตอนทั้งสองนั้นแตกต่างกัน การเรียกใช้ temp table เร็วขึ้นสองสามวินาทีและการเรียกตัวแปร table นั้นช้ากว่าประมาณ 1.5 วินาที ตัวแปรตารางช้าลงอาจมีการอธิบายบางส่วนโดยการรวบรวมตัวแปรตารางเลื่อนเวลาซึ่งเป็นตัวเลือกเพิ่มประสิทธิภาพใหม่ในปี 2019

เมื่อมองถึงความแตกต่างใน Perfview มันได้เปลี่ยนบิต - sqlmin ไม่มีอีกต่อไป - แต่sqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucketเป็น

ถั่ว

SQL Server 2019 (ตารางระบบ In-Memory Tempdb)

สิ่งนี้เกี่ยวกับสิ่งใหม่ในสิ่งที่ตารางระบบหน่วยความจำ? หืม? จีบด้วยไหม?

มาเปิดกัน!

EXEC sys.sp_configure @configname = 'advanced', 
                      @configvalue = 1  
RECONFIGURE;

EXEC sys.sp_configure @configname = 'tempdb metadata memory-optimized', 
                      @configvalue = 1 
RECONFIGURE;

โปรดทราบว่านี่ต้องใช้ SQL Server เพื่อรีสตาร์ทเพื่อให้อภัยฉันในขณะที่ฉันรีบูต SQL ในบ่ายวันศุกร์ที่น่ารักนี้

ตอนนี้ทุกอย่างดูแตกต่าง:

SELECT c.name,
       c.value_in_use,
       c.description
FROM sys.configurations AS c
WHERE c.name = 'tempdb metadata memory-optimized';

SELECT *, 
       OBJECT_NAME(object_id) AS object_name, 
       @@VERSION AS sql_server_version
FROM tempdb.sys.memory_optimized_tables_internal_attributes;

ถั่ว

เวลาต่างกัน:

  • ตารางชั่วคราว: 11638 ms
  • ตัวแปรตาราง: 7403 ms

ตารางชั่วคราวทำได้ดีกว่าประมาณ 4 วินาที! นั่นคือสิ่งที่

ฉันชอบบางสิ่ง

เวลานี้ความแตกต่างของ Perfview ไม่น่าสนใจมาก เคียงข้างกันมันเป็นเรื่องที่น่าสนใจที่จะสังเกตว่าเวลาใกล้เคียงกันมากแค่ไหน:

ถั่ว

สิ่งหนึ่งที่น่าสนใจใน diff คือการเรียกไปhkengine!ที่ซึ่งอาจดูเหมือนชัดเจนเนื่องจาก hekaton-ish กำลังใช้งานอยู่

ถั่ว

เท่าที่สองรายการแรกในต่างฉันไม่สามารถทำมากntoskrnl!?:

ถั่ว

หรือsqltses!CSqlSortManager_80::GetSortKeyแต่พวกเขาอยู่ที่นี่เพื่อ Smrtr Ppl ™เพื่อดู:

ถั่ว

โปรดทราบว่ามีเอกสารที่ไม่มีเอกสารและไม่ปลอดภัยสำหรับการผลิตดังนั้นโปรดอย่าใช้การตั้งค่าสถานะเริ่มต้นการติดตามคุณสามารถใช้เพื่อให้มีวัตถุระบบตาราง temp เพิ่มเติม (sysrowsets, sysallocunits และ sysseobjvalues) รวมอยู่ในคุณสมบัติในหน่วยความจำ ไม่ได้สร้างความแตกต่างที่เห็นได้ชัดเจนในเวลาดำเนินการในกรณีนี้

Roundup

แม้ในเซิร์ฟเวอร์ SQL เวอร์ชันใหม่กว่าการเรียกใช้ตัวแปรความถี่สูงไปยังตารางจะเร็วกว่าการเรียกความถี่สูงไปยังตารางชั่วคราว

ถึงแม้ว่ามันจะดึงดูดการตำหนิการคอมไพล์, การคอมไพล์ใหม่, สถิติอัตโนมัติ, สลัก, สปิลล็อค, แคชหรือปัญหาอื่น ๆ ปัญหานี้ยังคงชัดเจนเกี่ยวกับการจัดการการล้างข้อมูลตารางชั่วคราว

เป็นการเรียกที่ใกล้ยิ่งขึ้นใน SQL Server 2019 ด้วยการเปิดใช้ตารางระบบในหน่วยความจำ แต่ตัวแปรตารางยังทำงานได้ดีขึ้นเมื่อความถี่การโทรสูง

แน่นอนว่าเป็นนักปราชญ์ที่มีชื่อเสียงคนหนึ่งเคยรำพึง: "ใช้ตัวแปรตารางเมื่อตัวเลือกแผนไม่ใช่ปัญหา"


ดี - ขอโทษฉันพลาดที่คุณได้เพิ่มคำตอบนี้จนกระทั่งเพียงแค่ติดตามลิงก์ในโพสต์บล็อก "ดีบั๊ก"
Martin Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.