Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Meminimalkan dampak pelebaran kolom IDENTITAS – bagian 1

[ 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 ]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Model Data untuk Melacak Kepemilikan Anda yang Paling Berharga

  2. Cara Memperbarui Kolom Berdasarkan Kolom Lain di SQL

  3. Apa Pemicu dalam SQL dan bagaimana menerapkannya?

  4. Model Data Perawatan Hewan Peliharaan

  5. Cara Menyimpan Jadwal Karyawan di Database