Free space of mdf and ldf does not match with database free space

IT *_*her 9 sql-server-2008 sql-server ssms sql-server-2008-r2

In SSMS I saw file size related properties and found below details for one database. Here values does not match with other properties. Here size of the mdf, ldf and total size matches with other values under each window. But Available free space of mdf and ldf if added then it does not equal to Available free space shown in shrink database window and free space shown in database properties. This is true for any database. Why is it so? Please can anyone explain the logic behind this?

Under database properties:

Size: 91.31 MB
Space available:13.40 MB

Under database file properites:

mdf size: 17 MB
ldf size: 75 MB

under shrink database:

Currently allocated size: 91.31 MB
Available free space: 13.40 MB

under shrink file-for data file:

当前分配的大小:16.38 MB
可用空间:12.63 MB

在收缩文件-for 日志文件下:

当前分配的大小:74.94 MB
可用空间:55.62 MB

Aar*_*and 11

这看起来并不是那么疯狂,但请注意,某些 UI 对话框可能没有完全最新的信息(这就是我们拥有DBCC UPDATEUSAGE 之类的内容的原因),并且其中一些还可能涉及四舍五入计算。最后,对话框显示整个数据库的总空间,但未分配的空间只计算数据文件,而不是日志。

让我们合并一些事情。

  1. 数据库属性和收缩数据库显示相同的东西(无论如何你都不应该在收缩数据库 UI 中!)。
  2. 数据库文件属性显示 17 + 75 = 92,在加法之前进行四舍五入,可能与 1 中的 91.31 相同。
  3. 对于分配的空间,单个文件的收缩显示 16.38 + 74.94 = 91.32 - 同样,可能有一些四舍五入,否则完全匹配 1。
  4. 对于可用空间,单个文件的收缩是我怀疑存在真正差异的唯一地方,这是因为 UI 在获取数据的位置方面不一致,并且其中一些地方受制于需要 DBCC UPDATEUSAGE 的缓存。

让我看看这些不同的对话框为我的 AdventureWorks2012 本地副本运行了什么(从这个脚本放大了某些表)。

EXEC sp_spaceused;
Run Code Online (Sandbox Code Playgroud)

这将返回(仅限第一个结果集):

database_size    unallocated space
-------------    -----------------
   1545.81 MB          6.67 MB
Run Code Online (Sandbox Code Playgroud)

基本上运行这个 - 我已经通过跟踪确认 - 与从数据库属性和数据库收缩对话框执行的查询大致相同(我已经从存储过程中剔除了不相关的部分,并添加了一个外部查询来表示数学SSMS 用于显示):

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;
Run Code Online (Sandbox Code Playgroud)

这将返回一个匹配项:

database_size    unallocated space
-------------    -----------------
    1545.8125             6.671875
Run Code Online (Sandbox Code Playgroud)

这些对话框都正确显示了这些信息。数据库属性对话框:

数据库属性对话框

收缩数据库对话框:

收缩数据库对话框

另一方面,收缩文件对话框运行一个略有不同的查询(为了方便起见,这也是雕刻/改编的):

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;
Run Code Online (Sandbox Code Playgroud)

还要注意,除了从函数而不是从 DMV 获取大小数据之外,还没有为新文件类型更新谓词,例如 filestream/hekaton。

结果:

        Currently allocated space    Available free space
----    -------------------------    --------------------
Data                         1517                  7.9375 -- wrong
Log                       28.8125               25.671875 -- wrong
Run Code Online (Sandbox Code Playgroud)

问题在于FILEPROPERTY()函数,它不能保证是最新的(即使在DBCC UPDATEUSAGE(0);运行之后;更多内容见下文)。这最终会导致对话框中出现以下误导性信息:

错误的可用空间读数

再次注意,6.67 MB 从来都不是真正准确的,因为这只是测量总数据库大小 - 分配的页面数,完全不考虑日志。

老实说,如果您想要准确报告数据库中使用的空间,请停止使用运行各种不同查询的米老鼠 UI 来解决这个问题,并停止使用收缩文件对话框来检索信息。在某些情况下,这些显然会受到陈旧数据问题的影响。针对您可以信任的来源运行实际查询。这是我更喜欢的:

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;
Run Code Online (Sandbox Code Playgroud)

此查询返回三个看起来应该很熟悉的数字,一个不应该:

file_type    size           available
---------    -----------    ----------
data         1517.000000     6.6718750
log            28.812500    17.9008512
Run Code Online (Sandbox Code Playgroud)

请注意,DBCC SQLPERF也稍微容易出现空间使用问题,例如在运行后:

DBCC UPDATEUSAGE(0);
Run Code Online (Sandbox Code Playgroud)

上面的查询产生了这个:

file_type    size           available
---------    -----------    ----------
data         1517.000000     8.0781250
log            28.812500    17.8669481
Run Code Online (Sandbox Code Playgroud)

sp_spaceused现在也产生匹配的数字 ( 1545.81 MB / 8.08 MB),即使 - 再次 - 这只是数据文件中的可用空间,并且数据库属性和数据库收缩对话框也是“准确的”(但收缩文件对话框仍然是离开 -FILEPROPERTY()似乎根本不受其影响UPDATEUSAGE):

更新使用后的数据库属性对话框

更新使用后的数据库收缩对话框

更新使用后的数据文件收缩对话框

更新使用后的日志文件收缩对话框

哦,不妨展示一下 Windows 资源管理器对这些文件的看法,这样您就可以将确定 MB 的计算联系起来:

Windows 中的文件大小

当然,这一切需要有多准确取决于您将如何处理这些信息。