Warum wirkt sich DELETE nach wie vor auf die Leistung aus?


20

Am Ende befindet sich ein Testskript zum Vergleichen der Leistung zwischen einer @table-Variablen und einer #temp-Tabelle. Ich denke, ich habe es richtig eingerichtet - die Performance-Timings werden außerhalb der DELETE / TRUNCATE-Befehle genommen. Die Ergebnisse, die ich erhalte, sind wie folgt (Zeiten in Millisekunden).

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

Nur um sicherzugehen, dass ich vernünftig bin, zeigt dies, dass CURRENT_TIMESTAMP (aka GetDate()) zum Zeitpunkt der Anweisung und nicht des Stapels genommen wird. Daher sollte es keine Interaktion zwischen TRUNCATE / DELETE und der SET @StartTime = CURRENT_TIMESTAMPAnweisung geben.

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

Es ist ziemlich konsistent im Sprung zwischen dem ersten Lauf und den nachfolgenden Läufen, wenn DELETE zum Löschen der Tabelle verwendet wird. Was fehlt mir in meinem Verständnis von DELETE ? Ich habe dies viele Male wiederholt, die Reihenfolge vertauscht, Tempdb-Größe angepasst, um kein Wachstum zu erfordern usw.

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;

Antworten:


20

Dieser Unterschied scheint nur zuzutreffen, wenn das Objekt ein B + -Baum ist. Beim Entfernen der primary keyauf dem Tisch befindlichen Variablen, damit es sich um einen Haufen handelt, habe ich die folgenden Ergebnisse erhalten

2560
2120
2080
2130
2140

Aber mit der PK habe ich auch in meinen Tests ein ähnliches Muster mit den unten angegebenen typischen Ergebnissen gefunden.

+--------+--------+---------+-------------------+
| @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 |
+--------+--------+---------+-------------------+

Ich gehe davon aus, dass beim Durchführen von Bulk-Einfügungen in lokale temporäre B + -Bäume einige Optimierungen verfügbar sind, die nur gelten, wenn noch keine Seiten zugewiesen wurden.

Ich stütze mich auf die folgenden Beobachtungen.

  1. Beim Ausführen verschiedener Versionen Ihres Testcodes habe ich nur dieses Muster mit @table_variablesund #tempTabellen gesehen. Weder permanente Tabellen tempdbnoch ##Tabellen.

  2. Um eine langsamere Leistung zu erzielen, ist es nicht erforderlich, zuvor eine große Anzahl von Zeilen zur Tabelle hinzugefügt und daraus entfernt zu haben. Es reicht aus, eine einzelne Zeile hinzuzufügen und dort zu belassen.

  3. TRUNCATEhebt die Zuordnung aller Seiten aus der Tabelle auf. DELETEDie Zuordnung der letzten Seite in der Tabelle wird nicht aufgehoben.

  4. Die Verwendung des VS 2012-Profilers zeigt, dass SQL Server im schnelleren Fall einen anderen Codepfad verwendet. 36% der Zeit wird für langsame Fälle sqlmin.dll!RowsetBulk::InsertRowaufgewendet, sqlmin.dll!RowsetNewSS::InsertRowwährend 61% der Zeit für langsame Fälle aufgewendet werden.

Laufen

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

Nach dem Löschen wird zurückgegeben

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

Ich fand, dass es möglich war, die Zeitdiskrepanz etwas zu verringern, indem das Ablaufverfolgungsflag 610 aktiviert wurde .

Dies hatte den Effekt der Verringerung der Menge von im wesentlichen für die nachfolgenden Einsätze Protokollierung (unten von 350 MB bis 103 MB , da es nicht mehr um die einzelnen eingesetzten Zeilenwerte protokolliert) , aber dies hatte nur eine geringe Verbesserung der Zeitpunkte für das zweite und nachfolgende @table, #tableFälle und die Lücke bleibt immer noch. Das Ablaufverfolgungsflag hat die allgemeine Leistung von Einfügungen für die beiden anderen Tabellentypen erheblich verbessert.

+--------+--------+---------+-------------------+
| @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 |
+--------+--------+---------+-------------------+

Beim Durchsuchen des Transaktionsprotokolls ist mir aufgefallen, dass die anfänglichen Einfügungen für leere lokale temporäre Tabellen noch minimaler protokolliert zu sein scheinen (bei 96 MB).

