Ada mitos yang terus-menerus di dunia SQL Server bahwa DROP TABLE
dan TRUNCATE TABLE
perintah tidak dicatat.
Mereka tidak. Keduanya dicatat sepenuhnya, tetapi dicatat secara efisien.
Anda dapat dengan mudah membuktikan ini kepada diri sendiri. Jalankan kode berikut untuk menyiapkan database dan tabel pengujian, dan tunjukkan bahwa kita memiliki 10.000 baris di tabel kita:
CREATE DATABASE [TruncateTest]; GO ALTER DATABASE [TruncateTest] SET RECOVERY SIMPLE; GO USE [TruncateTest]; GO CREATE TABLE [TestTable] ( [c1] INT IDENTITY, [c2] CHAR (8000) DEFAULT 'a'); GO SET NOCOUNT ON; GO INSERT INTO [TestTable] DEFAULT VALUES; GO 10000 SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Hasil:
Jumlah Baris———–
10000
Dan kemudian kode berikut, yang memotong tabel dalam transaksi dan memeriksa jumlah baris:
BEGIN TRAN; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Hasil:
Jumlah Baris———–
0
Sekarang meja itu kosong. Tapi saya bisa memutar kembali transaksi dan mengembalikan semua data lagi:
ROLLBACK TRAN; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Hasil:
Jumlah Baris———–
10000
Jelas TRUNCATE
operasi harus dicatat jika tidak operasi roll back tidak akan bekerja.
Jadi dari mana kesalahpahaman itu berasal?
Itu berasal dari perilaku DROP
dan TRUNCATE
operasi di meja besar. Mereka akan menyelesaikan hampir secara instan, dan jika Anda melihat log transaksi menggunakan fn_dblog
tepat setelah itu, Anda hanya akan melihat sejumlah kecil catatan log yang dihasilkan dari operasi tersebut. Angka kecil itu tidak berkorelasi dengan ukuran tabel yang terpotong atau dijatuhkan, sehingga seolah-olah DROP
dan TRUNCATE
operasi tidak dicatat.
Tapi mereka sepenuhnya login, seperti yang saya tunjukkan di atas. Jadi di mana catatan log untuk operasi?
Jawabannya adalah bahwa catatan log akan dibuat, hanya tidak segera, dengan mekanisme yang disebut 'penurunan yang ditangguhkan', yang ditambahkan di SQL Server 2000 SP3.
Ketika sebuah tabel dijatuhkan atau dipotong, semua halaman file data yang dialokasikan untuk tabel tersebut harus tidak dialokasikan. Mekanisme untuk ini sebelum SQL Server 2000 SP3 adalah sebagai berikut:
Untuk setiap tingkat yang dialokasikan ke tabelMulai
Dapatkan kunci alokasi eksklusif pada tingkat
Selidiki kunci halaman untuk setiap halaman sejauh (dapatkan kunci dalam mode eksklusif, dan segera jatuhkan, pastikan tidak ada orang lain yang mengunci halaman)
JANGAN lepaskan kunci tingkat, menjamin bahwa tidak ada orang lain yang dapat menggunakan tingkat itu
Pindah ke tingkat berikutnya
End
Karena semua penguncian dipertahankan hingga akhir operasi, dan setiap penguncian membutuhkan sedikit memori, manajer penguncian mungkin kehabisan memori saat DROP
atau TRUNCATE
dari meja yang sangat besar terjadi. Beberapa pelanggan SQL Server mulai menemukan bahwa mereka mengalami kondisi kehabisan memori pada SQL Server 2000, karena tabel tumbuh sangat besar dan jauh melampaui pertumbuhan memori sistem.
Mekanisme deferred-drop mensimulasikan DROP
atau TRUNCATE
operasi selesai dengan segera, dengan melepaskan pengait alokasi untuk tabel dan menempatkannya pada 'antrean penangguhan-jatuhkan', untuk kemudian diproses oleh tugas latar belakang. Operasi unhook-and-transfer ini hanya menghasilkan beberapa catatan log. Ini adalah operasi yang sedang dilakukan dan dibatalkan dalam contoh kode saya di atas.
'Tugas latar belakang yang ditangguhkan' berputar setiap beberapa detik dan membatalkan alokasi semua halaman dan perluasan pada antrian penangguhan-jatuhkan dalam jumlah kecil batch, menjamin bahwa operasi tidak akan kehabisan memori. Dealokasi ini semuanya dicatat sepenuhnya, tetapi ingat bahwa membatalkan alokasi halaman yang penuh dengan data atau catatan indeks tidak mencatat penghapusan individual dari catatan tersebut; alih-alih seluruh halaman hanya ditandai sebagai tidak dialokasikan dalam peta byte alokasi PFS (Page Free Space) yang relevan.
Dari SQL Server 2000 SP3 dan seterusnya, saat Anda melakukan DROP
atau TRUNCATE
tabel, Anda hanya akan melihat beberapa catatan log yang dihasilkan. Jika Anda menunggu sekitar satu menit, dan kemudian melihat log transaksi lagi, Anda akan melihat ribuan catatan log telah dihasilkan oleh operasi penangguhan-jatuhkan, masing-masing membatalkan alokasi halaman atau batas. Operasi dicatat sepenuhnya dan efisien.
Berikut ini contohnya, menggunakan skenario yang kami buat di atas:
CHECKPOINT; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'LogRecCount' FROM fn_dblog (NULL, NULL); GO
Hasil:
LogRecCount———–
25
Seperti yang Anda lihat, jelas tidak ada catatan log yang membatalkan alokasi 10.000 halaman, ditambah 1.250 luasan di tabel TestTable.
Jika saya menunggu beberapa detik, lalu jalankan fn_dblog
kode lagi, saya mendapatkan:
———–
3811
Anda mungkin bertanya-tanya mengapa tidak ada setidaknya 10.000 catatan log – satu untuk setiap halaman yang dibatalkan alokasinya. Itu karena dealokasi halaman bahkan dicatat secara efisien – dengan satu catatan log yang mencerminkan perubahan alokasi halaman PFS untuk 8 halaman file data berturut-turut, alih-alih satu catatan log untuk setiap halaman file data yang mencerminkan perubahan status alokasinya di halaman PFS.
SQL Server selalu mencoba untuk menghasilkan log transaksi sesedikit mungkin, sambil tetap mengikuti aturan tentang logging penuh atau minimal berdasarkan model pemulihan saat ini. Jika Anda ingin melihat catatan log aktual yang dihasilkan oleh mekanisme unhook-and-transfer dan deferred-drop, cukup ganti * untuk COUNT (*) dalam kode fn_dblog di atas dan cari transaksi dengan Nama Transaksi yang disetel ke DeferredAllocUnitDrop::Process
.
Di postingan mendatang, saya akan membahas internal yang mendukung mitos persisten lainnya seputar aspek kinerja SQL Server Storage Engine.