ผลลัพธ์ของ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% |
+-----------------------------------+-------------------+
โปรไฟล์ตารางชั่วคราว
โปรไฟล์ตัวแปรตาราง
#temp
ตารางเพียงครั้งเดียวแม้ว่าจะได้รับการเคลียร์แล้วและมีการเติมข้อมูลอีกครั้ง 9,999 ครั้งหลังจากนั้น