Ruang bebas mdf dan ldf tidak cocok dengan ruang bebas basis data

9

Dalam SSMS saya melihat properti terkait ukuran file dan menemukan rincian di bawah ini untuk satu database. Di sini nilai tidak cocok dengan properti lainnya. Di sini ukuran mdf, ldf, dan ukuran total cocok dengan nilai lain di bawah setiap jendela. Tetapi ruang bebas yang tersedia dari mdf dan ldf jika ditambahkan maka itu tidak sama dengan ruang bebas yang tersedia ditampilkan di jendela basis data menyusut dan ruang bebas yang ditunjukkan pada properti basis data. Ini berlaku untuk basis data apa pun. Kenapa gitu? Adakah yang bisa menjelaskan logika di balik ini?

Di bawah properti basis data:

Ukuran: 91,31 MB
Ruang yang tersedia: 13,40 MB

Di bawah hak file file database:

ukuran mdf: 17 MB
ukuran ldf: 75 MB

di bawah basis data menyusut:

Ukuran yang dialokasikan saat ini: 91,31 MB
Ruang kosong yang tersedia: 13,40 MB

di bawah shrink file-untuk file data:

ukuran yang dialokasikan saat ini: 16,38 MB
Ruang kosong yang tersedia: 12,63 MB

di bawah shrink file-untuk file log:

ukuran yang dialokasikan saat ini: 74,94 MB
Ruang kosong yang tersedia: 55,62 MB

Peneliti IT
sumber

Jawaban:

11

Ini sepertinya tidak terlalu gila, tetapi perhatikan bahwa beberapa dialog UI mungkin tidak memiliki informasi yang benar-benar terkini (inilah sebabnya kami memiliki hal-hal seperti DBCC UPDATEUSAGE ), dan pembulatan juga dapat terlibat dalam beberapa dialog tersebut perhitungan. Akhirnya, dialog menunjukkan ruang total untuk seluruh database , tetapi ruang yang tidak terisi hanya dihitung untuk file data , bukan log.

Mari kita menyatukan beberapa hal.

  1. Properti basis data dan basis data menyusut menunjukkan hal yang sama (bukan berarti Anda seharusnya berada di UI basis data menyusut!).
  2. Properti file database menunjukkan 17 + 75 = 92 yang, dengan pembulatan sebelum penambahan, mungkin sama 91,31 dalam 1.
  3. Untuk ruang yang dialokasikan, menyusut untuk file individual menunjukkan 16,38 + 74,94 = 91,32 - sekali lagi, mungkin beberapa pembulatan di sana, jika tidak tepat sama dengan 1.
  4. Untuk ruang yang tersedia, menyusut untuk file individual adalah satu-satunya tempat di mana saya mencurigai adanya perbedaan nyata, dan ini karena UI tidak konsisten tentang di mana ia mendapatkan datanya, dan beberapa tempat ini tunduk pada caching yang mengharuskan DBCC UPDATEUSAGE.

Biarkan saya melihat apa yang dijalankan oleh berbagai dialog ini untuk salinan AdventureWorks2012 lokal saya (dengan tabel tertentu diperbesar dari skrip ini ).

EXEC sp_spaceused;

Ini kembali (hanya resultset pertama):

database_size    unallocated space
-------------    -----------------
   1545.81 MB          6.67 MB

Pada dasarnya menjalankan ini, yang - saya sudah konfirmasi melalui jejak - kira-kira permintaan yang sama dieksekusi dari properti database dan dialog menyusut database (saya telah mengukir bagian-bagian yang tidak relevan dari prosedur yang disimpan, dan menambahkan permintaan luar untuk mewakili matematika yang SSMS lakukan untuk tampilan):

SELECT database_size = DbSize*8.0/1024 + LogSize*8.0/1024,
  [unallocated space] = (DbSize-SpaceUsed)*8.0/1024
FROM
(
  SELECT
    (SELECT SUM(CAST(df.size as float)) FROM sys.database_files AS df 
       WHERE df.type in ( 0, 2, 4 ) ) AS [DbSize],
    SUM(a.total_pages) AS [SpaceUsed],
    (SELECT SUM(CAST(df.size as float)) FROM sys.database_files AS df 
       WHERE df.type in (1, 3)) AS [LogSize]
  FROM sys.partitions p 
    join sys.allocation_units a on p.partition_id = a.container_id 
    left join sys.internal_tables it on p.object_id = it.object_id
) AS x;

Ini mengembalikan kecocokan:

database_size    unallocated space
-------------    -----------------
    1545.8125             6.671875

Semua dialog ini menunjukkan informasi ini dengan benar. Dialog Properti Database:

Dialog Properti Database

Kecilkan dialog Database:

Kecilkan dialog Database

