Temukan ukuran terkompresi dari semua tabel dalam database

12

Di Dynamics AX ada mekanisme caching di mana tabel dapat dikonfigurasi untuk dimuat ke dalam memori dan di-cache. Cache ini terbatas pada jumlah tertentu dari KB untuk mencegah masalah memori. Pengaturan yang saya bicarakan dipanggil entiretablecachedan memuat seluruh tabel dalam memori segera setelah satu catatan diminta.

Hingga baru-baru ini kami mengandalkan beberapa skrip untuk memverifikasi ukuran tabel yang memiliki pengaturan ini untuk melihat apakah ukuran tabel di atas batas ini.

Namun sekarang, kompresi ikut berperan dan hal-hal seperti sp_spaceused atau sys.allocation_units tampaknya melaporkan ruang yang sebenarnya digunakan oleh data terkompresi.

Jelas, server aplikasi bekerja dengan data yang tidak terkompresi sehingga ukuran data pada disk di SQL Server tidak relevan. Saya membutuhkan ukuran sebenarnya dari data yang tidak terkompresi.

Saya tahu sp_estimate_data_compression_savings tetapi seperti namanya, ini hanya perkiraan.
Saya lebih suka memiliki ukuran yang setepat mungkin.

Satu-satunya cara saya bisa memikirkan adalah beberapa SQL dinamis yang berbelit-belit membuat tabel terkompresi dengan struktur yang sama dengan tabel terkompresi, memasukkan data terkompresi dalam tabel bayangan itu dan kemudian memeriksa ukuran tabel bayangan itu.
Tak perlu dikatakan, ini agak membosankan dan perlu beberapa saat untuk berjalan pada database beberapa ratus GB.

Powershell bisa menjadi pilihan, tapi saya tidak ingin mengulangi semua tabel untuk melakukan select * pada mereka untuk memeriksa ukuran dalam skrip karena itu hanya akan membanjiri cache dan mungkin akan memakan waktu lama juga.

Singkatnya, saya butuh cara untuk mendapatkan ukuran untuk setiap tabel karena akan pernah terkompresi dan dengan fragmentasi keluar dari persamaan seperti yang disajikan ke aplikasi, jika itu mungkin. Saya terbuka untuk pendekatan yang berbeda, T-SQL lebih disukai tetapi saya tidak menentang Powershell atau pendekatan kreatif lainnya.

Asumsikan buffer dalam aplikasi adalah ukuran data. Bigint selalu berukuran bigint, dan tipe data karakter adalah 2 byte per karakter (unicode). Data BLOB mengambil ukuran data juga, enum pada dasarnya adalah int dan data numerik adalah numerik (38,12), datetime adalah ukuran datetime. Juga, tidak ada NULLnilai, mereka disimpan sebagai string kosong, 1900-01-01atau nol.

Tidak ada dokumentasi tentang bagaimana ini diterapkan, tetapi asumsi didasarkan pada beberapa pengujian dan skrip yang digunakan oleh PFE dan tim dukungan (yang juga mengabaikan kompresi tampaknya, karena cek dibangun dalam aplikasi dan aplikasi tidak dapat memberitahu jika data yang mendasarinya dikompresi) yang juga memeriksa ukuran tabel. Tautan ini misalnya menyatakan:

Hindari menggunakan cache Seluruh Tabel untuk tabel besar (di AX 2009 lebih dari 128 KB atau 16 halaman, di AX 2012 atas pengaturan aplikasi 'seluruh tabel ukuran cache' [default: 32KB, atau 4 halaman]) - alih-alih pindah ke caching rekaman.

Tom V - coba topanswers.xyz
sumber
3
Ini hacky, tetapi mungkin salinan yang dipulihkan dengan kompresi dinonaktifkan akan menjadi yang paling tepat. Kemudian Anda juga menguji pemulihan, yang membuat Anda terlihat seperti DBA TOP 1.
Erik Darling
Percayalah itu akan menjadi taruhan terbaik Anda. Mungkin ada cara untuk semacam mencoba dan melakukan perhitungan. Berapa banyak baris menurut tipe data panjang dan kolom yang dikalikan dikalikan kemudian tambahkan dalam indeks, dll. Ini jauh lebih berfungsi daripada membuat skrip pemulihan dan menonaktifkan kompresi yang disarankan @sp_BlitzErik di atas. Dan siapa yang tidak ingin menjadi DBA TOP 1?
Mike Walsh
SUM (datalength ()) untuk semua kolom mendapatkan ukuran data yang tidak terkompresi?
Tapakah Ua
@sp_BlitzErik Itu bisa menjadi jawaban, bukan komentar.
Tom V - coba topanswers.xyz

