Mengapa ruang data tabel memakan 4x ukuran data mentah?

18

Saya punya meja dengan 490 M baris dan 55 GB ruang tabel, jadi sekitar 167 byte per baris. Tabel ini memiliki tiga kolom: a VARCHAR(100), a DATETIME2(0), dan a SMALLINT. Panjang rata-rata teks di VARCHARlapangan adalah sekitar 21,5, sehingga data mentah harus sekitar 32 byte per baris: 22 + 2 untuk VARCHAR, 6 untuk DATETIME2, dan 2 untuk bilangan bulat 16-bit.

Perhatikan bahwa ruang di atas hanya data, bukan indeks. Saya menggunakan nilai yang dilaporkan di bawah Properties | Penyimpanan | Umum | Ruang data.

Tentu saja harus ada beberapa overhead, tetapi 135 byte per baris sepertinya banyak, terutama untuk tabel besar. Kenapa ini bisa terjadi? Adakah yang melihat pengganda serupa? Faktor-faktor apa yang dapat memengaruhi jumlah ruang ekstra yang dibutuhkan?

Sebagai perbandingan, saya mencoba membuat tabel dengan dua INTbidang dan 1 baris M. Ruang data yang dibutuhkan adalah 16,4 MB: 17 byte per baris, dibandingkan dengan 8 byte data mentah. Tabel tes lain dengan INTdan VARCHAR(100)diisi dengan teks yang sama seperti tabel nyata menggunakan 39 byte per baris (44 K baris), di mana saya harapkan 28 ditambah sedikit.

Jadi tabel produksi memiliki overhead yang jauh lebih besar. Apakah ini karena lebih besar? Saya berharap ukuran indeks kira-kira N * log (N), tapi saya tidak melihat mengapa ruang yang diperlukan untuk data aktual menjadi non-linear.

Terima kasih sebelumnya untuk petunjuk apa pun!

EDIT:

Semua bidang yang tercantum adalah NOT NULL. Tabel sebenarnya memiliki PK berkerumun di VARCHARlapangan dan DATETIME2lapangan, dalam urutan itu. Untuk dua tes, yang pertama INTadalah PK (berkerumun).

Jika penting: tabel adalah catatan hasil ping. Bidangnya adalah URL, tanggal / waktu ping, dan latensi dalam milidetik. Data secara konstan ditambahkan, dan tidak pernah diperbarui, tetapi data dihapus secara berkala untuk menguranginya menjadi hanya beberapa catatan per jam per URL.

EDIT:

Jawaban yang sangat menarik di sini menunjukkan bahwa, untuk indeks dengan banyak membaca dan menulis, membangun kembali mungkin tidak bermanfaat. Dalam kasus saya, ruang yang dikonsumsi adalah masalah, tetapi jika kinerja menulis lebih penting, orang mungkin lebih baik dengan indeks yang lemah.

Jon dari Semua Perdagangan
sumber

Jawaban:

11

Setelah diskusi dalam komentar pada pertanyaan awal, tampaknya dalam kasus ini ruang yang hilang disebabkan oleh pilihan kunci berkerumun, yang telah menyebabkan fragmentasi besar-besaran.

Selalu patut memeriksa keadaan fragmentasi melalui sys.dm_db_index_physical_stats dalam situasi ini.

Sunting: Mengikuti pembaruan dalam komentar

Kepadatan halaman rata-rata (sebelum membangun kembali indeks berkerumun) adalah 24%, yang sangat cocok dengan pertanyaan awal. Halaman itu hanya 1/4 penuh, jadi ukuran totalnya 4x ukuran data mentah.

Mark Storey-Smith
sumber
7

Struktur pada disk memiliki overhead:

  • tajuk baris
  • null bitmap + pointer
  • offset kolom panjang variabel
  • pointer versi baris (opsional)
  • ...

Mengambil 2 x 4 byte kolom int, Anda miliki

  • Header baris 4 byte
  • 2 byte pointer ke bitmap NULL
  • 8 byte untuk 2 kolom int
  • 3 byte NULL bitmap

Wow, 17 byte!

Anda dapat melakukan hal yang sama untuk tabel tes kedua yang memiliki lebih banyak overhead seperti yang asli:

  • 2 byte untuk hitungan kolom panjang variabel
  • 2 byte per kolom panjang variabel