Bemerkenswerterweise hatten diese schnelleren Einfügungen nur 657Transaktionen ( LOP_BEGIN_XACT/ LOP_COMMIT_XACTPaare) im Vergleich zu Over 10,000in den langsameren Fällen. Insbesondere LOP_FORMAT_PAGEscheinen die Operationen stark reduziert zu sein. Die langsameren Fälle haben einen Transaktionsprotokolleintrag für jede Seite in der Tabelle (ungefähr 10,270), verglichen mit nur 4solchen Einträgen im schnellen Fall.

In allen drei Fällen wurde das folgende Protokoll verwendet (Ich habe die Protokollsätze für Aktualisierungen von Systembasistabellen gelöscht, um die Textmenge zu verringern, sie sind jedoch weiterhin in den Summen enthalten).

Protokollierung der ersten Einfügung gegen @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 |
+-----------------------+----------+----------------------------------------------+---------------+---------+

Protokollierung nachfolgender Beilagen TF 610 aus (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 |
+-----------------------+--------------------+----------------------------------------------+---------------+---------+

Protokollierung nachfolgender Inserts TF 610 auf (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 |
+-------------------------+-------------------------+----------------------------------------------+---------------+---------+

Danke für die ausführliche Bestätigung. Es stellt sich immer noch die Frage, warum DELETE die Tabelle unter Verwendung Ihres Begriffs nicht wirklich leer macht. Dies würde auch für die Verwendung von #temp-Tabellen sprechen, wenn clear / populate in einer Stapelverarbeitungsschleife verwendet wird.
21.

1
@RichardTheKiwi - Der Nutzen von TRUNCATEOver DELETEalleine würde auch dafür sprechen. Ich würde auch selten Tabellenvariablen für eine große Anzahl von Zeilen in Betracht ziehen.
Martin Smith

Dies klingt träge, aber würde eine 1000-malige Wiederholung eines Einfügens von 1 bis 10 Datensätzen (Variablen) in einem Stapel nicht die gleichen Symptome zeigen? Die Verwendung einer großen Anzahl von Zeilen dient nur dazu, das Problem zu verschlimmern und eine Skalierung bereitzustellen, um den Unterschied besser zu erkennen. Der Kern der Frage besteht darin, auf die eine oder andere Weise zu beweisen, dass #temp-Tabellen besser wären, sobald wir den Unterschied kennen.
21.

Nun, meine Theorie ist, dass es die Zuordnung der 10,000+Seiten ist, die viel optimierter abläuft und einen gewissen Overhead pro Seite zu vermeiden scheint. Für kleinere Beilagen würde ich erwarten, dass ein solcher Unterschied weniger signifikant ist.
Martin Smith

@RichardTheKiwi - Danke! Dazu gibt es wahrscheinlich mehr zu sagen. Da werde ich versuchen, auf die gleiche Version wie SQL Kiwi zu aktualisieren und sehen, ob ich noch die verschiedenen Codepfade sehe. Wenn ja, ist es möglicherweise hardwareabhängig, dass es einen solchen Unterschied macht (meine Tests wurden auf meinem Desktop-PC mit allen Daten und Protokolldateien auf derselben SSD durchgeführt)
Martin Smith

0

Beobachtung und Spekulation. . .

Auf einigen Systemen ist CURRENT_TIMESTAMP der Zeitpunkt zum Start der aktuellen Transaktion. Eine Schnellsuche ergab keine endgültige Dokumentation darüber, wie sich CURRENT_TIMESTAMP auf SQL Server verhält. Der Standardmodus von SQL Server ist jedoch das automatische Festschreiben von Transaktionen, und hier gibt es keinen BEGIN TRANSACTION. Daher sollte es die Zeit unmittelbar vor der INSERT-Anweisung sein. (Die DELETE-Anweisung sollte automatisch festgeschrieben werden. Unabhängig davon, wie CURRENT_TIMESTAMP auf SQL Server funktioniert, sollte sie nichts mit der DELETE-Anweisung zu tun haben, wenn Sie automatisch festgeschriebene Transaktionen verwenden.)

Bei der ersten Iteration hat die DELETE-Anweisung keine wirkliche Arbeit zu erledigen, und es sind keine einzelnen Zeilen zu protokollieren. Vielleicht weiß der Optimierer das, und das verkürzt die Zeit für die erste Iteration. (Die Kombination aus keiner zu löschenden Zeile und keiner einzelnen zu protokollierenden Zeile.)

Sie können das (glaube ich) testen, indem Sie es vor dem Löschen einfügen.


Ich werde heute aufhören, Fragen zu beantworten. Oder was auch immer ich tue, wenn ich Dinge in dieses Kästchen tippe.
Mike Sherrill 'Cat Recall'

Sollte diese Antwort als veraltet, tangential und ablenkend gelöscht werden?
14.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.