[ Bagian 1 | Bagian 2 | Bagian 3 | Bagian 4 ]
Masalah yang saya lihat muncul beberapa kali baru-baru ini adalah skenario di mana Anda telah membuat kolom IDENTITY sebagai INT, dan sekarang mendekati batas atas dan perlu membuatnya lebih besar (BIGINT). Jika meja Anda cukup besar sehingga Anda mencapai batas atas bilangan bulat (lebih dari 2 miliar), ini bukan operasi yang dapat Anda lakukan antara makan siang dan rehat kopi Anda pada hari Selasa. Seri ini akan mengeksplorasi mekanisme di balik perubahan tersebut, dan berbagai cara untuk mewujudkannya dengan berbagai dampak pada waktu aktif. Pada bagian pertama, saya ingin melihat lebih dekat dampak fisik dari perubahan INT menjadi BIGINT tanpa variabel lainnya.
Apa yang Sebenarnya Terjadi Saat Anda Memperluas INT?
INT dan BIGINT adalah tipe data ukuran tetap, oleh karena itu konversi dari satu ke yang lain harus menyentuh halaman, menjadikannya operasi ukuran data. Ini kontra-intuitif, karena sepertinya tidak mungkin untuk perubahan tipe data dari INT ke BIGINT untuk memerlukan ruang tambahan pada halaman segera (dan untuk kolom IDENTITY, selamanya). Berpikir secara logis, ini adalah ruang yang tidak mungkin diperlukan sampai nanti, ketika nilai INT yang ada diubah menjadi nilai> 4 byte. Tapi ini bukan cara kerjanya hari ini. Mari kita buat tabel sederhana dan lihat:
CREATE TABLE dbo.FirstTest ( RowID int IDENTITY(1,1), Filler char(2500) NOT NULL DEFAULT 'x' ); GO INSERT dbo.FirstTest WITH (TABLOCKX) (Filler) SELECT TOP (20) 'x' FROM sys.all_columns AS c; GO
Kueri sederhana dapat memberi tahu saya halaman rendah dan tinggi yang dialokasikan untuk objek ini, serta jumlah halaman total:
SELECT lo_page = MIN(allocated_page_page_id), hi_page = MAX(allocated_page_page_id), page_count = COUNT(*) FROM sys.dm_db_database_page_allocations ( DB_ID(), OBJECT_ID(N'dbo.FirstTest'), NULL, NULL, NULL );
Sekarang jika saya menjalankan kueri itu sebelum dan sesudah mengubah tipe data dari INT ke BIGINT:
ALTER TABLE dbo.FirstTest ALTER COLUMN RowID bigint;
Saya melihat hasil ini:
-- before: lo_page hi_page page_count ------- ------- ---------- 243 303 17 -- after: lo_page hi_page page_count ------- ------- ---------- 243 319 33
Jelas bahwa 16 halaman baru ditambahkan untuk memberi ruang bagi ruang tambahan yang diperlukan (walaupun kita tahu tidak ada nilai dalam tabel yang benar-benar membutuhkan 8 byte). Tapi ini tidak benar-benar tercapai seperti yang mungkin Anda pikirkan – alih-alih melebarkan kolom pada halaman yang ada, baris dipindahkan ke halaman baru, dengan penunjuk tertinggal di tempatnya. Melihat halaman 243 sebelum dan sesudah (dengan DBCC PAGE
tidak berdokumen ):
-- ******** Page 243, before: ******** Slot 0 Offset 0x60 Length 12 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 12 Memory Dump @0x000000E34B9FA060 0000000000000000: 10000900 01000000 78020000 .. .....x... Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4 RowID = 1 Slot 0 Column 2 Offset 0x8 Length 1 Length (physical) 1 filler = x -- ******** Page 243, after: ******** Slot 0 Offset 0x60 Length 9 Record Type = FORWARDING_STUB Record Attributes = Record Size = 9 Memory Dump @0x000000E34B9FA060 0000000000000000: 04280100 00010078 01 .(.....x. Forwarding to = file 1 page 296 slot 376
Kemudian jika kita melihat target pointer, halaman 296, slot 376, kita melihat:
Slot 376 Offset 0x8ca Length 34 Record Type = FORWARDED_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Record Size = 34 Memory Dump @0x000000E33BBFA8CA 0000000000000000: 32001100 01000000 78010000 00000000 00030000 2.......x........... 0000000000000014: 01002280 0004f300 00000100 0000 .."...ó....... Forwarded from = file 1 page 243 slot 0 Slot 376 Column 67108865 Offset 0x4 Length 0 Length (physical) 4 DROPPED = NULL Slot 376 Column 2 Offset 0x8 Length 1 Length (physical) 1 filler = x Slot 376 Column 1 Offset 0x9 Length 8 Length (physical) 8 RowID = 1
Ini jelas merupakan perubahan yang sangat mengganggu pada struktur tabel. (Dan pengamatan samping yang menarik:urutan fisik kolom, RowID dan pengisi, telah dibalik pada halaman.) Ruang cadangan melonjak dari 136 KB menjadi 264 KB, dan fragmentasi rata-rata meningkat sedikit dari 33,3% menjadi 40%. Ruang ini tidak dapat dipulihkan dengan pembangunan kembali, online atau tidak, atau penataan ulang, dan – seperti yang akan kita lihat segera – ini bukan karena tabelnya terlalu kecil untuk dimanfaatkan.
Catatan:ini benar bahkan dalam versi terbaru SQL Server 2016 – sementara semakin banyak operasi seperti ini telah ditingkatkan menjadi operasi metadata-saja di versi modern, yang ini belum diperbaiki, meskipun jelas bisa jadi – sekali lagi, terutama dalam kasus di mana kolomnya adalah kolom IDENTITAS, yang tidak dapat diperbarui menurut definisi.
Melakukan operasi dengan sintaks ALTER COLUMN / ONLINE baru, yang saya bicarakan tahun lalu, menghasilkan beberapa perbedaan:
-- drop / re-create here ALTER TABLE dbo.FirstTest ALTER COLUMN RowID bigint WITH (ONLINE = ON);
Sekarang sebelum dan sesudah menjadi:
-- before: lo_page hi_page page_count ------- ------- ---------- 243 303 17 -- after: lo_page hi_page page_count ------- ------- ---------- 307 351 17
Dalam hal ini, ini masih merupakan operasi ukuran data, tetapi halaman yang ada disalin dan dibuat ulang karena opsi ONLINE. Anda mungkin bertanya-tanya mengapa, ketika kami mengubah ukuran kolom sebagai operasi ONLINE, tabel mampu menjejalkan lebih banyak data ke dalam jumlah halaman yang sama? Setiap halaman sekarang lebih padat (lebih sedikit baris tetapi lebih banyak data per halaman), dengan biaya pencar – fragmentasi berlipat ganda dari 33,3% menjadi 66,7%. Ruang yang digunakan menunjukkan lebih banyak data dalam ruang cadangan yang sama (dari 72 KB / 136 KB menjadi 96 KB / 136 KB).
Dan pada Skala yang Lebih Besar?
Mari lepaskan tabel, buat ulang, dan isi dengan lebih banyak data:
CREATE TABLE dbo.FirstTest ( RowID INT IDENTITY(1,1), filler CHAR(1) NOT NULL DEFAULT 'x' ); GO INSERT dbo.FirstTest WITH (TABLOCKX) (filler) SELECT TOP (5000000) 'x' FROM sys.all_columns AS c1 CROSS JOIN sys.all_columns AS c2;
Sejak awal, kami sekarang memiliki 8.657 halaman, tingkat fragmentasi 0,09%, dan ruang yang digunakan adalah 69.208 KB / 69.256 KB.
Jika kita mengubah tipe data menjadi bigint, kita melompat ke 25.630 halaman, fragmentasi berkurang menjadi 0,06%, dan ruang yang digunakan adalah 205.032 KB / 205.064 KB. Rekondisi online tidak mengubah apa pun, begitu pula reorg. Seluruh proses, termasuk pembuatan ulang, membutuhkan waktu sekitar 97 detik di komputer saya (populasi data membutuhkan waktu 2 detik).
Jika kita mengubah tipe data menjadi bigint menggunakan ONLINE, bump hanya menjadi 11.140 halaman, fragmentasi menjadi 85,5%, dan ruang yang digunakan adalah 89.088 KB / 89160 KB. Pembuatan ulang dan penataan ulang online masih tidak mengubah apa pun. Kali ini, seluruh proses hanya membutuhkan waktu sekitar satu menit. Jadi sintaks baru pasti mengarah ke operasi yang lebih cepat dan lebih sedikit ruang disk tambahan, tetapi fragmentasi tinggi. Saya akan mengambilnya.
Berikutnya
Saya yakin Anda melihat tes saya di atas, dan bertanya-tanya tentang beberapa hal. Yang paling penting, mengapa tabel itu tumpukan? Saya ingin menyelidiki apa yang sebenarnya terjadi pada struktur halaman dan jumlah halaman tanpa indeks, kunci, atau batasan yang mengaburkan detailnya. Anda mungkin juga bertanya-tanya mengapa perubahan ini begitu mudah – dalam skenario di mana Anda harus mengubah kolom IDENTITAS yang sebenarnya, itu mungkin juga kunci utama yang dikelompokkan, dan memiliki dependensi kunci asing di tabel lain. Ini pasti memperkenalkan beberapa cegukan pada prosesnya. Kita akan melihat lebih dekat hal-hal ini di postingan berikutnya dalam seri ini.
—
[ Bagian 1 | Bagian 2 | Bagian 3 | Bagian 4 ]