Dialog file menyusut , di sisi lain, menjalankan kueri yang sedikit berbeda (sekali lagi ini diukir / disesuaikan untuk kenyamanan):

SELECT SUBSTRING(name, CHARINDEX('_',name)+1, 4), 
  [Currently allocated space] = size/1024.0, 
  [Available free space] = (Size-UsedSpace)/1024.0
FROM
(
  SELECT s.name, 
    CAST(FILEPROPERTY(s.name, 'SpaceUsed') AS float)*CONVERT(float,8) AS [UsedSpace],
    s.size * CONVERT(float,8) AS [Size]
  FROM sys.database_files AS s
  WHERE (s.type IN (0,1))
) AS x;

Perhatikan juga, bahwa selain mendapatkan data ukuran dari fungsi alih-alih DMV, predikat belum diperbarui untuk tipe file baru, seperti filestream / hekaton.

Hasil:

        Currently allocated space    Available free space
----    -------------------------    --------------------
Data                         1517                  7.9375 -- wrong
Log                       28.8125               25.671875 -- wrong

Masalahnya adalah FILEPROPERTY()fungsi, yang tidak dijamin mutakhir (bahkan setelah DBCC UPDATEUSAGE(0);dijalankan; lebih banyak di bawah). Ini berakhir dengan informasi yang menyesatkan ini pada dialog:

Ruang bacaan yang tersedia salah

Perhatikan, sekali lagi, 6,67 MB itu tidak pernah benar-benar akurat, karena ini hanya mengukur ukuran total basis data - jumlah halaman yang dialokasikan, sepenuhnya mengabaikan log.

Dalam semua kejujuran, jika Anda ingin pelaporan yang akurat tentang ruang yang digunakan dalam database, berhenti menggunakan UI mickey mouse yang menjalankan semua jenis pertanyaan yang berbeda untuk mencari tahu, dan berhenti menggunakan dialog file menyusut untuk mengambil informasi. Ini jelas tunduk pada masalah data basi dalam kasus-kasus tertentu. Jalankan kueri aktual terhadap sumber yang dapat Anda percayai. Inilah yang saya sukai:

DECLARE @log_used DECIMAL(19,7);
CREATE TABLE #x(n SYSNAME, s DECIMAL(19,7), u DECIMAL(19,7), b BIT);
INSERT #x EXEC('DBCC SQLPERF(LogSpace);');
SELECT @log_used = u FROM #x WHERE n = DB_NAME();
DROP TABLE #x;

DECLARE @data_used DECIMAL(19,7);
SELECT @data_used = SUM(a.total_pages)*8/1024.0
FROM sys.partitions AS p 
INNER JOIN sys.allocation_units AS a 
ON p.[partition_id] = a.container_id;

;WITH x(t,s) AS
( 
  SELECT [type] = CASE 
    WHEN [type] IN (0,2,4) THEN 'data' ELSE 'log' END, 
    size*8/1024.0 FROM sys.database_files AS f
)
SELECT 
  file_type = t, 
  size = s,
  available = s-CASE t WHEN 'data' THEN @data_used ELSE @log_used END 
FROM x;

Kueri ini mengembalikan tiga angka yang seharusnya terlihat sangat akrab, dan yang tidak:

file_type    size           available
---------    -----------    ----------
data         1517.000000     6.6718750
log            28.812500    17.9008512

Perhatikan bahwa DBCC SQLPERF juga sedikit rentan terhadap masalah dengan penggunaan ruang, misalnya setelah menjalankan:

DBCC UPDATEUSAGE(0);

Sebaliknya, kueri di atas menghasilkan ini:

file_type    size           available
---------    -----------    ----------
data         1517.000000     8.0781250
log            28.812500    17.8669481

sp_spaceusedsekarang menghasilkan angka yang cocok juga ( 1545.81 MB / 8.08 MB), meskipun - lagi - itu hanya ruang yang tersedia dalam file data , dan properti database dan dialog menyusut database juga "akurat" (tetapi dialog menyusut file masih jalan keluar - FILEPROPERTY()sepertinya tidak terpengaruh UPDATEUSAGEsama sekali):

Dialog Properti Database setelah pembaruan penggunaan

Dialog penyusutan basis data setelah pembaruan digunakan

Dialog menyusut file data setelah pembaruan digunakan

Dialog penyusutan file log setelah pembaruan digunakan

Oh, dan mungkin juga menunjukkan apa yang Windows Explorer pikirkan tentang file-file ini, sehingga Anda dapat menghubungkannya dengan perhitungan yang dilakukan untuk menentukan MB:

Ukuran file di Windows

Seberapa akurat semua ini perlu, tentu saja, tergantung pada apa yang akan Anda lakukan dengan informasi tersebut.

Aaron Bertrand
sumber