Tại sao XÓA lại để lại hiệu ứng kéo dài về hiệu suất?


20

Cuối cùng là một kịch bản thử nghiệm để so sánh hiệu suất giữa biến @table và bảng #temp. Tôi nghĩ rằng tôi đã thiết lập chính xác - thời gian thực hiện được thực hiện bên ngoài các lệnh XÓA / TRUNCATE. Kết quả mà tôi nhận được như sau (thời gian tính bằng mili giây).

@Table Variable  #Temp (delete)  #Temp (truncate)
---------------  --------------  ----------------
5723             5180            5506
15636            14746           7800
14506            14300           5583
14030            15460           5386
16706            16186           5360

Để chắc chắn rằng tôi lành mạnh, điều này cho thấy rằng CURRENT_TIMESTAMP (hay còn gọi là GetDate()) được thực hiện tại thời điểm phát biểu, không phải theo đợt, do đó, không nên có sự tương tác giữa TRUNCATE / DELETE với SET @StartTime = CURRENT_TIMESTAMPcâu lệnh.

select current_timestamp
waitfor delay '00:00:04'
select current_timestamp

-----------------------
2012-10-21 11:29:20.290

-----------------------
2012-10-21 11:29:24.290

Nó khá nhất quán trong bước nhảy giữa lần chạy đầu tiên và lần chạy tiếp theo khi XÓA được sử dụng để xóa bảng. Tôi còn thiếu điều gì trong sự hiểu biết của tôi về XÓA ? Tôi đã lặp lại điều này nhiều lần, hoán đổi thứ tự, kích thước tempdb để không yêu cầu tăng trưởng, v.v.

CREATE TABLE #values (
  id int identity primary key, -- will be clustered
  name varchar(100) null,
  number int null,
  type char(3) not null,
  low int null,
  high int null,
  status smallint not null
);
GO
SET NOCOUNT ON;

DECLARE @values TABLE (
  id int identity primary key clustered,
  name varchar(100) null,
  number int null,
  type char(3) not null,
  low int null,
  high int null,
  status smallint not null
);
DECLARE  @ExecutionTime  TABLE(      Duration bigINT    ) 
DECLARE  @StartTime DATETIME,  @i INT = 1; 
WHILE (@i <= 5) 
  BEGIN 
    DELETE @values;
    DBCC freeproccache With NO_InfoMSGS;
    DBCC DROPCLEANBUFFERS With NO_InfoMSGS;
    SET @StartTime = CURRENT_TIMESTAMP -- alternate getdate() 
    /****************** measured process ***********************/ 

    INSERT @values SELECT a.* FROM master..spt_values a join master..spt_values b on b.type='P' and b.number < 1000;

    /**************** end measured process *********************/ 
    INSERT @ExecutionTime 
    SELECT DurationInMilliseconds = datediff(ms,@StartTime,CURRENT_TIMESTAMP) 
    SET @i +=  1 
  END -- WHILE 

SELECT DurationInMilliseconds = Duration FROM   @ExecutionTime 
GO 

-- Temporary table
DECLARE  @ExecutionTime  TABLE(      Duration bigINT    ) 
DECLARE  @StartTime DATETIME,  @i INT = 1; 
WHILE (@i <= 5) 
  BEGIN 
    delete #values;
    -- TRUNCATE TABLE #values;
    DBCC freeproccache With NO_InfoMSGS;
    DBCC DROPCLEANBUFFERS With NO_InfoMSGS;
    SET @StartTime = CURRENT_TIMESTAMP -- alternate getdate() 
    /****************** measured process ***********************/ 

    INSERT #values SELECT a.* FROM master..spt_values a join master..spt_values b on b.type='P' and b.number < 1000;

    /**************** end measured process *********************/ 
    INSERT @ExecutionTime 
    SELECT DurationInMilliseconds = datediff(ms,@StartTime,CURRENT_TIMESTAMP) 
    SET @i +=  1 
  END -- WHILE 

SELECT DurationInMilliseconds = Duration FROM   @ExecutionTime 
GO

DROP TABLE  #values 
SET NOCOUNT OFF;

Câu trả lời:


20

Sự khác biệt này dường như chỉ áp dụng khi đối tượng là cây B +. Khi loại bỏ biến primary keytrên bảng để nó là một đống, tôi đã nhận được các kết quả sau

2560
2120
2080
2130
2140