Kenapa bedanya? Selain itu (saya tidak akan menautkan ini)

  • apakah Anda pernah membangun kembali indeks untuk mendefrag mereka?
  • menghapus jangan mengambil kembali ruang
  • halaman data akan terpecah jika Anda memasukkan ke tengah
  • pembaruan dapat menyebabkan forward pointer (meninggalkan celah)
  • baris meluap
  • kolom varchar dihapus tanpa indeks membangun kembali atau DBCC CLEANTABLE
  • heap atau tabel (heap tidak memiliki indeks berkerumun = catatan tersebar di seluruh)
  • Level isolasi RCSI (tambahan 14 byte per baris)
  • spasi tambahan (SET ANSI_PADDING AKTIF secara default) di varchar. Gunakan DATALENGTH untuk checl, bukan LEN
  • Jalankan sp_spaceused with @updateusage = 'true'
  • ...

Lihat ini: SQL Server: Cara membuat tabel yang mengisi satu halaman 8 KB?

Dari SO:

gbn
sumber
Sampel kolom int 2x4 byte tidak 100% benar. Anda akan memiliki header baris 4 byte (2 status byte dan 2 byte untuk ukuran data panjang tetap). Maka Anda akan memiliki 2x4 byte untuk data. Dua byte untuk jumlah kolom dan satu byte untuk bitmap nol, memberikan total catatan panjang 15 byte, bukan 17.
Mark S. Rasmussen
@ Mark S. Rasmussen: Di mana Anda mendapatkan "2 byte untuk ukuran data panjang tetap"? MSDN? Dan bitmap nol selalu 3 byte: sqlskills.com/blogs/paul/post/… + msdn.microsoft.com/en-us/library/ms178085%28v=sql.90%29.aspx
gbn
Wow, detail luar biasa! Saya menghitung bidang panjang VARCHARdalam estimasi saya di atas, tetapi tidak untuk jumlah kolom. Tabel ini tidak memiliki bidang NULLable (seharusnya menyebutkan itu), apakah masih mengalokasikan byte untuknya?
Jon of All Trades
Apakah indeks pembangunan kembali mempengaruhi bagian data dari ruang yang dibutuhkan? Mungkin membangun kembali indeks cluster akan. Sisipan memang terjadi di tengah, banyak, meskipun jika saya bertukar urutan bidang pengelompokan yang akan berhenti. Sebagian besar sisanya tidak boleh berlaku dalam kasus ini, tapi itu referensi yang bagus untuk kasus umum. Saya akan memeriksa tautan Anda. Barang bagus!
Jon of All Trades
1
@ gbn 2 byte untuk ukuran data panjang tetap adalah bagian dari header baris 4 byte yang Anda sebutkan. Ini adalah pointer yang menunjuk ke akhir bagian panjang data tetap / awal jumlah kolom / bitmap nol. Bitmap NULL tidak selalu tiga byte. Jika Anda memasukkan jumlah kolom, maka itu akan menjadi minimal tiga byte, tetapi mungkin lebih - Saya membagi bitmap dan jumlah kolom dalam deskripsi saya. Juga, bitmap NULL tidak selalu ada, meskipun akan dalam kasus ini.
Mark S. Rasmussen
5

Apakah tipe data berubah dari waktu ke waktu? Apakah kolom panjang variabel telah dihapus? Apakah indeks telah sering didefragmentasi tetapi tidak pernah dibangun kembali? Apakah banyak baris telah dihapus atau banyak kolom panjang variabel telah diperbarui secara signifikan? Diskusi yang bagus di sini .

Aaron Bertrand
sumber
Saya 97% yakin bahwa saya belum mengubah tipe data atau menghapus bidang. Jika saya melakukannya, itu akan sangat awal ketika tabel memiliki baris jauh lebih sedikit. Tidak ada penghapusan atau pembaruan, data hanya ditambahkan.
Jon of All Trades
Koreksi: ada yang menghapus, dan cukup sedikit. Tabelnya memiliki pertumbuhan bersih yang cukup besar, jadi saya membayangkan ruang ini akan cepat digunakan kembali.
Jon of All Trades
Dengan banyak penghapusan data dapat digunakan kembali atau tidak. Apa kunci pengelompokan tabel? Apakah sisipan ada di tengah meja atau di akhir?
mrdenny
Kunci yang dikelompokkan adalah majemuk, pada bidang VARCHARdan DATETIME2, dalam urutan itu. Sisipan akan didistribusikan secara merata untuk bidang pertama. Untuk bidang kedua, nilai-nilai baru dan akan selalu lebih besar daripada yang ada.
Jon of All Trades