今天我意识到我们的 sql server 的计划缓存中塞满了 hundreads 和数千个几乎相同的编译计划。
在消耗约 4500 MB 的实时系统上,计划总数约为 30.000。看着它们,有数千个几乎相同。
一些样本:
(@ID uniqueidentifier,@GSS nvarchar(663))UPDATE [TAB1] SET [GSS]=@GSS WHERE [ID]=@ID
(@ID uniqueidentifier,@GSS nvarchar(664))UPDATE [TAB1] SET [GSS]=@GSS WHERE [ID]=@ID
(@ID uniqueidentifier,@GSS nvarchar(665))UPDATE [TAB1] SET [GSS]=@GSS WHERE [ID]=@ID
(@ID uniqueidentifier,@GSS nvarchar(666))UPDATE [TAB1] SET [GSS]=@GSS WHERE [ID]=@ID
(@ID uniqueidentifier,@GSS nvarchar(669))UPDATE [TAB1] SET [GSS]=@GSS WHERE [ID]=@ID
(@ID uniqueidentifier,@FR ntext,@uiStamp datetime,@uiUser varchar(10))UPDATE [TTR] SET [FR]=@FR, [uiStamp]=@uiStamp, [uiUser]=@uiUser WHERE [ID]=@ID
(@ID uniqueidentifier,@FR ntext,@uiStamp datetime,@uiUser varchar(11))UPDATE [TTR] SET [FR]=@FR, [uiStamp]=@uiStamp, [uiUser]=@uiUser WHERE [ID]=@ID
(@ID uniqueidentifier,@FR ntext,@uiStamp datetime,@uiUser varchar(12))UPDATE [TTR] SET [FR]=@FR, [uiStamp]=@uiStamp, [uiUser]=@uiUser WHERE [ID]=@ID
(@ID uniqueidentifier,@FR ntext,@uiStamp datetime,@uiUser varchar(13))UPDATE [TTR] SET [FR]=@FR, [uiStamp]=@uiStamp, [uiUser]=@uiUser WHERE [ID]=@ID
该应用程序几乎在任何地方都使用sp_executesql并将值作为参数传递,这就是为什么我确信计划得到重用的原因。
但现在看起来该应用程序根本不关心字符串类型的长度,长度会根据值的实际长度自动添加,从而导致每个传递的字符串值的每个字符串长度组合都有不同的计划。所以大多数情况下,UPDATE 和 INSERT 语句似乎是这里的问题所在。我想一些开发人员在这一点上已经节省了一些时间,只是简单地忽略了大小。
这些冗余 UPDATE/INSERT 类型的计划的使用次数相对较少(通常为 1 次,有的高达 10 次,只有少数高达 40 次)。
它们的大小介于 0.05 MB 和 2 MB 之间。很难说出可能节省的确切值和数量,因为语句之间的长度常量不断变化,因此无法正确排序。但是,我认为这里的平均计划大小在大约 15000 个更新/插入计划中是 0.17 MB,我猜至少可以节省 80%,大概是实际使用的 2550 MB 中的 2040 MB 的潜在节省那15000个计划。这会将总计划缓存使用量从 4500 MB 减少到……。大约 2500 MB。
看起来我们可以很容易地在这里节省一些内存,对吧?
我们谈论的是内存不多 (32 GB) 的 sql server,它们确实可以从一些额外的可用内存中获益,但是,我想这也是一般系统健康状况的问题。
所以我的问题是,我能做些什么呢?我在数据库端有任何选项来控制这个吗?是否有任何我还不知道的临时查询优化选项让我们忽略这个?
还是真的需要开发者在输入参数中正确添加type和typelength?
在 (n)varchar 类型的情况下,表定义中已经定义了前导长度,那么使用这些类型有什么缺点吗?
附加信息:
与此同时,我发现K.Tripps关于计划缓存变得疯狂的帖子......她的查询显示我有 1500 MB 和 20300 个计划,缓存中的使用计数为 1......
在我的特定情况下,“针对临时工作负载进行优化”是灵丹妙药吗?
更多信息: 我发现查询是由 SqlDataAdapter 和 SqlCommandBuilder 创建的。commandbuilder 无论如何都不会从原始表中导出所有已创建参数的参数大小。如果未设置它们,则传入我们的实际值的大小将用作大小值。这可以在此处找到:“如果未明确设置,则根据指定参数值的实际大小推断大小。 ”
查看计划缓存、临时工作负载和清除一次性计划缓存膨胀。
总结一下:
选项 2 -
根据一次性计划浪费的总量清除所有缓存(2005/2008):
我们结束了更改应用程序代码。无法避免生成的 Sql 语句中的动态字符长度,以通过 sql server 的配置来保存额外执行计划的创建和存储。所以我们所做的是从命令构建器创建语句的数据集中读取源表的架构。
这样就可以通过将 SqlParameter.SourceColumn 属性与 schemaDT[SqlParameter.SourceColumn] 匹配来为每个参数获取正确的 SqlParameter.Size 属性。