Saya telah membuat tabel berikut:
CREATE TABLE dbo.TestStructure
(
id INT NOT NULL,
filler1 CHAR(36) NOT NULL,
filler2 CHAR(216) NOT NULL
);
dan kemudian membuat indeks berkerumun:
CREATE CLUSTERED INDEX idx_cl_id
ON dbo.TestStructure(id);
Berikutnya saya mengisinya dengan 30 baris setiap ukuran adalah 256 byte (berdasarkan tabel deklarasi):
DECLARE @i AS int = 0;
WHILE @i < 30
BEGIN
SET @i = @i + 1;
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (@i, 'a', 'b');
END;
Sekarang berdasarkan informasi yang saya baca di "Pelatihan Kit (Ujian 70-461): Meminta Microsoft SQL Server 2012 (Itzik Ben-Gan)" buku:
SQL Server secara internal mengatur data dalam file data di halaman. Halaman adalah unit 8 KB dan milik satu objek; misalnya, ke tabel atau indeks. Halaman adalah unit terkecil dari membaca dan menulis. Halaman disusun lebih lanjut menjadi luasan. Luasnya terdiri dari delapan halaman berturut-turut. Halaman dari batas tertentu dapat dimiliki oleh satu objek atau beberapa objek. Jika halaman milik beberapa objek, maka luasnya disebut tingkat campuran; jika halaman milik satu objek, maka luasnya disebut tingkat seragam. SQL Server menyimpan delapan halaman pertama objek dalam luasan campuran. Ketika suatu objek melebihi delapan halaman, SQL Server mengalokasikan luasan seragam yang seragam untuk objek ini. Dengan organisasi ini, benda-benda kecil menghabiskan lebih sedikit ruang dan benda-benda besar kurang terfragmentasi.
Jadi di sini saya memiliki halaman 8KB luas campuran pertama, diisi dengan 7680 byte (saya telah memasukkan 30 kali 256 byte ukuran baris, jadi 30 * 256 = 7680), untuk memeriksa ukuran saya telah menjalankan ukuran periksa proc - mengembalikan hasil berikut
index_type_desc: CLUSTERED INDEX
index_depth: 1
index_level: 0
page_count: 1
record_count: 30
avg_page_space_used_in_percent: 98.1961947121324
name : TestStructure
rows : 30
reserved : 16 KB
data : 8 KB
index_size : 8 KB
unused : 0 KB
Jadi 16 KB dicadangkan untuk tabel, halaman 8 KB pertama adalah untuk halaman Root IAM, yang kedua adalah untuk halaman penyimpanan data daun yang 8KB dengan pekerjaan ~ 7,5 KB, sekarang ketika saya memasukkan baris baru dengan 256 Byte:
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (1, 'a', 'b');
itu tidak disimpan di halaman yang sama meskipun memiliki ruang 256 byte (7680 b + 256 = 7936 yang masih lebih kecil dari 8KB), halaman data baru dibuat, tetapi baris baru itu bisa masuk pada halaman lama yang sama , mengapa SQL Server membuat halaman baru ketika itu bisa menghemat ruang dan mencari waktu membeli memasukkannya di halaman yang ada?
Catatan: hal yang sama terjadi pada heap index.
sumber
Meskipun benar bahwa SQL Server menggunakan 8k (8192 byte) halaman data untuk menyimpan 1 atau lebih baris, setiap halaman data memiliki beberapa overhead (96 byte), dan setiap baris memiliki beberapa overhead (setidaknya 9 byte). 8192 byte bukan murni data.
Untuk pemeriksaan yang lebih rinci tentang cara kerjanya, silakan lihat jawaban saya untuk pertanyaan DBA.SE berikut:
SUM dari DATALENGTHs tidak cocok dengan ukuran tabel dari sys.allocation_units
Dengan menggunakan informasi dalam jawaban yang tertaut itu, kita bisa mendapatkan gambaran yang lebih jelas tentang ukuran baris aktual:
Menggunakan
DBCC PAGE
konfirmasi perhitungan saya dengan menunjukkan:Record Size 263
(untuktempdb
), danRecord Size 277
(untuk database yang diatur keALLOW_SNAPSHOT_ISOLATION ON
).Sekarang, dengan 30 baris, yaitu:
TANPA info versi
30 * 263 akan memberi kita 7890 byte. Kemudian tambahkan 96 byte header halaman untuk 7986 byte yang digunakan. Terakhir, tambahkan 60 byte (2 per baris) dari array slot untuk total 8.046 byte yang digunakan pada halaman, dan 146 sisanya. Menggunakan
DBCC PAGE
konfirmasi perhitungan saya dengan menunjukkan:m_slotCnt 30
(mis. jumlah baris)m_freeCnt 146
(yaitu jumlah byte yang tersisa di halaman)m_freeData 7986
(yaitu data + header halaman - 7890 + 96 - array slot tidak diperhitungkan dalam perhitungan byte "bekas")DENGAN info versi
30 * 277 byte dengan total 8310 byte. Tapi 8310 lebih dari 8192, dan itu bahkan tidak memperhitungkan tajuk halaman 96 byte atau 2 byte per baris slot array (30 * 2 = 60 byte) yang seharusnya memberi kita hanya 8.036 byte yang dapat digunakan untuk baris.
TAPI, bagaimana dengan 29 baris? Itu akan memberi kita 8.033 byte data (29 * 277) + 96 byte untuk header halaman + 58 byte untuk array slot (29 * 2) sama dengan 8187 byte. Dan itu akan meninggalkan halaman dengan 5 byte tersisa (8192 - 8187; tidak dapat digunakan, tentu saja). Menggunakan
DBCC PAGE
konfirmasi perhitungan saya dengan menunjukkan:m_slotCnt 29
(mis. jumlah baris)m_freeCnt 5
(yaitu jumlah byte yang tersisa di halaman)m_freeData 8129
(yaitu data + header halaman - 8033 + 96 - array slot tidak diperhitungkan dalam perhitungan byte "bekas")Tentang Tumpukan
Tumpukan mengisi halaman data sedikit berbeda. Mereka mempertahankan perkiraan yang sangat kasar tentang jumlah ruang yang tersisa di halaman. Ketika melihat output DBCC, melihat baris untuk:
PAGE HEADER: Allocation Status PFS (1:1)
. Anda akan melihatVALUE
sesuatu menunjukkan sepanjang garis0x60 MIXED_EXT ALLOCATED 0_PCT_FULL
(ketika saya melihat tabel Clustered) atau0x64 MIXED_EXT ALLOCATED 100_PCT_FULL
ketika melihat tabel Heap. Ini dievaluasi per Transaksi, jadi melakukan sisipan individual seperti tes yang dilakukan di sini dapat menunjukkan hasil yang berbeda antara tabel Clustered dan Heap. Namun, melakukan operasi DML tunggal untuk semua 30 baris akan mengisi tumpukan seperti yang diharapkan.Namun, tidak ada detail mengenai Heaps yang secara langsung memengaruhi tes khusus ini karena kedua versi tabel tersebut cocok dengan 30 baris dengan hanya 146 byte yang tersisa. Itu tidak cukup ruang untuk baris lain, terlepas dari Clustered atau Heap.
Harap diingat bahwa tes ini agak sederhana. Menghitung ukuran aktual sebuah baris bisa menjadi sangat rumit tergantung pada berbagai faktor, seperti
SPARSE
:, Kompresi Data, data LOB, dll.Untuk melihat detail halaman data, gunakan kueri berikut:
** Nilai "info versi" 14 byte akan ditampilkan jika database Anda disetel ke
ALLOW_SNAPSHOT_ISOLATION ON
atauREAD_COMMITTED_SNAPSHOT ON
.sumber
tempdb
? Saya dapat mereproduksi angka persis yang disediakan oleh OP.ALLOW_SNAPSHOT_ISOLATION
. Saya juga baru saja mencobatempdb
dan melihat bahwa "info versi" tidak ada, maka 30 baris cocok. Saya akan memperbarui untuk menambahkan info baru.Struktur sebenarnya dari halaman data cukup kompleks. Meskipun secara umum dinyatakan bahwa 8060 byte per halaman tersedia untuk data pengguna, ada beberapa overhead tambahan yang tidak dihitung di mana saja yang menghasilkan perilaku ini.
Namun, Anda mungkin telah memperhatikan bahwa SQL Server benar-benar memberi Anda petunjuk bahwa baris ke-31 tidak akan masuk ke halaman. Agar baris berikutnya masuk ke halaman yang sama,
avg_page_space_used_in_percent
nilainya harus di bawah 100% - (100/31) = 96.774194, dan dalam kasus Anda, itu jauh di atas itu.PS Saya yakin saya melihat penjelasan detail hingga ke byte struktur data di salah satu buku "SQL Server Internals" karya Kalen Delaney, tapi itu hampir 10 tahun yang lalu, jadi mohon maafkan saya karena tidak mengingat lebih detail. Selain itu, struktur halaman cenderung berubah dari versi ke versi.
sumber