Nhưng với PK tôi đã tìm thấy một mô hình tương tự trong các thử nghiệm của mình cũng như với các kết quả điển hình dưới đây.

+--------+--------+---------+-------------------+
| @table | #table | ##table | [permanent_table] |
+--------+--------+---------+-------------------+
|   2670 |   2683 |    9603 |              9703 |
|   6823 |   6840 |    9723 |              9790 |
|   6813 |   6816 |    9626 |              9703 |
|   6883 |   6816 |    9600 |              9716 |
|   6840 |   6856 |    9610 |              9673 |
+--------+--------+---------+-------------------+

Lý thuyết của tôi là có một số tối ưu hóa có sẵn khi thực hiện chèn số lượng lớn vào các cây B + tạm thời cục bộ chỉ áp dụng khi nó chưa có bất kỳ trang nào được phân bổ.

Tôi dựa trên những quan sát sau đây.

  1. Khi chạy các phiên bản khác nhau của mã kiểm tra của bạn, tôi chỉ thấy mẫu này @table_variables#tempbảng. Không phải bảng vĩnh viễn trong bảng tempdbcũng không ##.

  2. Để có được hiệu suất chậm hơn, trước đây không cần phải thêm và xóa một số lượng lớn các hàng khỏi bảng. Chỉ cần thêm một hàng duy nhất và để nó trong đó là đủ.

  3. TRUNCATEgiải quyết tất cả các trang từ bảng. DELETEsẽ không khiến trang cuối cùng trong bảng bị hủy.

  4. Sử dụng trình lược tả VS 2012 cho thấy trong trường hợp nhanh hơn, SQL Server sử dụng một đường dẫn mã khác. 36% thời gian được dành cho sqlmin.dll!RowsetBulk::InsertRowso với 61% thời gian dành sqlmin.dll!RowsetNewSS::InsertRowcho trường hợp chậm hơn.

Đang chạy

SELECT * 
FROM sys.dm_db_index_physical_stats(2,OBJECT_ID('tempdb..#values'),1,NULL, 'DETAILED')

sau khi xóa trả về

+-------------+------------+--------------+--------------------+
| index_level | page_count | record_count | ghost_record_count |
+-------------+------------+--------------+--------------------+
|           0 |          1 |            0 |                  1 |
|           1 |          1 |            1 |                  0 |
|           2 |          1 |            1 |                  0 |
+-------------+------------+--------------+--------------------+

Tôi thấy rằng có thể giảm bớt sự khác biệt về thời gian bằng cách bật cờ theo dõi 610 .

Điều này có ảnh hưởng của việc giảm số lượng của đăng nhập đáng kể cho chèn tiếp theo (giảm từ 350 MB đến 103 MB vì nó không còn ghi lại giá trị hàng chèn cá nhân) nhưng điều này chỉ có một sự cải thiện nhỏ trong timings cho thứ 2 và tiếp theo @table, #tabletrường hợp và khoảng cách vẫn còn. Cờ theo dõi đã cải thiện hiệu suất chung của các phần chèn vào hai loại bảng khác một cách đáng kể.

+--------+--------+---------+-------------------+
| @table | #table | ##table | [permanent_table] |
+--------+--------+---------+-------------------+
|   2663 |   2670 |    5403 |              5426 |
|   5390 |   5396 |    5410 |              5403 |
|   5373 |   5390 |    5410 |              5403 |
|   5393 |   5410 |    5406 |              5433 |
|   5386 |   5396 |    5390 |              5420 |
+--------+--------+---------+-------------------+

Từ việc xem xét nhật ký giao dịch, tôi nhận thấy rằng các lần chèn ban đầu vào các bảng tạm thời cục bộ trống dường như thậm chí còn được ghi lại tối thiểu (ở mức 96 MB).

Đáng chú ý là các chèn nhanh hơn này chỉ có 657các giao dịch ( LOP_BEGIN_XACT/ LOP_COMMIT_XACTcặp) so với 10,000trong các trường hợp chậm hơn. Trong LOP_FORMAT_PAGEhoạt động cụ thể có vẻ giảm nhiều. Các trường hợp chậm hơn có một mục nhật ký giao dịch cho điều này cho mỗi trang trong bảng (khoảng 10,270) so với chỉ 4các mục như vậy trong trường hợp nhanh.

