Mengapa DELETE meninggalkan efek yang tersisa pada kinerja?

20

Pada akhirnya adalah skrip uji untuk membandingkan kinerja antara variabel @table dan tabel #temp. Saya pikir saya sudah mengaturnya dengan benar - timing kinerja diambil di luar perintah DELETE / TRUNCATE. Hasil yang saya dapatkan adalah sebagai berikut (kali dalam milidetik).

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

Hanya untuk memastikan saya waras, ini menunjukkan bahwa CURRENT_TIMESTAMP (alias GetDate()) diambil pada saat pernyataan, bukan dari bets, jadi tidak boleh ada interaksi antara TRUNCATE / DELETE dengan SET @StartTime = CURRENT_TIMESTAMPpernyataan tersebut.

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

Ini cukup konsisten dalam lompatan antara run pertama dan run berikutnya ketika DELETE digunakan untuk menghapus tabel. Apa yang saya lewatkan dalam pemahaman saya akan HAPUS ? Saya telah mengulangi ini berkali-kali, menukar urutan, ukuran tempdb untuk tidak memerlukan pertumbuhan dll.

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;
孔夫子
sumber

Jawaban:

20

Perbedaan ini tampaknya hanya berlaku ketika objek adalah pohon B +. Ketika menghapus primary keyvariabel di atas meja jadi tumpukan saya mendapat hasil berikut

2560
2120
2080
2130
2140

Tetapi dengan PK saya menemukan pola yang sama dalam tes saya juga dengan hasil khas di bawah ini.

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

Teori saya adalah bahwa ada beberapa optimasi yang tersedia ketika melakukan penyisipan massal ke pohon B + lokal sementara yang hanya berlaku ketika belum ada alokasi halaman.

Saya mendasarkan ini pada pengamatan berikut.

  1. Saat menjalankan berbagai versi kode pengujian Anda, saya hanya melihat pola @table_variablesdan #temptabel ini. Bukan tabel permanen tempdbatau ##tabel.

  2. Untuk mendapatkan kinerja yang lebih lambat, Anda tidak perlu menambahkan dan menghapus sebelumnya sejumlah besar baris dari tabel. Cukup menambahkan satu baris dan membiarkannya di sana sudah cukup.

  3. TRUNCATEalokasikan semua halaman dari tabel. DELETEtidak akan menyebabkan halaman terakhir dalam tabel menjadi tidak dialokasikan.

  4. Menggunakan profiler VS 2012 menunjukkan bahwa dalam kasus yang lebih cepat SQL Server menggunakan jalur kode yang berbeda. 36% dari waktu dihabiskan sqlmin.dll!RowsetBulk::InsertRowvs 61% dari waktu yang dihabiskan sqlmin.dll!RowsetNewSS::InsertRowuntuk kasus yang lebih lambat.

Lari

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

setelah itu hapus kembali

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

Saya menemukan bahwa mungkin untuk mengurangi perbedaan waktu dengan mengaktifkan jejak flag 610 .

Ini memiliki efek mengurangi kuantitas penebangan secara substansial untuk menyisipkan berikutnya (turun dari 350 MB ke 103 MB karena tidak lagi log individu nilai-nilai baris dimasukkan) tapi ini hanya perbaikan kecil dalam timing untuk 2 dan selanjutnya @table, #tablekasus dan kesenjangan masih ada. Bendera jejak meningkatkan kinerja umum menyisipkan ke dua jenis tabel lainnya secara signifikan.

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

Dari melihat log transaksi saya perhatikan bahwa sisipan awal terhadap tabel temporer lokal yang kosong tampaknya bahkan lebih sedikit dicatat (pada 96 MB).

Khususnya sisipan yang lebih cepat ini hanya memiliki 657transaksi ( LOP_BEGIN_XACT/ LOP_COMMIT_XACTpasangan) dibandingkan dengan lebih 10,000dalam kasus yang lebih lambat. Dalam LOP_FORMAT_PAGEoperasi tertentu tampak jauh berkurang. Kasing yang lebih lambat memiliki entri log transaksi untuk ini untuk setiap halaman dalam tabel (sekitar 10,270) dibandingkan dengan 4entri tersebut dalam kasing cepat.

