查找数据库中所有表的未压缩大小


12

在Dynamics AX中,存在一种缓存机制,可以将表配置为加载到内存中并进行缓存。此高速缓存限制为一定数量的KB,以防止出现内存问题。我正在谈论的设置被调用,entiretablecache并在请求单个记录时立即将整个表加载到内存中。

直到最近,我们还是依靠一些脚本来验证具有此设置的表的大小,以查看表大小是否超过此限制。

现在,压缩开始发挥作用,像sp_spaceusedsys.allocation_units之类的东西似乎报告了压缩数据实际使用的空间。

显然,应用程序服务器正在处理未压​​缩的数据,因此SQL Server中磁盘上的数据大小无关紧要。我需要未压缩数据的实际大小。

我知道sp_estimate_data_compression_savings,但是顾名思义,这只是一个估计。
我希望尺寸尽可能正确。

我能想到的唯一方法是一些复杂的动态SQL创建与压缩表具有相同结构的未压缩表,将压缩数据插入该影子表中,然后检查该影子表的大小。
不用说,这有点乏味,并且需要花费一些时间才能在数百GB的数据库上运行。

Powershell可能是一个选项,但是我不想遍历所有表以select *对它们执行操作以检查脚本中的大小,因为那样只会淹没缓存,并且可能还需要很长时间。

简而言之,如果可能的话,我需要一种获取每个表大小的方法,因为一旦将其解压缩,就可以将碎片从呈现给应用程序的方程式中分离出来。我对各种方法持开放态度,首选使用T-SQL,但我不反对Powershell或其他创造性方法。

假设应用程序中的缓冲区是数据的大小。bigint始终是bigint的大小,并且字符数据类型为每个字符2个字节(unicode)。BLOB数据也占用数据的大小,枚举基本上是int,数字数据是numeric(38,12),datetime是datetime的大小。另外,没有NULL值,它们要么存储为空字符串,要么存储1900-01-01为零。

没有有关如何实现此方法的文档,但是这些假设是基于一些测试以及PFE和支持团队使用的脚本(显然,它们也忽略了压缩,因为检查是在应用程序中构建的,而应用程序无法分辨(如果基础数据已压缩),这还将检查表大小。例如,此链接指出:

避免对大型表使用EntireTable缓存(在AX 2009中超过128 KB或16页,在AX 2012中超过“整个表缓存大小”应用程序设置[默认值:32KB或4页])–改为记录缓存。


3
它很hacky,但是也许最精确的是禁用压缩的还原副本。然后,您还要测试还原,这使您看起来像TOP 1 DBA。
艾瑞克·达林

相信那将是您最好的选择。可能存在尝试进行数学运算的方法。根据定义的列数据类型和长度乘以多少行,然后再添加索引,等等。这比脚本化还原和禁用@sp_BlitzErik上面建议的工作要多得多。还有谁不想成为DBA前1名呢?
Mike Walsh

所有列的SUM(datalength())是否获​​得未压缩的数据大小?
塔帕卡乌阿

@sp_BlitzErik可能是答案,而不是评论。
汤姆五世

Answers:


7

我需要未压缩数据的实际大小。
...
我希望尺寸尽可能正确。

虽然对这些信息的需求当然是可以理解的,但是由于错误的假设,尤其是在“尽可能正确”的情况下获取此信息比每个人的期望都更加棘手。无论是执行问题中提到的未压缩影子表的想法,还是@sp_BlitzErik在有关还原数据库并在那里进行解压缩以进行检查的评论中的建议,都不应假定未压缩表的大小==内存中所述数据的大小在应用服务器上:

  1. 表中的所有行是否都已缓存?还是只是在一定范围内?这里的假设是全部,并且可能是正确的,但是我认为至少应该提到这可能不是这种情况(除非文档另有说明,但这仍然是一个小问题,只是不想不用说)。

    问题已更新为状态:是的,所有行都已缓存。

  2. 结构开销

    1. 在DB方面:DB方面的
      页面和行开销:在页面上适合多少行是由许多可能偏离估计值的因素决定的。即使使用FILLFACTOR100(或0),由于页数不足以容纳整行,因此仍有可能在页面上留下一些未使用的空间。这是页面标题之外的内容。另外,如果启用了任何快照隔离功能,我相信版本号将占用每行额外的13个字节,这会超出估计值。还有其他与行的实际大小有关的细节(NULL位图,可变长度的列等),但是到目前为止提到的项目应该单独说明这一点。
    2. 在应用程序服务器端:
      哪种类型的集合用于存储缓存的结果?我认为这是一个.NET应用程序,所以是DataTable吗?通用清单?分类字典?每种类型的收藏都有不同程度的窃听。我不希望有任何选项能够反映数据库方面的页面和行开销,尤其是在规模方面(我相信少量的行可能没有足够多的影响,但是您没有在寻找差异)以数百个字节或仅几kB为单位)。
  3. 资料类型
    1. 在数据库方面:
      CHAR/ VARCHAR数据每个字符存储1个字节(此刻暂时忽略双字节字符)。XML进行了优化,以不占用文本表示所暗示的空间。此数据类型创建元素和属性名称的字典,并用相应的ID(实际上是不错的)替换文档中对它们的实际引用。否则,字符串值都是UTF-16(每个“字符”为2或4个字节),就像NCHAR/ 一样NVARCHARDATETIME2在6到8个字节之间。DECIMAL介于5到17个字节之间(取决于精度)。
    2. 在应用程序服务器端:
      字符串(同样,假设是.NET)始终为UTF-16。对于8位字符串(例如VARCHAR保留的字符串)没有优化。但是,字符串也可以被“ interned”,这是一个可以多次引用的共享副本(但我不知道这是否适用于集合中的字符串,如果适用,则它是否适用于所有类型的集合)。XML可能会或可能不会以相同的方式存储在内存中(我将不得不查一下)。DateTime始终是8个字节(如T-SQL DATETIME,但不是象DATETIMEDATETIME2)。Decimal始终为16个字节

