membangun kembali pada indeks berkerumun, mengapa datasize menyusut?

10

Ketika kami melakukan pembangunan kembali pada indeks berkerumun di atas meja yang memiliki sekitar 15gb data di dalamnya dan data menyusut menjadi 5gb, bagaimana ini bisa terjadi? "Data" apa yang dihapus?

Ukuran data yang saya maksud adalah kolom "data" dari DBCC sp_spaceused

Sebelum Membangun Kembali pada indeks berkerumun:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    39169656 KB 15857960 KB 22916496 KB 395200 KB

Setelah dibangun kembali pada indeks berkerumun:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    29076736 KB 5867048 KB  22880144 KB 329544 KB

TSQL untuk membangun kembali:

USE [DAX5TEST]
GO
ALTER INDEX [I_212RECID] ON [dbo].[LEDGERJOURNALTRANS] REBUILD PARTITION = ALL WITH ( PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, ONLINE = ON, SORT_IN_TEMPDB = OFF, DATA_COMPRESSION = PAGE, FILLFACTOR = 85 )
GO
Daniel Björk
sumber
Apakah Anda menentukan ukuran data dari ukuran file?
JNK
Ukuran data yang saya maksud adalah kolom "data" dari DBCC sp_spaceused
Daniel Björk
Itu akan menjadi kolom "data" dari EXEC sp_spaceused.
RLF
1
Apakah setiap orang melewatkan OP yang menggunakan kompresi halaman = diaktifkan dalam skrip rekondisi dan saya kira itu tidak terjadi sebelumnya. Daniel dapatkah Anda mengonfirmasi?
Shanky
1
@Shanky: ALTER INDEXPernyataan itu sepertinya dihasilkan oleh kode (karena menyertakan banyak opsi dalam pengaturan default), jadi saya curiga itu dibuat dari opsi indeks yang ada. Tapi Anda benar: jika kompresi tidak diaktifkan pada indeks berkerumun sebelum ini dijalankan maka itu pasti akan menjelaskan sebagian besar pengurangan jejak data. (lagi: Daniel, dapatkah Anda mengkonfirmasi dengan satu atau lain cara?)
David Spillett

Jawaban:

16

Ketika tabel memiliki indeks berkerumun, indeks adalah data tabel (jika tidak, Anda memiliki tabel tipe tumpukan). Pembangunan kembali indeks berkerumun (sebenarnya indeks apa pun, tetapi ruang tidak akan dihitung sebagai "data" untuk indeks yang tidak berkerumun) akan menghasilkan halaman yang digunakan sebagian digabung menjadi bentuk yang lebih lengkap.

Saat Anda memasukkan data ke dalam indeks (berkerumun atau sebaliknya) di halaman daun urutan indeks dibuat sesuai kebutuhan dan Anda hanya akan memiliki satu halaman parsial: satu di bagian akhir. Saat Anda memasukkan data di luar urutan indeks, halaman perlu dipisah agar data tersebut pas di tempat yang tepat: Anda berakhir dengan dua halaman yang kira-kira setengah penuh dan baris baru masuk ke salah satunya. Seiring waktu, hal ini dapat terjadi banyak, menghabiskan cukup banyak ruang ekstra, meskipun pada tahap selanjutnya memasukkan akan mengisi beberapa celah. Halaman non-daun akan melihat efek yang sama juga, tetapi halaman data sebenarnya jauh lebih signifikan dalam ukuran daripada mereka.

Menghapus juga dapat menghasilkan sebagian halaman. Jika Anda menghapus semua baris dalam halaman itu akan dihitung sebagai "tidak digunakan" tetapi jika memiliki satu atau lebih baris data yang tersisa itu masih dihitung sebagai sedang digunakan. Bahkan jika hanya ada satu baris menggunakan 10 byte dalam satu halaman, halaman itu dihitung sebagai 8192 byte dalam hitungan ruang yang digunakan. Sekali lagi sisipan di masa depan mungkin mengisi beberapa celah.

Untuk baris panjang variabel, pembaruan juga dapat memiliki efek yang sama: karena satu baris semakin kecil itu dapat meninggalkan ruang di halamannya yang nantinya tidak mudah untuk digunakan kembali, dan jika sebuah baris di halaman yang hampir penuh tumbuh lebih lama, hal itu dapat memaksa pemisahan halaman .

SQL Server tidak menghabiskan waktu untuk menormalkan data dengan mengatur ulang bagaimana halaman digunakan, sampai secara eksplisit diberitahu seperti indeks Anda membangun kembali pesanan, karena latihan pengumpulan sampah seperti itu bisa menjadi mimpi buruk kinerja.

Saya menduga ini adalah apa yang Anda lihat, meskipun saya akan mengatakan bahwa memiliki cukup ruang yang dialokasikan untuk ~ 2,7 kali jumlah data yang benar-benar diperlukan adalah kasus yang sangat buruk. Mungkin menyiratkan bahwa Anda memiliki sesuatu yang acak sebagai salah satu kunci penting dalam indeks (mungkin kolom UUID) yang berarti bahwa baris baru tidak mungkin ditambahkan dalam urutan indeks, dan / atau bahwa sejumlah besar penghapusan telah terjadi baru-baru ini.

Contoh Pemisahan Halaman

Memasukkan dalam urutan indeks dengan baris panjang tetap yang empat pas ke halaman:

Start with one empty page: 
        [__|__|__|__]
Add the first item in index order:
        [00|__|__|__]
Add the next three
        [00|02|04|06]
