Tom*_*m V 12 sql-server size cache compression
在 Dynamics AX 中有一个缓存机制,可以将表配置为加载到内存中并缓存。此缓存限制为一定数量的 KB,以防止出现内存问题。我正在谈论的设置被调用entiretablecache
并在请求单个记录时将整个表加载到内存中。
直到最近,我们依靠一些脚本来验证具有此设置的表的大小,以查看表大小是否高于此限制。
然而,现在,压缩开始发挥作用,诸如sp_spaceused或sys.allocation_units 之类的东西似乎报告了压缩数据实际使用的空间。
显然,应用程序服务器正在处理未压缩的数据,因此 SQL Server 中磁盘上的数据大小无关紧要。我需要未压缩数据的实际大小。
我知道sp_estimate_data_compression_savings但正如名字所说,这只是一个估计。
我希望尺寸尽可能正确。
我能想到的唯一方法是一些复杂的动态 SQL 创建与压缩表具有相同结构的未压缩表,将压缩数据插入到影子表中,然后检查影子表的大小。
不用说,这有点乏味,在数百 GB 的数据库上运行需要一段时间。
Powershell 可能是一个选项,但我不想遍历所有表以select *
对它们执行 a以检查脚本中的大小,因为这只会淹没缓存并且可能需要很长时间。
简而言之,如果可能的话,我需要一种方法来获取每个表的大小,因为它一旦被解压缩,并且在呈现给应用程序的等式中会出现碎片。我对不同的方法持开放态度,首选 T-SQL,但我不反对 Powershell 或其他创造性方法。
假设应用程序中的缓冲区是数据的大小。bigint 始终是 bigint 的大小,而字符数据类型是每个字符 2 个字节(unicode)。BLOB 数据也占用数据的大小,枚举基本上是一个整数,数字数据是 numeric(38,12),datetime 是日期时间的大小。此外,没有NULL
值,它们要么存储为空字符串,要么存储1900-01-01
为零。
没有关于如何实现的文档,但这些假设基于一些测试以及 PFE 和支持团队使用的脚本(显然也忽略了压缩,因为检查是在应用程序中构建的,而应用程序无法分辨)如果底层数据被压缩),它还检查表大小。例如,此链接指出:
避免对大型表使用 EntireTable 缓存(在 AX 2009 中超过 128 KB 或 16 页,在 AX 2012 中超过“整个表缓存大小”应用程序设置[默认值:32KB 或 4 页])——改为使用记录缓存。
我需要未压缩数据的实际大小。
...
我希望尺寸尽可能正确。
虽然对这些信息的渴望当然是可以理解的,但由于错误的假设,获取这些信息,尤其是在“尽可能正确”的情况下比每个人都预期的要困难。无论是做问题中提到的未压缩影子表的想法,还是@sp_BlitzErik 在关于恢复数据库并在那里解压缩以进行检查的评论中的建议,都不应假设未压缩表的大小 == 内存中所述数据的大小在应用服务器上:
是所有表中的行被缓存?还是只是在一个范围内?这里的假设是全部,这可能是正确的,但我认为至少应该提到情况可能并非如此(除非文档另有说明,但这无论如何都是一个小问题,只是不想就不提了)。
问题已更新为:是的,所有行都被缓存。
结构开销
FILLFACTOR
为 100(或 0),页面上仍然可能有一些未使用的空间,因为它不足以容纳整行。这是除页眉之外的内容。此外,如果启用了任何快照隔离功能,我相信每行都会有额外的 13 个字节被版本号占用,这会影响估计值。还有其他与行的实际大小(NULL 位图、可变长度列等)相关的细节,但到目前为止提到的项目应该单独说明这一点。DataTable
? 通用清单?排序字典?每种类型的收藏都有不同数量的偷听。我不希望任何选项都必须反映 DB 端的 Page 和 Row 开销,尤其是在规模上(我确信少量的行可能没有足够的变化,但您并不是在寻找差异以数百字节或仅几 kB 为单位)。CHAR
/VARCHAR
数据以每个字符 1 个字节存储(暂时忽略双字节字符)。XML
被优化为不占用几乎与文本表示所暗示的一样多的空间。这种数据类型创建了一个元素和属性名称的字典,并用它们各自的 ID 替换文档中对它们的实际引用(实际上还不错)。否则,字符串值都是 UTF-16(每个“字符”2 或 4 个字节),就像NCHAR
/一样NVARCHAR
。DATETIME2
介于 6 到 8 个字节之间。DECIMAL
介于 5 到 17 个字节之间(取决于精度)。VARCHAR
。但是,字符串也可以“实习”,这是一个可以多次引用的共享副本(但我不知道这是否适用于集合中的字符串,或者如果适用,是否适用于所有类型的集合)。XML
可能会或可能不会以相同的方式存储在内存中(我将不得不查找)。DateTime
总是 8 个字节(像 T-SQL DATETIME
,但不像DATE
, TIME
, 或DATETIME2
)。Decimal
是始终为16个字节。所有这一切都说:在数据库端几乎没有什么可以在应用程序服务器端获得相当准确的内存占用大小。在加载特定表后,您需要找到一种方法来查询应用服务器本身,因此知道它有多大。而且我不确定调试器是否会让您看到填充集合的运行时大小。如果不是,那么接近的唯一方法是遍历表的所有行,将每列乘以适当的.NET大小(例如INT
= * 4
、VARCHAR
= DATALENGTH() * 2
、NVARCHAR
= 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
Run Code Online (Sandbox Code Playgroud)
但请记住,这不考虑集合或集合元素的开销。并且不确定我们是否可以在没有调试器的情况下获得该值(或者可能是 ILSpy 之类的东西,但我不建议这样做,因为它可能会违反当地法律的 EULA)。
从您的问题来看,您似乎有一个最大缓存大小,S
并且您不想将超过该大小的表加载到缓存中。如果这是真的,那么您不需要知道每个表的确切大小。您只需要知道一个表是大于还是小于最大缓存大小S
。根据您的表的列定义和行数,这是一个非常容易的问题。
我同意 Solomon Rutzky 的出色回答,因为查看未压缩的数据不是可行的方法,而且可能很难对缓存中的表的真实大小得出一个很好的近似值。但是,我将在问题的框架内工作,并假设您可以根据静态数据类型的列定义和动态列的实际长度开发一个足够接近的公式。
如果您将数据类型映射到缓存大小,那么您应该能够评估某些表,甚至无需查看其中的数据:
sys.partitions
使用列定义查看和计算表的大小来估算行数。BIGINT
列的表的数据大小可能为 10000000 * (8+8+8+8+8) = 400 M 字节,这可能大于缓存大小限制S
。如果它也有一堆字符串列也没关系。BIGINT
列和一NVARCHAR(20)
列的 100 行表可能不会超过 100 * (8 + 2 * 20) = 4800 字节。S
,它极不可能适合缓存。您必须进行测试以确定是否存在这样的值。您可能需要查询不符合上述任何条件的表的数据。您可以使用一些技巧来最小化此操作对性能的影响。我会说您在这里有两个相互竞争的优先级:您重视准确性,但也不想扫描数据库中的所有数据。可以在您的计算中添加某种缓冲区。我不知道排除略低于最大缓存大小的S
表或包含略高于最大缓存大小的表是否更可接受。
以下是使查看表数据的查询更快的一些想法:
TABLESAMPLE
,只要您的样本量足够大,您就可以使用。SUM()
根据该聚合的值计算提前退出的a 。我只见过这对ROW_NUMBER()
. 但是您可以扫描表的前 10%,保存计算的数据大小,扫描接下来的 10%,依此类推。对于对于缓存来说太大的表,您可以通过提前退出来使用此方法节省大量工作。我意识到我没有在这个答案中包含任何 SQL 代码。让我知道为我在这里讨论的任何想法编写演示代码是否有帮助。
归档时间: |
|
查看次数: |
1498 次 |
最近记录: |