Nhật ký được sử dụng trong cả ba trường hợp như sau (Tôi đã xóa các bản ghi nhật ký để cập nhật vào các bảng cơ sở hệ thống để giảm lượng văn bản nhưng chúng vẫn được bao gồm trong tổng số)

Ghi nhật ký chèn đầu tiên vào @table_var(96,5 MB)

+-----------------------+----------+----------------------------------------------+---------------+---------+
|       Operation       | Context  |                AllocUnitName                 | Size in Bytes |   Cnt   |
+-----------------------+----------+----------------------------------------------+---------------+---------+
| LOP_BEGIN_XACT        | LCX_NULL | NULL                                         |         83876 |     658 |
| LOP_COMMIT_XACT       | LCX_NULL | NULL                                         |         34164 |     657 |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL | NULL                                         |           120 |       3 |
| LOP_FORMAT_PAGE       | LCX_HEAP | dbo.#531856C7                                |            84 |       1 |
| LOP_FORMAT_PAGE       | LCX_IAM  | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |            84 |       1 |
| LOP_FORMAT_PAGE       | LCX_IAM  | dbo.#531856C7                                |            84 |       1 |
| LOP_FORMAT_PAGE       | LCX_IAM  | Unknown Alloc Unit                           |            84 |       1 |
| LOP_HOBT_DDL          | LCX_NULL | NULL                                         |           216 |       6 |
| LOP_HOBT_DELTA        | LCX_NULL | NULL                                         |           320 |       5 |
| LOP_IDENT_NEWVAL      | LCX_NULL | NULL                                         |     100240000 | 2506000 |
| LOP_INSERT_ROWS       | LCX_HEAP | dbo.#531856C7                                |            72 |       1 |
| LOP_MODIFY_ROW        | LCX_IAM  | dbo.#531856C7                                |            88 |       1 |
| LOP_MODIFY_ROW        | LCX_PFS  | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |        158592 |    1848 |
| LOP_MODIFY_ROW        | LCX_PFS  | dbo.#531856C7                                |            80 |       1 |
| LOP_MODIFY_ROW        | LCX_PFS  | Unknown Alloc Unit                           |        216016 |    2455 |
| LOP_SET_BITS          | LCX_GAM  | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |         84360 |    1406 |
| LOP_SET_BITS          | LCX_GAM  | Unknown Alloc Unit                           |        147120 |    2452 |
| LOP_SET_BITS          | LCX_IAM  | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |         84360 |    1406 |
| LOP_SET_BITS          | LCX_IAM  | Unknown Alloc Unit                           |        147120 |    2452 |
| Total                 | NULL     | NULL                                         |     101209792 | 2519475 |
+-----------------------+----------+----------------------------------------------+---------------+---------+

Ghi nhật ký sau đó chèn TF 610 (350 MB)

+-----------------------+--------------------+----------------------------------------------+---------------+---------+
|       Operation       |      Context       |                AllocUnitName                 | Size in Bytes |   Cnt   |
+-----------------------+--------------------+----------------------------------------------+---------------+---------+
| LOP_BEGIN_CKPT        | LCX_NULL           | NULL                                         |            96 |       1 |
| LOP_BEGIN_XACT        | LCX_NULL           | NULL                                         |       1520696 |   12521 |
| LOP_COMMIT_XACT       | LCX_NULL           | NULL                                         |        651040 |   12520 |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL           | NULL                                         |            40 |       1 |
| LOP_DELETE_SPLIT      | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |          2160 |      36 |
| LOP_END_CKPT          | LCX_NULL           | NULL                                         |           136 |       1 |
| LOP_FORMAT_PAGE       | LCX_HEAP           | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |        859236 |   10229 |
| LOP_FORMAT_PAGE       | LCX_IAM            | Unknown Alloc Unit                           |            84 |       1 |
| LOP_FORMAT_PAGE       | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |          3108 |      37 |
| LOP_HOBT_DDL          | LCX_NULL           | NULL                                         |           648 |      18 |
| LOP_HOBT_DELTA        | LCX_NULL           | NULL                                         |        657088 |   10267 |
| LOP_IDENT_NEWVAL      | LCX_NULL           | NULL                                         |     100239960 | 2505999 |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |     258628000 | 2506000 |
| LOP_INSERT_ROWS       | LCX_HEAP           | dbo.#531856C7                                |            72 |       1 |
| LOP_INSERT_ROWS       | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |       1042776 |   10302 |
| LOP_MODIFY_HEADER     | LCX_HEAP           | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |        859236 |   10229 |
| LOP_MODIFY_HEADER     | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |          3192 |      38 |
| LOP_MODIFY_ROW        | LCX_IAM            | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |           704 |       8 |
| LOP_MODIFY_ROW        | LCX_PFS            | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |        934264 |   11550 |
| LOP_MODIFY_ROW        | LCX_PFS            | Unknown Alloc Unit                           |        783984 |    8909 |
| LOP_SET_BITS          | LCX_GAM            | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |         76980 |    1283 |
| LOP_SET_BITS          | LCX_GAM            | Unknown Alloc Unit                           |        534480 |    8908 |
| LOP_SET_BITS          | LCX_IAM            | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 |         76980 |    1283 |
| LOP_SET_BITS          | LCX_IAM            | Unknown Alloc Unit                           |        534480 |    8908 |
| LOP_SHRINK_NOOP       | LCX_NULL           | NULL                                         |            32 |       1 |
| LOP_XACT_CKPT         | LCX_NULL           | NULL                                         |            92 |       1 |
| Total                 | NULL               | NULL                                         |     367438748 | 5119297 |
+-----------------------+--------------------+----------------------------------------------+---------------+---------+

