我在SQL Server 2012中有一个多租户数据库,每个租户的行都由一个“tenant_id”列(也称为共享数据库,共享模式方法)进行标识。一些租户,特别是新租户,只有很少的行,而其他租户则有很多。
SQL Server的查询优化器通常根据第一次执行时提供的参数构建查询计划,然后将此计划重用于所有未来的查询,即使提供了不同的参数。这被称为参数嗅探。
我们的数据库问题在于,SQL Server有时会基于指向较小租户的参数构建这些计划,这对于该租户可以正常工作,但是当它将缓存的计划重新应用于较大的租户时,它会发生严重错误(通常是超时)。通常,我们只有在较大的租户联系我们报告超时错误时才会发现这种情况,然后我们必须进入系统并手动清除所有查询计划以进行更正。
SQL Server的查询优化器通常根据第一次执行时提供的参数构建查询计划,然后将此计划重用于所有未来的查询,即使提供了不同的参数。这被称为参数嗅探。
我们的数据库问题在于,SQL Server有时会基于指向较小租户的参数构建这些计划,这对于该租户可以正常工作,但是当它将缓存的计划重新应用于较大的租户时,它会发生严重错误(通常是超时)。通常,我们只有在较大的租户联系我们报告超时错误时才会发现这种情况,然后我们必须进入系统并手动清除所有查询计划以进行更正。
有一个查询提示可以使用,防止SQL Server缓存查询计划(OPTIMIZE FOR UNKNOWN
),但这会导致一些额外的开销,因为每次调用查询时都会重新生成查询计划。另一个问题是,我们正在使用Entity Framework,它没有提供在查询中指定OPTIMIZE FOR UNKNOWN
提示的能力。
所以问题是 - 关于参数嗅探,对于多租户数据库的最佳实践是什么?是否有一种方法可以在整个数据库范围内禁用参数嗅探而无需在每个查询中指定?如果有,那是否是最佳方法?我应该以其他方式分区数据吗?是否有其他我没有考虑的方法?
OPTIMIZE FOR UNKNOWN
并不具备您所描述的行为。您将其与OPTION (RECOMPILE)
混淆了。如果您使用企业版,可以查看计划指南以提供提示。或者有一个跟踪标志(4136)可以完全禁用参数嗅探,但这适用于实例而非数据库。 - Martin Smith