Tad*_*mas 3 sql-server stored-procedures sql-server-2008-r2 plan-cache
我看到由于编译特定存储过程时的锁定而导致阻塞(如KB 263889 中所述)。基本上,有几个进程在同一个资源“TAB:8:1044511100:0 [COMPILE]”上等待 LCK_M_X,如果我查找对象,它就是我的存储过程之一。
试图缩小为什么不缓存该过程的执行计划的范围,我发现sp_executesql的@params 参数的大小是一个因素:如果@params 超过 4000 个字符,有时执行计划会被缓存,但是如果@params 为 4000 个字符或更少,则每次都会缓存计划。
这可以通过以下过程重现:
CREATE PROCEDURE [dbo].ObviouslyAnonymizedProcedure (
@SchemaId int = Null
, @TypeDesc varchar(60) = Null
, @paramFoo varchar(100) = null
, @paramBar datetime2(4) = null
, @paramBaz numeric(4,3) = null
-- Parameter names have been changed. I added a "param" prefix and set type
-- varchar(100) to more easily get up to 4000 characters in this example.
, @paramQux varchar(100) = null
, @paramQuux varchar(100) = null
, @paramCorge varchar(100) = null
, @paramGrault varchar(100) = null
, @paramGarply varchar(100) = null
, @paramWaldo varchar(100) = null
, @paramFred varchar(100) = null
, @paramPlugh varchar(100) = null
, @paramXyzzy varchar(100) = null
, @paramThud varchar(100) = null
, @paramWibble varchar(100) = null
, @paramWobble varchar(100) = null
, @paramWubble varchar(100) = null
, @paramTimey varchar(100) = null
, @paramWimey varchar(100) = null
, @paramFlob varchar(100) = null
, @paramAlpha varchar(100) = null
, @paramBeta varchar(100) = null
, @paramGamma varchar(100) = null
, @paramDelta varchar(100) = null
, @paramEpsilon varchar(100) = null
, @paramZeta varchar(100) = null
, @paramEta varchar(100) = null
, @paramTheta varchar(100) = null
, @paramIota varchar(100) = null
, @paramKappa varchar(100) = null
, @paramLambda varchar(100) = null
, @paramMu varchar(100) = null
, @paramNu varchar(100) = null
, @paramXi varchar(100) = null
, @paramOmicron varchar(100) = null
, @paramPi varchar(100) = null
, @paramRho varchar(100) = null
, @paramSigma varchar(100) = null
, @paramTau varchar(100) = null
, @paramUpsilon varchar(100) = null
, @paramPhi varchar(100) = null
, @paramChi varchar(100) = null
, @paramPsi varchar(100) = null
, @paramOmega varchar(100) = null
, @paramAlfa varchar(100) = null
, @paramBravo varchar(100) = null
, @paramCharlie varchar(100) = null
, @paramEcho varchar(100) = null
, @paramFoxtrot varchar(100) = null
, @paramGolf varchar(100) = null
, @paramHotel varchar(100) = null
, @paramIndia varchar(100) = null
, @paramJuliet varchar(100) = null
, @paramKilo varchar(100) = null
, @paramLima varchar(100) = null
, @paramMike varchar(100) = null
, @paramNovember varchar(100) = null
, @paramOscar varchar(100) = null
, @paramPapa varchar(100) = null
, @paramQuebec varchar(100) = null
, @paramRomeo varchar(100) = null
, @paramSierra varchar(100) = null
, @paramTango varchar(100) = null
, @paramUniform varchar(100) = null
, @paramVictor varchar(100) = null
, @paramWhiskey varchar(100) = null
, @paramXray varchar(100) = null
, @paramYankee varchar(100) = null
, @paramZulu varchar(100) = null
, @paramAdam varchar(100) = null
, @paramBoy varchar(100) = null
, @paramCharles varchar(100) = null
, @paramDavid varchar(100) = null
, @paramEdward varchar(100) = null
, @paramFrank varchar(100) = null
, @paramGeorge varchar(100) = null
, @paramHenry varchar(100) = null
, @paramIda varchar(100) = null
, @paramJohn varchar(100) = null
, @paramKing varchar(100) = null
, @paramLincoln varchar(100) = null
, @paramMary varchar(100) = null
, @paramNora varchar(100) = null
, @paramOcean varchar(100) = null
, @paramPaul varchar(100) = null
, @paramQueen varchar(100) = null
, @paramRobert varchar(100) = null
, @paramSam varchar(100) = null
, @paramTom varchar(100) = null
, @paramUnion varchar(100) = null
, @paramWilliam varchar(100) = null
, @paramYoung varchar(100) = null
, @paramZebra varchar(100) = null
, @paramAlf varchar(100) = null
, @paramBet varchar(100) = null
, @paramGaml varchar(100) = null
, @paramDelt varchar(100) = null
, @paramHe varchar(100) = null
, @paramWau varchar(100) = null
, @paramZai varchar(100) = null
, @paramHet varchar(100) = null
, @paramTet varchar(100) = null
, @paramYod varchar(100) = null
, @paramKaf varchar(100) = null
, @paramLamd varchar(100) = null
, @paramMem varchar(100) = null
, @paramNun varchar(100) = null
, @paramSemk varchar(100) = null
, @paramAin varchar(100) = null
, @paramPe varchar(100) = null
, @paramSade varchar(100) = null
, @paramQof varchar(100) = null
, @paramRosh varchar(100) = null
, @paramShin varchar(100) = null
, @paramAlaph varchar(100) = null
, @paramBeth varchar(100) = null
, @paramGamal varchar(100) = null
, @paramDalath varchar(100) = null
, @paramWaw varchar(100) = null
, @paramZain varchar(100) = null
, @paramHeth varchar(100) = null
, @paramTeth varchar(100) = null
, @paramYudh varchar(100) = null
, @paramKaph varchar(100) = null
, @paramLamadh varchar(100) = null
, @paramIsAggregateFunction bit = null
, @paramIsCheckConstraint bit = null
, @paramIsDefaultConstraint bit = null
, @paramIsForeignKeyConstraint bit = null
, @paramIsScalarFunction bit = null
, @paramIsCLRScalarFunction bit = null
, @paramIsCLRTableValuedFunction bit = null
, @paramIsTableValuedFunction bit = null
, @paramIsInlineTableValuedFunction bit = null
, @paramIsInternalTable bit = null
, @paramIsStoredProcedure bit = null
, @paramIsCLRProcedure bit = null
, @paramIsPlanGuide bit = null
, @paramIsPrimaryKeyConstraint bit = null
, @paramIsRule bit = null
, @paramIsReplicationFilterProcedure bit = null
, @paramIsSystemTable bit = null
, @paramIsSynonym bit = null
, @paramIsSequenceObject bit = null
, @paramIsServiceQueue bit = null
, @paramIsCLRDMLTrigger bit = null
, @paramIsDMLTrigger bit = null
, @paramIsTableType bit = null
, @paramIsTable bit = null
, @paramIsUniqueConstraint bit = null
, @paramIsView bit = null
, @paramIsExtendedStoredProcedure bit = null
, @paramStartRow int
, @paramMaxRows int
)
AS
BEGIN
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE @Select nvarchar(max)
DECLARE @From varchar(max)
DECLARE @Where varchar(max) = ''
DECLARE @Concat varchar(9) = ''
DECLARE @LF char(2) = char(13) + char(10)
DECLARE @Tab char(1) = char(9)
DECLARE @FieldListOuter varchar(max)
DECLARE @FieldListInner varchar(max)
Set @FieldListOuter = @LF
+ @Tab + ' t1.name'+ @LF
+ @Tab + ', t1.object_id'+ @LF
+ @Tab + ', SCHEMA_NAME(t1.schema_id) AS schema_name'+ @LF
+ @Tab + ', t1.type_desc'+ @LF
Set @FieldListInner = @LF
+ @Tab + ' t0.name'+ @LF
+ @Tab + ', t0.object_id'+ @LF
+ @Tab + ', t0.schema_id'+ @LF
+ @Tab + ', t0.type_desc'+ @LF
-- Actual FROM clause joins a few tables. This is a simplified version
-- to serve as an example.
Set @From = N' From sys.objects as t0 with(nolock) ' + @LF
If @SchemaId is NOT NULL
Begin
Set @Where = @Where + @Concat + 't0.schema_id = @SchemaId'
Set @Concat = ' And '
End
If @TypeDesc is NOT NULL
Begin
Set @Where = @Where + @Concat + 't0.type_desc = @TypeDesc'
Set @Concat = ' And '
End
-- etc...
-- Set @Where = @Where + @Concat + 't0.bar_data = @paramBar'
-- Set @Where = @Where + @Concat + 't0.baz_data = @paramBaz'
-- Set @Where = @Where + @Concat + 't0.is_table = @paramIsTable'
-- Set @Where = @Where + @Concat + 'other_table_alias.value = @param...'
If @Where <> ''
Set @Where = 'WHERE ' + @Where
Set @Select =
'SELECT ' + @FieldListOuter +
' FROM ( SELECT ROW_NUMBER() OVER (ORDER BY t0.[object_id]) AS [ROW_NUMBER], ' + @FieldListInner +
@From + @Where+') '+
' AS t1 ' +
' WHERE t1.[ROW_NUMBER] BETWEEN @paramStartRow + 1 and @paramStartRow + @paramMaxRows ORDER BY [ROW_NUMBER]'
Execute sp_executesql
@Select
, N'@SchemaId int, @TypeDesc varchar(36), @paramFoo varchar(100), @paramBar datetime2(4), @paramBaz decimal(4,3), @paramQux varchar(100), @paramQuux varchar(100), @paramCorge varchar(100), @paramGrault varchar(100), @paramGarply varchar(100), @paramWaldo varchar(100), @paramFred varchar(100), @paramPlugh varchar(100), @paramXyzzy varchar(100), @paramThud varchar(100), @paramWibble varchar(100), @paramWobble varchar(100), @paramWubble varchar(100), @paramTimey varchar(100), @paramWimey varchar(100), @paramFlob varchar(100), @paramAlpha varchar(100), @paramBeta varchar(100), @paramGamma varchar(100), @paramDelta varchar(100), @paramEpsilon varchar(100), @paramZeta varchar(100), @paramEta varchar(100), @paramTheta varchar(100), @paramIota varchar(100), @paramKappa varchar(100), @paramLambda varchar(100), @paramMu varchar(100), @paramNu varchar(100), @paramXi varchar(100), @paramOmicron varchar(100), @paramPi varchar(100), @paramRho varchar(100), @paramSigma varchar(100), @paramTau varchar(100), @paramUpsilon varchar(100), @paramPhi varchar(100), @paramChi varchar(100), @paramPsi varchar(100), @paramOmega varchar(100), @paramAlfa varchar(100), @paramBravo varchar(100), @paramCharlie varchar(100), @paramEcho varchar(100), @paramFoxtrot varchar(100), @paramGolf varchar(100), @paramHotel varchar(100), @paramIndia varchar(100), @paramJuliet varchar(100), @paramKilo varchar(100), @paramLima varchar(100), @paramMike varchar(100), @paramNovember varchar(100), @paramOscar varchar(100), @paramPapa varchar(100), @paramQuebec varchar(100), @paramRomeo varchar(100), @paramSierra varchar(100), @paramTango varchar(100), @paramUniform varchar(100), @paramVictor varchar(100), @paramWhiskey varchar(100), @paramXray varchar(100), @paramYankee varchar(100), @paramZulu varchar(100), @paramAdam varchar(100), @paramBoy varchar(100), @paramCharles varchar(100), @paramDavid varchar(100), @paramEdward varchar(100), @paramFrank varchar(100), @paramGeorge varchar(100), @paramHenry varchar(100), @paramIda varchar(100), @paramJohn varchar(100), @paramKing varchar(100), @paramLincoln varchar(100), @paramMary varchar(100), @paramNora varchar(100), @paramOcean varchar(100), @paramPaul varchar(100), @paramQueen varchar(100), @paramRobert varchar(100), @paramSam varchar(100), @paramTom varchar(100), @paramUnion varchar(100), @paramWilliam varchar(100), @paramYoung varchar(100), @paramZebra varchar(100), @paramAlf varchar(100), @paramBet varchar(100), @paramGaml varchar(100), @paramDelt varchar(100), @paramHe varchar(100), @paramWau varchar(100), @paramZai varchar(100), @paramHet varchar(100), @paramTet varchar(100), @paramYod varchar(100), @paramKaf varchar(100), @paramLamd varchar(100), @paramMem varchar(100), @paramNun varchar(100), @paramSemk varchar(100), @paramAin varchar(100), @paramPe varchar(100), @paramSade varchar(100), @paramQof varchar(100), @paramRosh varchar(100), @paramShin varchar(100), @paramAlaph varchar(100), @paramBeth varchar(100), @paramGamal varchar(100), @paramDalath varchar(100), @paramWaw varchar(100), @paramZain varchar(100), @paramHeth varchar(100), @paramTeth varchar(100), @paramYudh varchar(100), @paramKaph varchar(100), @paramLamadh varchar(100), @paramIsAggregateFunction bit, @paramIsCheckConstraint bit, @paramIsDefaultConstraint bit, @paramIsForeignKeyConstraint bit, @paramIsScalarFunction bit, @paramIsCLRScalarFunction bit, @paramIsCLRTableValuedFunction bit, @paramIsTableValuedFunction bit, @paramIsInlineTableValuedFunction bit, @paramIsInternalTable bit, @paramIsStoredProcedure bit, @paramIsCLRProcedure bit, @paramIsPlanGuide bit, @paramIsPrimaryKeyConstraint bit, @paramIsRule bit, @paramIsReplicationFilterProcedure bit, @paramIsSystemTable bit, @paramIsSynonym bit, @paramIsSequenceObject bit, @paramIsServiceQueue bit, @paramIsCLRDMLTrigger bit, @paramIsDMLTrigger bit, @paramIsTableType bit, @paramIsTable bit, @paramIsUniqueConstraint bit, @paramIsView bit, @paramIsExtendedStoredProcedure bit, @paramStartRow int, @paramMaxRows int '
, @SchemaId, @TypeDesc, @paramFoo, @paramBar, @paramBaz, @paramQux, @paramQuux, @paramCorge, @paramGrault, @paramGarply, @paramWaldo, @paramFred, @paramPlugh, @paramXyzzy, @paramThud, @paramWibble, @paramWobble, @paramWubble, @paramTimey, @paramWimey, @paramFlob, @paramAlpha, @paramBeta, @paramGamma, @paramDelta, @paramEpsilon, @paramZeta, @paramEta, @paramTheta, @paramIota, @paramKappa, @paramLambda, @paramMu, @paramNu, @paramXi, @paramOmicron, @paramPi, @paramRho, @paramSigma, @paramTau, @paramUpsilon, @paramPhi, @paramChi, @paramPsi, @paramOmega, @paramAlfa, @paramBravo, @paramCharlie, @paramEcho, @paramFoxtrot, @paramGolf, @paramHotel, @paramIndia, @paramJuliet, @paramKilo, @paramLima, @paramMike, @paramNovember, @paramOscar, @paramPapa, @paramQuebec, @paramRomeo, @paramSierra, @paramTango, @paramUniform, @paramVictor, @paramWhiskey, @paramXray, @paramYankee, @paramZulu, @paramAdam, @paramBoy, @paramCharles, @paramDavid, @paramEdward, @paramFrank, @paramGeorge, @paramHenry, @paramIda, @paramJohn, @paramKing, @paramLincoln, @paramMary, @paramNora, @paramOcean, @paramPaul, @paramQueen, @paramRobert, @paramSam, @paramTom, @paramUnion, @paramWilliam, @paramYoung, @paramZebra, @paramAlf, @paramBet, @paramGaml, @paramDelt, @paramHe, @paramWau, @paramZai, @paramHet, @paramTet, @paramYod, @paramKaf, @paramLamd, @paramMem, @paramNun, @paramSemk, @paramAin, @paramPe, @paramSade, @paramQof, @paramRosh, @paramShin, @paramAlaph, @paramBeth, @paramGamal, @paramDalath, @paramWaw, @paramZain, @paramHeth, @paramTeth, @paramYudh, @paramKaph, @paramLamadh, @paramIsAggregateFunction, @paramIsCheckConstraint, @paramIsDefaultConstraint, @paramIsForeignKeyConstraint, @paramIsScalarFunction, @paramIsCLRScalarFunction, @paramIsCLRTableValuedFunction, @paramIsTableValuedFunction, @paramIsInlineTableValuedFunction, @paramIsInternalTable, @paramIsStoredProcedure, @paramIsCLRProcedure, @paramIsPlanGuide, @paramIsPrimaryKeyConstraint, @paramIsRule, @paramIsReplicationFilterProcedure, @paramIsSystemTable, @paramIsSynonym, @paramIsSequenceObject, @paramIsServiceQueue, @paramIsCLRDMLTrigger, @paramIsDMLTrigger, @paramIsTableType, @paramIsTable, @paramIsUniqueConstraint, @paramIsView, @paramIsExtendedStoredProcedure, @paramStartRow, @paramMaxRows
END
Run Code Online (Sandbox Code Playgroud)
如果您执行以下操作,您将看到该过程的缓存计划:
DECLARE @SchemaId int = SCHEMA_ID('dbo')
exec dbo.ObviouslyAnonymizedProcedure
@paramStartRow=0
,@paramMaxRows=10
,@SchemaId=@SchemaId
,@TypeDesc=N'SQL_STORED_PROCEDURE'
SELECT OBJECT_NAME(CONVERT(int, pvt.objectid)) AS name
, pvt.refcounts
, pvt.usecounts
, pvt.set_options
, pvt.plan_handle
FROM (SELECT cp.plan_handle, cp.refcounts, cp.usecounts, a.attribute, a.value
FROM sys.dm_exec_cached_plans cp
OUTER APPLY sys.dm_exec_plan_attributes(plan_handle) AS a
WHERE cp.objtype = 'Proc' AND cp.cacheobjtype = 'Compiled Plan') AS cpa
PIVOT (MAX(cpa.value) FOR cpa.attribute IN (objectid, dbid, set_options)) AS pvt
WHERE pvt.dbid = DB_ID()
ORDER BY name;
Run Code Online (Sandbox Code Playgroud)
但是,如果您随后进入sp_executesql
调用并在参数列表字符串常量的末尾添加空格并删除/添加过程,则(通常)缓存中不会有计划。我有时看到它缓存,但不可靠。
如果重要的话,我正在运行 SQL Server 2008 R2 SP1 (10.50.2500)。这应该修复了KB 2380435,这让我想到查看sp_executesql
参数列表的大小。
我们正在运行一个确实需要这么多参数的报告。这是 SQL Server 的记录限制,还是有解决此问题的已知方法?
我建议完全不同的策略。与其命名 18,000 个参数,为什么不使用表值参数?我在这里做了一些关于您使用所有这些参数的确切目的的飞跃(因为您非常方便地为我们匿名了它们:-)),但是如果您创建这些类型:
CREATE TYPE dbo.VarcharParameters AS TABLE
(
ParamName SYSNAME,
ParamValue VARCHAR(100)
);
CREATE TYPE dbo.BitParameters AS TABLE
(
ParamName SYSNAME,
ParamValue BIT
);
Run Code Online (Sandbox Code Playgroud)
然后将程序更改如下(请注意有关如何处理 TVP 中的内容的内嵌注释):
CREATE PROCEDURE dbo.ObviouslyAnonymizedProcedure2
@SchemaID INT = NULL,
@TypeDesc NVARCHAR(60) = NULL,
@VCParams dbo.VarcharParameters READONLY,
@BitParams dbo.BitParameters READONLY,
@paramStartRow INT,
@paramMaxRows INT
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
DECLARE
@sql NVARCHAR(MAX) = N'',
@From NVARCHAR(MAX) = N'',
@Where NVARCHAR(MAX) = N'',
@LF CHAR(2) = CHAR(13) + CHAR(10),
@Tab CHAR(1) = CHAR(9),
@FLOuter NVARCHAR(MAX),
@FLInner NVARCHAR(MAX);
DECLARE @LFTab CHAR(3) = @LF + @Tab;
SET @FLOuter = @LFTab + ' t1.name'
+ @LFTab + ', t1.object_id'
+ @LFTab + ', SCHEMA_NAME(t1.schema_id) AS schema_name'
+ @LFTab + ', t1.type_desc'
+ @LF;
SET @FLInner = @LFTab + ' t0.name'
+ @LFTab + ', t0.object_id'
+ @LFTab + ', t0.schema_id'
+ @LFTab + ', t0.type_desc'
+ @LF;
SET @From = N' From sys.objects as t0 with(nolock) ' + @LF;
IF @SchemaId IS NOT NULL
BEGIN
SET @Where = @Where + ' AND t0.schema_id = @SchemaId';
END
IF @TypeDesc IS NOT NULL
BEGIN
SET @Where = @Where + ' AND t0.type_desc = @TypeDesc'
END
-- obviously you need a bunch more of these, and I'm making
-- a half-educated guess about how the bit params are used:
IF EXISTS (SELECT 1 FROM @BitParams WHERE ParamName = 'paramIsView' AND ParamValue = 1)
BEGIN
SET @Where += ' AND t0.type_desc = ''VIEW'''
END
-- and I'm not clear exactly what you're doing with the varchar params,
-- but if you give some more clues I'm sure we can work that out too.
-- It may be very simple to build a string from those, without having to
-- reference every single one of them by name, depending on what they do.
SET @sql = 'SELECT ' + @FLOuter + ' FROM ( SELECT ROW_NUMBER() OVER
(ORDER BY t0.[object_id]) AS rn, ' + @FLInner +
@From + ' WHERE 1 = 1 ' + @Where + ') AS t1
WHERE t1.rn BETWEEN @paramStartRow + 1
AND @paramStartRow + @paramMaxRows ORDER BY rn;'
EXEC sp_executesql @sql,
N'@SchemaId INT,@TypeDesc NVARCHAR(60),@paramStartRow INT,@paramMaxRows INT',
@SchemaID, @TypeDesc, @paramStartRow, @paramMaxRows;
END
GO
Run Code Online (Sandbox Code Playgroud)
现在你可以这样称呼它:
DECLARE @x dbo.VarcharParameters;
INSERT @x VALUES
('paramFoo', 'wuzzuh'),
('paramGamma', 'foobar');
DECLARE @y dbo.BitParameters;
INSERT @y VALUES
('paramIsView', 0),
('paramIsTable', 0);
EXEC dbo.ObviouslyAnonymizedProcedure2
@SchemaId = 1,
@TypeDesc = NULL,
@VCParams = @x,
@BitParams = @y,
@paramStartRow = 1,
@ParamMaxRows = 20;
Run Code Online (Sandbox Code Playgroud)
我不会展示我的结果,因为它们会与您的不同,但我敢打赌,参数的大量减少将消除您遇到的编译问题。
这也是您从 T-SQL 调用此过程的方式;为了从 C# 调用它,您需要使用 DataTable 或 List 或兼容的东西。我这里有一个例子。
这在添加新参数方面也更加灵活——您不必更改存储过程的接口,只需将它们添加到过程主体(如果相关)和填充数据表的代码中。
现在只需让我们了解所有 varchar 参数的作用,您可能离解决方案又近了一步。:-)
归档时间: |
|
查看次数: |
1218 次 |
最近记录: |