Ghi nhật ký sau đó chèn TF 610 vào (103 MB)

+-------------------------+-------------------------+----------------------------------------------+---------------+---------+
|        Operation        |         Context         |                AllocUnitName                 | Size in Bytes |   Cnt   |
+-------------------------+-------------------------+----------------------------------------------+---------------+---------+
| LOP_BEGIN_CKPT          | LCX_NULL                | NULL                                         |           192 |       2 |
| LOP_BEGIN_XACT          | LCX_NULL                | NULL                                         |       1339796 |   11099 |
| LOP_BULK_EXT_ALLOCATION | LCX_NULL                | NULL                                         |         20616 |     162 |
| LOP_COMMIT_XACT         | LCX_NULL                | NULL                                         |        577096 |   11098 |
| LOP_CREATE_ALLOCCHAIN   | LCX_NULL                | NULL                                         |            40 |       1 |
| LOP_DELETE_SPLIT        | LCX_INDEX_INTERIOR      | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |          2160 |      36 |
| LOP_END_CKPT            | LCX_NULL                | NULL                                         |           272 |       2 |
| LOP_FORMAT_PAGE         | LCX_BULK_OPERATION_PAGE | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |        863520 |   10280 |
| LOP_FORMAT_PAGE         | LCX_IAM                 | Unknown Alloc Unit                           |            84 |       1 |
| LOP_FORMAT_PAGE         | LCX_INDEX_INTERIOR      | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |          3108 |      37 |
| LOP_HOBT_DELTA          | LCX_NULL                | NULL                                         |        666496 |   10414 |
| LOP_IDENT_NEWVAL        | LCX_NULL                | NULL                                         |     100239960 | 2505999 |
| LOP_INSERT_ROWS         | LCX_CLUSTERED           | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |         23544 |     218 |
| LOP_INSERT_ROWS         | LCX_HEAP                | dbo.#719CDDE7                                |            72 |       1 |
| LOP_INSERT_ROWS         | LCX_INDEX_INTERIOR      | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |       1042776 |   10302 |
| LOP_MODIFY_HEADER       | LCX_BULK_OPERATION_PAGE | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |        780216 |   10266 |
| LOP_MODIFY_HEADER       | LCX_HEAP                | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |       1718472 |   20458 |
| LOP_MODIFY_HEADER       | LCX_INDEX_INTERIOR      | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |          3192 |      38 |
| LOP_MODIFY_ROW          | LCX_IAM                 | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |           704 |       8 |
| LOP_MODIFY_ROW          | LCX_PFS                 | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |        114832 |    1307 |
| LOP_MODIFY_ROW          | LCX_PFS                 | Unknown Alloc Unit                           |        231696 |    2633 |
| LOP_RANGE_INSERT        | LCX_NULL                | NULL                                         |            48 |       1 |
| LOP_SET_BITS            | LCX_GAM                 | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |         77100 |    1285 |
| LOP_SET_BITS            | LCX_GAM                 | Unknown Alloc Unit                           |        157920 |    2632 |
| LOP_SET_BITS            | LCX_IAM                 | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 |         77100 |    1285 |
| LOP_SET_BITS            | LCX_IAM                 | Unknown Alloc Unit                           |        157920 |    2632 |
| LOP_XACT_CKPT           | LCX_NULL                | NULL                                         |            92 |       1 |
| Total                   | NULL                    | NULL                                         |     108102960 | 2602218 |
+-------------------------+-------------------------+----------------------------------------------+---------------+---------+

