为什么从表中选择min对分区键的速度很慢?

时间:2014-03-28 14:51:25

标签: sql-server sql-server-2008

我有一个巨大的表格(更新:表格超过1G行),按日期列分区。还有一个索引也被列分隔。

create table T (a int, b date, c int, d.... primary key (a, b)) on psdate(b) 
-- distinct count of b is around 300 only.
create index I on T (c, d) include (e, f, ....) on psdate(b)

然而,运行以下

的速度和成本都很低
select min(b), max(b) from T

执行计划显示它将对I执行所有分区的索引扫描。难道不能立即返回结果吗?

顺便说一下,查询select e from T where a = ....似乎也很昂贵。我应该创建一些非分区索引吗?可以不分割巨大的索引吗?

2 个答案:

答案 0 :(得分:2)

第一个问题应该是:为什么你的表被分区了?您是否使用分区将数据加载到空分区然后将其切换?或者,您是否通过快速老化刚刚超出30天或12个月范围的数据来使用分区来管理滑动窗口?如果这些都不是,那么很可能不应该将表分区为开头。表分区的目的不是为了提高查询性能(至少不是根据编写该功能的团队而来),并且在您开始达到10亿行之前可能无法帮助它。

关于您的select min(b) from T查询及相关问题:

  

执行计划显示它将对I进行索引扫描   分区。它不应该立即返回结果吗?

不,它不会立即返回,因为分区需要将分区键用作谓词(即在WHERE子句中或作为JOIN条件),因此它可以进行分区消除。还要考虑你正在寻找MIN(日期),如果分区函数的范围包含超过1个DATE值,那么即使优化器相当快地缩小了特定分区,它仍然需要扫描整个分区,因为您没有在该DATE字段上定义索引;分区划分数据,它不索引数据。

所以,首先要考虑的是:

  • 取消对表的分区(严重的是,如果您仅使用分区来提高查询性能,甚至不接近10亿行,那么您不应该使用分区,而您所做的任何“修复”此问题只会用来掩盖先前错误决定的错误决定)

关于min(b)查询,从其中一个查询开始,因为您需要一个MIN / MAX操作所需排序的索引:

  • 尝试仅在b
  • 上创建分区索引
  • b上创建一个非分区索引(正如您所询问的那样,但如果您执行SWITCH分区,这可能有一个缺点,因为这可能需要删除非分区索引并重新加)

此外,您可以尝试使用以下技术之一,因为您确实需要将分区键作为谓词:

需要考虑的其他解决方案
假设您在10亿+行中的DATE字段有大约300个不同的值,则将不同的值存储在另一个表中。如果永远不更新DATE字段并且不删除行,则这非常简单,因为不同的值最多会增加而不是可能不再存在。您可以为不同的值创建表,最初通过一次性脚本填充它,然后在T表上有一个AFTER INSERT触发器,它检查不同的值表以查看传入的行是否具有任何DATE值尚未出现在不同的值表中,如果是,则插入它们。这不仅可以超快地获得b的MIN / MAX值,而且对于900个总字节,每300个行中的每个字节将为3个字节。另一方面,假设您的PK为CLUSTERED,仅在b上添加一个索引,对于每个10亿行,每个行中至少有7个字节(DATE字段为3个字节,INT字段为4个字节)总共7 GB。并且需要维护7 GB的索引,并且需要更长的时间和7 GB的维护操作,而不是900字节的维护操作:-D。如果可以更新DATE值或删除T中的行,那么维护不同的行表会有点棘手,但不会太多,而且查询速度仍然要小得多,速度也快。

答案 1 :(得分:1)

首先,我要说分区绝不是关于性能的。

保罗·怀特的这篇优秀文章做了很好的解释,为什么扫描所有分区,比我更好:

http://web.archive.org/web/20180422160838/http://sqlblog.com:80/blogs/paul_white/archive/2012/09/12/why-doesn-t-partition-elimination-work.aspx

它还提供了一些关于如何告诉SQL Server按照您的需要行事的好建议。