所有这些要说的是:在数据库方面,您几乎无法做任何事情,而在应用程序服务器方面甚至可以获得相当准确的内存占用空间。在加载特定表之后,您需要找到一种方法来查询应用服务器本身,因此要知道它有多大。而且我不确定调试器是否会让您看到已填充集合的运行时大小。如果不是这样,那么接近的唯一方法是遍历表的所有行,将每列乘以适当的.NET大小(例如INT= * 4VARCHAR= DATALENGTH() * 2NVARCHAR= DATALENGTH()XML=🙃等),但这仍然存在问题集合的开销加上集合的每个元素。

给定问题中的一些新定义,可能可以执行以下查询以使其更加接近。而且,表是否已压缩也无关紧要,尽管每个人都可以确定在生产时扫描所有行是否合适(可能是通过还原还是在非高峰时段进行):

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

但是请记住,这并未考虑收集或收集元素的开销。而且不确定是否可以在没有调试器的情况下获得该值(或者可能是类似ILSpy的内容,但我不建议这样做,因为它可能会违反EULA,具体取决于当地法律)。


我们最终在代码中实现了检查,以确保缓冲区大小在呈现给应用程序时得以确定。
汤姆五世-试试topanswers.xyz

6

从您的问题看来,您似乎有一个最大的缓存大小,S并且您不想将超出该大小的表加载到缓存中。如果是这样,那么您无需知道每个表的确切大小。您只需要知道表是大于还是小于最大缓存大小S。根据表的列定义和行数,这是一个容易得多的问题。

我同意所罗门·鲁兹基(Solomon Rutzky)的出色答案,因为查看未压缩的数据不是行之有效的方法,并且可能很难为高速缓存中的表的真实大小找到一个很好的近似值。但是,我将在问题的框架内工作,并假定您可以基于静态数据类型的列定义和动态列的实际长度来开发足够接近的公式。

如果您具有将数据类型映射到缓存大小的映射,那么您应该能够评估某些表,而无需查看其中的数据:

  1. 如果表仅具有静态数据类型(没有字符串或Blob),则可以通过查看sys.partitions并使用列定义计算表的大小来估计行数。
  2. 如果具有很多行的表具有足够的静态数据类型列,那么您可以在不查看其数据的情况下将其消除得太大。例如,具有1000万行和5 BIGINT列的表的数据大小可能为10000000 *(8 + 8 + 8 + 8 + 8)= 400 M字节,可能大于缓存大小限制S。它是否也具有一串字符串列并不重要。
  3. 如果只有几行的表足够小,则只需假设每种动态数据类型都具有最大可能的大小,便可以确认它低于限制。例如,具有一BIGINT列和一NVARCHAR(20)列的100行表不能超过100 *(8 + 2 * 20)= 4800字节。
  4. 确实可能是,如果表在SQL Server中具有压缩的大小,但压缩后的大小增加了某种程度S,那么它极不可能适合缓存。您必须进行测试才能确定是否存在这样的值。
  5. 您可能会很幸运,因为所有动态列都具有统计信息。统计信息包含有关平均长度的信息,这些信息可能足以满足您的目的。

您可能必须查询不符合以上任何条件的表的数据。您可以使用一些技巧来最大程度地降低性能影响。我要说的是,这里有两个相互竞争的优先事项:您既重视准确性,又不想扫描数据库中的所有数据。可能可以在计算中添加某种缓冲区。我不知道排除略小于最大高速缓存大小的S表还是包含略大于最大高速缓存大小的表是否更可接受。

以下是使查询表数据的查询更快的一些想法:

  1. 对于大表TABLESAMPLE,只要样本量足够大,就可以使用。
  2. 对于具有聚簇键的大型表,在聚簇键上批量处理它们可能会很有用。不幸的是,我不知道一种SUM()基于该合计值来计算提前退出的a的方法。我只看过这项工作ROW_NUMBER()。但是您可以扫描表的前10%,节省计算的数据大小,再扫描下10%,依此类推。对于对于高速缓存而言太大的表,您可以通过尽早退出使用此方法来节省大量工作。
  3. 对于某些表,您可能很幸运能够在所有动态列上覆盖索引。根据行大小或其他因素,一次扫描每个索引可能比进行表扫描更快。如果在读取单个列的索引后表大小太大,也可以提早退出此过程。
  4. 动态列的平均长度可能不会随时间变化很大。保存平均长度并在一段时间内在计算中使用这些值可能是很实际的。您可以基于表中的DML活动或基于某些其他指标来重置这些值。
  5. 如果可以对所有表进行测试以开发算法,那么您也许可以利用数据中的模式。例如,如果您处理从最小的第一个表开始的表,您可能会发现,一旦连续处理10个表(我将此数字加起来)对于高速缓存而言太大了,那么任何较大的表都不太可能适合该表。缓存。如果可以排除一些可能适合缓存的表,那么这可能是可以接受的。

我意识到我没有在此答案中包含任何SQL代码。让我知道为我在这里讨论的任何想法编写演示代码是否有帮助。


2
我没想到要排除这样的表格,我喜欢这种方法
Tom V-试试topanswers.xyz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.