Cảm ơn đã xác nhận chi tiết. Vì vậy, câu hỏi vẫn còn chờ đợi, tại sao XÓA không trả lại bảng để thực sự trống, sử dụng thuật ngữ của bạn. Ngoài ra, điều này sẽ tranh luận về việc sử dụng các bảng #temp nếu xóa / điền được sử dụng trong một vòng xử lý hàng loạt.
孔夫子

1
@RichardTheKiwi - Lợi ích của việc TRUNCATEqua DELETEngày của riêng mình cũng sẽ ủng hộ việc đó. Tôi cũng hiếm khi xem xét các biến bảng cho số lượng lớn các hàng.
Martin Smith

Điều này nghe có vẻ lười biếng, nhưng sẽ không lặp lại 1 đến 10 bản ghi (biến) chèn 1000 lần trong một lô cho thấy các triệu chứng tương tự? Việc sử dụng một số lượng lớn các hàng chỉ để làm trầm trọng thêm vấn đề và cung cấp quy mô để thấy rõ hơn sự khác biệt. Ý chính của câu hỏi là chứng minh bằng cách này hay cách khác rằng các bảng #temp sẽ tốt hơn, một khi chúng ta biết sự khác biệt là gì.
孔夫子

Lý thuyết của tôi là phân bổ các 10,000+trang xảy ra theo cách tối ưu hơn nhiều và dường như tránh một số chi phí trên mỗi trang. Đối với các phần chèn nhỏ hơn, tôi hy vọng bất kỳ sự khác biệt nào như vậy sẽ ít quan trọng hơn.
Martin Smith

@RichardTheKiwi - Cảm ơn! Có lẽ có nhiều điều để nói về điều này. Vì tôi sẽ thử nâng cấp lên cùng phiên bản với SQL Kiwi và xem liệu tôi có còn thấy các đường dẫn mã khác nhau không. Nếu vậy có lẽ phụ thuộc vào phần cứng mà nó tạo ra sự khác biệt như vậy (các thử nghiệm của tôi đã có trên máy tính để bàn của tôi với tất cả các tệp dữ liệu và nhật ký trên cùng một ổ SSD)
Martin Smith

0

Quan sát và đầu cơ. . .

Trên một số hệ thống, CURRENT_TIMESTAMP được xác định là thời điểm bắt đầu giao dịch hiện tại. Một tìm kiếm nhanh cho thấy không có tài liệu chính xác nào về cách thức hoạt động của CURRENT_TIMESTAMP trên SQL Server. Nhưng chế độ mặc định của SQL Server là tự động giao dịch và không có BEGIN TRANSACTION ở đây, do đó, phải là thời gian ngay trước câu lệnh INSERT. (Câu lệnh DELETE sẽ tự động cam kết và bất cứ cách nào CURRENT_TIMESTAMP hoạt động trên SQL Server, thì không nên làm gì với câu lệnh DELETE khi bạn đang sử dụng các giao dịch được cam kết tự động.)

Trong lần lặp đầu tiên, câu lệnh XÓA không có bất kỳ công việc thực sự nào và không có bất kỳ hàng riêng lẻ nào để đăng nhập. Có lẽ trình tối ưu hóa biết điều đó và điều đó làm giảm thời gian cho lần lặp đầu tiên. (Sự kết hợp của không có hàng nào để xóa và không có hàng riêng lẻ nào để đăng nhập.)

Bạn có thể kiểm tra điều đó (tôi nghĩ) bằng cách chèn trước khi xóa.


Tôi sẽ ngừng trả lời câu hỏi ngày hôm nay. Hoặc bất cứ điều gì tôi đang làm khi tôi gõ mọi thứ vào ô đó.
Mike Sherrill 'Nhớ lại mèo'

Câu trả lời này có nên bị xóa vì lỗi thời, tiếp tuyến và mất tập trung không?
孔夫子
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.