多租户SQL Server数据库和参数嗅探

时间:2012-10-19 17:36:38

标签: sql-server performance entity-framework multi-tenant parameter-sniffing

我在SQL Server 2012中有一个多租户数据库,其中每个租户的行由tenant_id列(也称为Shared Database, Shared Schema方法)标识。有些租户,特别是较新的租户,行数很少,而其他租户有很多。

SQL Server的查询优化器通常根据第一次执行时提供的参数构建查询计划,然后即使提供了不同的参数,也可以将此计划重新用于所有将来的查询。这称为parameter sniffing

我们对数据库的问题是,SQL Server有时会根据指向较小租户的参数构建这些计划,这对于该租户很有效,但是当它将缓存计划重新应用于更大的租户时,它会灾难性地失败(实际上通常是超时)。通常情况下,只有当我们的一个大租户联系我们遇到超时错误时才会发现这种情况,然后我们必须进入系统并手动清除所有查询计划以纠正错误。

您可以使用查询提示来阻止SQL Server缓存查询计划(OPTIMIZE FOR UNKNOWN),但这会导致一些额外的开销,因为每次调用查询时都会重新生成查询计划。另一个问题是我们使用的Entity Framework无法在查询中指定OPTIMIZE FOR UNKNOWN提示。

所以问题是 - 多租户数据库在参数嗅探方面的最佳做法是什么?有没有办法在数据库范围内禁用参数嗅探,而无需在每个查询中指定它?如果是这样,这是最好的方法吗?我应该以其他方式对数据进行分区吗?还有其他一些我没想过的方法吗?

2 个答案:

答案 0 :(得分:3)

我遇到了类似的问题,并通过像这样传递我的参数来成功解决它:

CREATE PROCEDURE [dbo].[InsertAPCheck]
@APBatchID  int = Null,
@BankAccountID  int = Null
AS
  /* copy parameters to temporary variables */
  SELECT @xAPBatchId = APBatchId, @xBankAccountID = @BankAccountID
 .
 .
 /* now run the meat of your logic using the temp variables */
 SELECT * FROM myTable where Id=@xAPBatchId.....etc.

换句话说,为传入的每个参数创建一个1-1的局部变量 然后只引用SP逻辑中的那些新变量。我可能错过了SQL Server可以为我做的一些优化,但最重要的是我错过了当参数嗅探时我得到的真正可怕的性能。

在你的情况下,也许你可以尝试这样做只是为了多租户id(我假设它是所有SP的参数?),并让SQL服务器优化其余的参数,如果可以的话。

答案 1 :(得分:0)

对于动态SQL(例如,Entity Framework生成的SQL),在命令文本中插入注释,该注释包含当前租户的标识符。这实际上是按租户划分SQL的执行计划缓存,从而使执行计划与租户隔离,但允许它们由同一租户重用。

要将注释添加到命令文本中,可以子类/实现DbConnection/IDbConnectionDbCommand/IDbCommand并应用装饰器模式。调用DbCommand/IDbCommand.Execute*可以在调用inner方法之前附加注释的租户ID,然后在返回后删除注释。使用修饰的连接初始化Entity Framework或您使用的任何ORM。

如果您有很多租户,则可以按租户大小类别对计划缓存进行分区。否则,您将有效地执行与OPTION (RECOMPILE)相同的操作,因为计划将在重新使用之前从缓存中过期。