Jawaban:

7

Saya membutuhkan ukuran sebenarnya dari data yang tidak terkompresi.
...
Saya lebih suka memiliki ukuran yang setepat mungkin.

Sementara keinginan untuk informasi ini tentu dapat dimengerti, mendapatkan informasi ini, terutama dalam konteks "koreksi mungkin" lebih sulit daripada yang diharapkan semua orang karena asumsi yang salah. Apakah melakukan ide tabel bayangan terkompresi yang disebutkan dalam pertanyaan, atau saran @ sp_BlitzErik dalam komentar tentang mengembalikan DB dan membuka kompresi di sana untuk memeriksa, tidak boleh diasumsikan bahwa ukuran tabel terkompresi == ukuran data kata dalam memori di server aplikasi:

  1. Apakah semua baris dalam tabel di-cache? Atau hanya dalam jangkauan? Asumsinya di sini adalah itu semua, dan itu mungkin benar, tetapi saya pikir setidaknya harus disebutkan bahwa ini mungkin tidak terjadi (kecuali dokumentasi menyatakan sebaliknya, tapi ini adalah poin kecil, hanya saja tidak mau itu tidak disebutkan).

    Pertanyaan telah diperbarui untuk menyatakan: ya, semua baris sedang di-cache.

  2. Struktur di atas kepala

    1. Di sisi DB:
      Halaman dan overhead-baris di sisi DB: Berapa banyak baris yang cocok pada halaman ditentukan oleh banyak faktor yang dapat membuang perkiraan. Bahkan dengan nilai FILLFACTOR100 (atau 0), masih ada kemungkinan ada ruang yang tidak digunakan yang tersisa pada halaman karena tidak cukup untuk seluruh baris. Dan itu selain tajuk halaman. Juga, jika fungsi Snapshot Isolasi diaktifkan, saya yakin akan ada tambahan 13 byte per baris yang diambil oleh nomor versi, dan itu akan membuang perkiraan. Ada hal-hal kecil lainnya yang terkait dengan ukuran aktual baris (bitmap NULL, kolom panjang variabel, dll) tetapi item yang disebutkan sejauh ini seharusnya hanya menjadi titik.
    2. Di sisi server aplikasi:
      Jenis koleksi apa yang digunakan untuk menyimpan hasil yang di-cache? Saya menganggap ini adalah aplikasi .NET, jadi apakah itu DataTable? Daftar generik? A SortedDictionary? Setiap jenis koleksi memiliki jumlah pendengaran yang berbeda. Saya tidak akan mengharapkan salah satu opsi untuk selalu mencerminkan overhead Page dan Row di sisi DB, terutama pada skala (saya yakin sejumlah kecil baris mungkin tidak memiliki cukup beragam masalah, tetapi Anda tidak mencari perbedaan dalam ratusan byte atau hanya beberapa kB).
  3. Datatypes
    1. Di sisi DB:
      CHAR/ VARCHARdata disimpan pada 1 byte per karakter (mengabaikan karakter byte ganda untuk saat ini). XMLdioptimalkan agar tidak memakan ruang hampir seperti yang ditunjukkan oleh representasi teks. Datatype ini membuat kamus elemen dan nama atribut dan menggantikan referensi yang sebenarnya untuk mereka dalam dokumen dengan ID masing-masing (agak bagus, sebenarnya). Jika tidak, semua nilai string adalah UTF-16 (2 atau 4 byte per "karakter"), seperti NCHAR/ NVARCHAR. DATETIME2adalah antara 6 dan 8 byte. DECIMALadalah antara 5 dan 17 byte (tergantung pada presisi).
    2. Di sisi server aplikasi:
      Strings (sekali lagi, dengan asumsi .NET) selalu UTF-16. Tidak ada optimasi untuk string 8-bit seperti yang VARCHARberlaku. TETAPI, string juga bisa "diinternir" yang merupakan salinan bersama yang dapat direferensikan berkali-kali (tapi saya tidak tahu apakah ini berfungsi untuk string dalam koleksi, atau jika demikian, apakah itu berfungsi untuk semua jenis koleksi). XMLmungkin atau mungkin tidak disimpan dengan cara yang sama dalam memori (saya harus mencari tahu itu). DateTimeselalu 8 byte (seperti T-SQL DATETIME, tapi tidak seperti DATE, TIME, atau DATETIME2). Decimaladalah selalu 16 byte .