Adding the next will result in a new page:
        [00|02|04|06] [08|__|__|__]
And so on...
        [00|02|04|06] [08|10|12|14] [16|18|__|__]

Sekarang untuk menambahkan baris di luar urutan indeks (inilah sebabnya saya menggunakan angka genap hanya di atas): Menambahkan 11berarti memperpanjang halaman kedua itu (tidak mungkin karena ukurannya tetap), memindahkan semuanya di atas 11 ke atas (terlalu mahal untuk indeks besar) atau membelah halaman seperti:

[00|02|04|06] [08|10|11|__] [12|14|__|__] [16|18|__|__]

Dari sini, menambahkan 13dan 17tidak akan menghasilkan perpecahan karena saat ini ada ruang di halaman yang relevan:

[00|02|04|06] [08|10|11|__] [12|13|14|__] [16|17|18|__]

tetapi menambahkan 03 akan:

[00|02|03|__] [04|06|__|__] [08|10|11|__] [12|13|14|__] [16|17|18|__]

Seperti yang Anda lihat, setelah operasi penyisipan tersebut, kami saat ini memiliki 5 halaman data yang dialokasikan yang dapat memenuhi total 20 baris, tetapi kami hanya memiliki 14 baris di sana ("membuang-buang" 30% dari ruang).

Rekondisi dengan opsi default (lihat di bawah tentang "faktor pengisian") akan menghasilkan:

[00|02|03|04] [06|08|10|11] [12|13|14|16] [17|18|__|__]

menyimpan satu halaman dalam contoh sederhana ini. Sangat mudah untuk melihat bagaimana penghapusan dapat memiliki efek yang sama seperti sisipan out-of-index.

Mitigasi

Jika Anda mengharapkan data datang dalam urutan yang cukup acak sehubungan dengan urutan indeks, Anda dapat menggunakan FILLFACTORopsi saat membuat atau membangun kembali indeks untuk memberitahu SQL Server untuk secara artifisial membuat celah untuk mengisi - mengurangi pemisahan halaman dalam jangka panjang tetapi mengambil lebih banyak ruang pada awalnya. Tentu saja salah nilai ini bisa membuat segalanya jauh lebih buruk daripada membuat situasi lebih baik, jadi tangani dengan hati-hati.

Pemisahan halaman, terutama pada indeks berkerumun, dapat memiliki implikasi kinerja untuk menyisipkan / memperbarui sehingga FILLFACTORkadang-kadang di-tweak karena alasan itu alih-alih masalah penggunaan ruang dalam database yang melihat banyak aktivitas penulisan (tetapi untuk sebagian besar aplikasi, di mana bacaan lebih banyak daripada menulis dengan beberapa urutan besarnya, Anda biasanya lebih baik meninggalkan faktor pengisian sebesar 100% kecuali untuk kasus tertentu seperti di mana Anda memiliki indeks di atas kolom dengan konten acak yang efektif).

Saya berasumsi DB nama besar lainnya memiliki opsi yang serupa, jika Anda memerlukan level kontrol ini juga.

Memperbarui

Mengenai ALTER INDEXpernyataan yang ditambahkan ke pertanyaan setelah saya mulai mengetik di atas: Saya berasumsi bahwa opsi yang sama seperti ketika indeks pertama kali dibangun (atau terakhir dibangun kembali) tetapi jika tidak maka opsi kompresi bisa sangat signifikan jika ditambahkan ini waktu sekitar. Juga dalam pernyataan itu fillfactor diatur ke 85% bukan 100% sehingga setiap halaman daun akan ~ 15% kosong segera setelah pembangunan kembali.

David Spillett
sumber
2
+1 Jika faktor isi halaman kurang dari 100%, misalnya jika faktor isi halaman adalah 50%, indeks berkerumun yang baru dibangun kembali ( tabel ) akan dua kali lebih besar jika itu dibangun kembali dengan faktor pengisian 100%.
Max Vernon
6

Ketika Anda membangun kembali indeks, itu secara harfiah menempatkan semua data ke halaman baru. Yang saya duga terjadi adalah bahwa Anda menghapus banyak data sebelum membangun kembali, misalnya menghapus kolom, memperbarui kolom lebar variabel untuk memiliki lebih sedikit data, mengubah ukuran kolom lebar tetap, atau menghapus banyak baris. Salah satu dari operasi ini dapat meninggalkan banyak ruang kosong pada halaman, yang tidak akan direklamasi sampai dibangun kembali. Kolom "data" di sp_spaceusedbukan mengukur data aktual, tetapi jumlah 8K halaman yang digunakan untuk menyimpan data. Halaman-halaman itu sekarang lebih penuh karena pembangunan kembali, sehingga jumlah data yang sama pas pada jumlah halaman yang lebih kecil.

Aaron Bertrand
sumber
5

The sp_spaceusedprosedur yang tersimpan tidak memeriksa ukuran total culmulative dari baris dalam database. Ini melaporkan ukuran ruang yang dialokasikan untuk menampung data dalam ukuran kumulatif dari luasan yang dialokasikan untuk data.

Jika ada freespace yang signifikan tersedia, seperti dari banyak baris yang dihapus, maka pembangunan kembali indeks berkerumun akan memadatkan ruang dalam halaman dan memperluas menjadi lebih efisien (yaitu lebih kecil) untuk alasan kinerja.

Jadi, tidak ada data yang harus dibuang, tetapi proses pembangunan kembali membuat ruang kosong yang tertanam di halaman data tersedia lagi.

RLF
sumber