Log yang digunakan dalam ketiga kasus adalah sebagai berikut (Saya telah menghapus catatan log untuk pembaruan ke tabel basis sistem untuk mengurangi jumlah teks tetapi mereka masih termasuk dalam total)

Memasukkan sisipan pertama terhadap @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 |
+-----------------------+----------+----------------------------------------------+---------------+---------+

Log off berikutnya memasukkan TF 610 off (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 |
+-----------------------+--------------------+----------------------------------------------+---------------+---------+

Mencatat sisipan TF 610 pada (103 MB) berikutnya

+-------------------------+-------------------------+----------------------------------------------+---------------+---------+
|        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 |
+-------------------------+-------------------------+----------------------------------------------+---------------+---------+
Martin Smith
sumber
Terima kasih atas konfirmasi terperinci. Jadi pertanyaannya masih ada, mengapa DELETE tidak mengembalikan tabel menjadi benar - benar kosong, menggunakan istilah Anda. Juga, ini akan berdebat untuk menggunakan tabel #temp jika clear / populate digunakan dalam loop pemrosesan batch.
孔夫子
1
@RichardTheKiwi - Manfaat TRUNCATElebih DELETEdari itu sendiri juga akan berdebat untuk itu. Saya juga jarang mempertimbangkan variabel tabel untuk sejumlah besar baris.
Martin Smith
Ini kedengarannya malas, tetapi tidakkah mengulangi 1 (10) rekaman (variabel) yang dimasukkan 1000 kali dalam satu batch menunjukkan gejala yang sama? Penggunaan sejumlah besar baris hanya untuk memperburuk masalah, dan memberikan skala untuk lebih melihat perbedaannya. Inti dari pertanyaan ini adalah untuk membuktikan satu atau lain cara bahwa tabel #temp akan lebih baik, setelah kita tahu apa perbedaannya.
孔夫子
Baik teori saya adalah bahwa itu adalah alokasi 10,000+halaman yang terjadi dengan cara yang jauh lebih dioptimalkan dan tampaknya menghindari beberapa overhead per halaman. Untuk sisipan yang lebih kecil saya berharap perbedaan seperti itu menjadi kurang signifikan.
Martin Smith
@RichardTheKiwi - Terima kasih! Mungkin ada lebih banyak untuk dikatakan tentang ini. Karena saya akan mencoba memutakhirkan ke versi yang sama dengan SQL Kiwi dan melihat apakah saya masih melihat jalur kode yang berbeda. Jika demikian, mungkin itu tergantung pada perangkat keras sehingga membuat perbedaan (pengujian saya dilakukan pada PC desktop saya dengan semua data dan file log pada SSD yang sama)
Martin Smith
0

Pengamatan dan spekulasi. . .

Pada beberapa sistem, CURRENT_TIMESTAMP didefinisikan sebagai waktu di awal transaksi saat ini. Pencarian cepat tidak menemukan dokumentasi pasti tentang bagaimana CURRENT_TIMESTAMP berperilaku pada SQL Server. Tetapi mode default SQL Server adalah untuk autocommit transaksi, dan tidak ada TRANSAKSI BEGIN di sini, jadi itu harus waktu tepat sebelum pernyataan INSERT. (Pernyataan DELETE harus secara otomatis melakukan, dan dengan cara apa pun CURRENT_TIMESTAMP bekerja pada SQL Server, seharusnya tidak ada hubungannya dengan pernyataan DELETE ketika Anda menggunakan transaksi yang dilakukan secara otomatis.)

Pada iterasi pertama, pernyataan DELETE tidak memiliki pekerjaan nyata untuk dilakukan, dan tidak ada baris individu untuk dicatat. Mungkin pengoptimal tahu itu, dan itu mengurangi waktu untuk iterasi pertama. (Kombinasi tanpa baris untuk dihapus, dan tidak ada baris untuk dicatat.)

Anda bisa mengujinya (saya pikir) dengan memasukkan sebelum menghapus.

Mike Sherrill 'Cat Recall'
sumber
Saya akan berhenti menjawab pertanyaan hari ini. Atau apa pun yang saya lakukan ketika saya mengetik sesuatu ke dalam kotak itu.
Mike Sherrill 'Cat Recall'
Haruskah jawaban ini dihapus sebagai usang, tangensial dan mengganggu?
孔夫子