Semua itu untuk mengatakan: hampir tidak ada yang dapat Anda lakukan di sisi DB untuk mendapatkan ukuran jejak memori yang bahkan cukup akurat di sisi server aplikasi. Anda perlu menemukan cara untuk menginterogasi server aplikasi itu sendiri, setelah dimuat dengan tabel tertentu, jadi tahu seberapa besar itu. Dan saya tidak yakin apakah debugger akan membiarkan Anda melihat ukuran runtime dari koleksi yang diisi. Jika tidak, maka satu-satunya cara untuk mendekati adalah melalui semua baris tabel, mengalikan setiap kolom dengan ukuran .NET yang sesuai (misalnya INT= * 4, VARCHAR= DATALENGTH() * 2, NVARCHAR= DATALENGTH(), XML= 🙃, dll), tetapi itu masih menyisakan pertanyaan. dari overhead koleksi ditambah setiap elemen koleksi.

Diberi beberapa definisi baru dalam pertanyaan, orang mungkin bisa melakukan kueri berikut untuk mendapatkan lebih dekat. Dan tidak masalah apakah tabel dikompresi atau tidak, meskipun terserah masing-masing orang untuk menentukan apakah pemindaian semua baris sesuai pada Produksi (mungkin dilakukan dari pemulihan atau selama jam sibuk):

SELECT
   SUM( DATALENGTH([NVarcharColumn_1]) + DATALENGTH([NVarcharColumn_N]) ) + 
   SUM( (DATALENGTH([VarcharColumn_1]) + DATALENGTH([VarcharColumn_N])) * 2 ) + 
   SUM(4 * [number_of_INT_columns]) +
   SUM(8 * [number_of_BIGINT_and_DATETIME_columns]) +
   SUM(16 * [number_of_DECIMAL/NUMERIC_and_UNIQUEIDENTIFIER_columns]) +
   etc..
FROM [SchemaName].[TableName] WITH (NOLOCK) -- assuming no Snapshot Isolation

Tapi ingat, ini tidak termasuk pengumpulan atau pengumpulan elemen koleksi. Dan tidak yakin apakah kita bisa mendapatkan nilai itu tanpa debugger (atau mungkin sesuatu seperti ILSpy, tapi saya tidak merekomendasikan hal itu karena mungkin melanggar EULA tergantung pada undang-undang setempat).

Solomon Rutzky
sumber
Kami akhirnya mengimplementasikan kode cek untuk memastikan ukuran buffer seperti yang disajikan ke aplikasi.
Tom V - coba topanswers.xyz
6

Dari pertanyaan Anda, sepertinya Anda memiliki ukuran cache maksimum Sdan Anda tidak ingin memuat tabel ke dalam cache yang melebihi ukuran itu. Jika itu benar maka Anda tidak perlu tahu ukuran pasti dari setiap tabel. Anda hanya perlu tahu apakah tabel lebih besar atau lebih kecil dari ukuran cache maksimum S. Itu adalah masalah yang jauh lebih mudah tergantung pada definisi kolom dan jumlah baris tabel Anda.

Saya setuju dengan jawaban luar biasa Solomon Rutzky dalam melihat data yang tidak terkompresi bukanlah cara yang tepat dan mungkin sulit untuk menghasilkan perkiraan yang baik untuk ukuran sebenarnya dari tabel dalam cache. Namun, saya akan bekerja dalam kerangka pertanyaan dan menganggap bahwa Anda dapat mengembangkan rumus yang cukup dekat berdasarkan definisi kolom untuk tipe data statis dan panjang aktual kolom dinamis Anda.

Jika Anda memiliki pemetaan tipe data ke ukuran cache maka Anda harus dapat mengevaluasi beberapa tabel tanpa melihat data di dalamnya:

  1. Jika tabel hanya memiliki tipe data statis (tanpa string atau gumpalan) maka Anda bisa memperkirakan jumlah baris dengan melihat sys.partitionsdan menghitung ukuran tabel menggunakan definisi kolom.
  2. Jika tabel dengan banyak baris memiliki kolom tipe data statis yang cukup maka Anda mungkin bisa menghilangkannya sebagai terlalu besar tanpa melihat datanya. Misalnya, tabel dengan 10 juta baris dan 5 BIGINTkolom dapat memiliki ukuran data yang berukuran 10000000 * (8 + 8 + 8 + 8 + 8) = 400 M byte yang bisa lebih besar dari batas ukuran cache Anda S. Tidak masalah jika memiliki banyak kolom string juga.
  3. Jika tabel dengan beberapa baris cukup kecil maka Anda mungkin dapat mengonfirmasi bahwa itu di bawah batas hanya dengan mengasumsikan bahwa setiap tipe data dinamis memiliki ukuran maksimum yang mungkin. Misalnya, tabel 100 baris dengan BIGINTkolom dan NVARCHAR(20)kolom mungkin tidak melebihi 100 * (8 + 2 * 20) = 4800 byte.
  4. Mungkin benar bahwa jika sebuah tabel memiliki ukuran terkompresi dalam SQL Server yang lebih besar oleh beberapa faktor Situ sangat tidak mungkin masuk dalam cache. Anda harus melakukan pengujian untuk mengetahui apakah nilai tersebut ada.
  5. Anda bisa beruntung karena semua kolom dinamis memiliki statistiknya. Statistik berisi informasi tentang panjang rata-rata dan yang mungkin cukup akurat untuk keperluan Anda.

Anda mungkin harus meminta data tabel yang tidak sesuai dengan kriteria di atas. Ada beberapa trik yang bisa Anda gunakan untuk meminimalkan dampak kinerja ini. Saya akan mengatakan bahwa Anda memiliki dua prioritas yang bersaing di sini: Anda menghargai keakuratan tetapi juga tidak ingin memindai semua data dalam database Anda. Dimungkinkan untuk menambahkan semacam buffer ke dalam perhitungan Anda. Saya tidak tahu apakah itu lebih dapat diterima untuk mengecualikan tabel yang sedikit di bawah ukuran cache maksimum Satau untuk memasukkan tabel yang sedikit di atas ukuran cache maksimum.

Berikut adalah beberapa ide untuk membuat kueri yang melihat data tabel lebih cepat:

  1. Untuk tabel besar Anda mungkin dapat menggunakan TABLESAMPLEselama ukuran sampel Anda cukup besar.
  2. Untuk tabel besar dengan kunci berkerumun mungkin berguna untuk memprosesnya dalam batch pada kunci berkerumun. Sayangnya saya tidak tahu cara menghitung SUM()yang berhenti lebih awal berdasarkan nilai agregat itu. Saya hanya pernah melihat itu berhasil ROW_NUMBER(). Tapi Anda bisa memindai 10% pertama dari tabel, menghemat ukuran data yang dihitung, memindai 10% berikutnya, dan seterusnya. Untuk tabel yang terlalu besar untuk cache, Anda mungkin dapat menyimpan sejumlah besar pekerjaan dengan pendekatan ini dengan berhenti lebih awal.
  3. Untuk beberapa tabel, Anda mungkin cukup beruntung memiliki indeks yang mencakup semua kolom dinamis. Bergantung pada ukuran baris atau faktor lain yang memindai setiap indeks pada satu waktu bisa lebih cepat daripada melakukan pemindaian tabel. Anda juga bisa keluar dari proses ini lebih awal jika ukuran tabel terlalu besar setelah membaca indeks pada satu kolom.
  4. Panjang rata-rata kolom dinamis Anda mungkin tidak terlalu berubah sepanjang waktu. Mungkin praktis untuk menghemat panjang rata-rata yang Anda hitung dan menggunakan nilai-nilai itu dalam perhitungan Anda untuk sementara waktu. Anda dapat mengatur ulang nilai-nilai ini berdasarkan aktivitas DML di tabel atau berdasarkan beberapa metrik lainnya.
  5. Jika mungkin untuk menjalankan tes di semua tabel untuk mengembangkan algoritma, maka Anda mungkin dapat mengambil keuntungan dari pola dalam data. Misalnya, jika Anda memproses tabel yang dimulai dengan yang terkecil terlebih dahulu, Anda mungkin menemukan bahwa begitu Anda memproses 10 (saya membuat angka ini) tabel dalam satu baris yang terlalu besar untuk cache maka sangat kecil kemungkinan bahwa tabel yang lebih besar akan cocok dengan cache. Ini mungkin dapat diterima jika tidak masalah untuk mengecualikan beberapa tabel yang mungkin bisa masuk ke dalam cache.

Saya menyadari bahwa saya tidak memasukkan kode SQL dalam jawaban ini. Beri tahu saya jika akan membantu untuk menulis kode demo untuk semua ide yang saya diskusikan di sini.

Joe Obbish
sumber
2
Saya belum memikirkan pendekatan mengecualikan tabel seperti itu, saya suka pendekatan
Tom V - coba topanswers.xyz