第十一周翻译

SQL Server统计信息简介
SQL Server统计信息是系统对象,其中包含索引键值中的数据分布信息,有时也包含常规列值中的数据分布信息。可以对支持比较操作的任何数据类型(如>、<、=)创建统计信息。让我们从上一章的清单2-15中创建的dbo.books表中检查idx_books_isbn索引统计信息。你可以使用dbcc show_statistics(‘dbo.books’,idx_books_isbn)命令来完成此操作。结果如图3-1所示。
图3-1  DBCC显示统计信息输出
正如你所见,dbcc show_statistics命令返回三个结果集。第一个包含有关统计信息的常规元数据信息,如名称、更新日期、更新统计信息时索引中的行数等。第一个结果集中的steps列指示柱状图中的步数/值数(稍后将详细介绍)。密度值不由查询优化器使用,仅出于向后兼容性的目的而显示。第二个结果集称为密度向量,包含有关统计(索引)中关键值组合的密度的信息。它是根据1/number个不同的值公式计算的,它指示每个键值组合平均有多少行。即使idx_books_isbn index只定义了一个键列isbn,它还包括一个作为索引行一部分的聚集索引键。我们的表有1252500个独特的ISBN值,ISBN列的密度为1.0/1252500=7.984032E-07。所有(isbn,bookid)列的组合也都是唯一的,并且密度相同。最后一个结果集称为直方图。柱状图中的每个记录称为柱状图步骤,包括统计(索引)最左边一列中的样本键值,以及从上一个值到当前值范围内的数据分布信息。让我们更深入地研究柱状图列。
“范围”列存储键的示例值。该值是由柱状图步骤定义的范围的上限键值。例如,在图3-1的柱状图中,记录(步骤)3,其范围hi_key='104-0100002488’存储有关从“101-0100001796”到“104-0100002488”的间隔的信息。“范围行”列估计间隔内的行数。在我们的例子中,由记录(步骤)3定义的间隔有8191行。eq_rows表示有多少行的键值等于范围_hi_key上限值。在我们的例子中,只有一行ISBN=‘104-0100002488’。distinct_range_rows表示间隔内有多少个键的不同值。在我们的例子中,键的所有值都是唯一的,因此不同的“range”行=范围“行”。avg_range_rows表示间隔中每个不同键值的平均行数。在我们的例子中,键的所有值都是唯一的,所以avg_range_rows=1。让我们将一组重复的isbn值插入到索引中,代码如清单3-1所示。
清单3-1 在索引中插入重复的isbn值。

;with Prefix(Prefix)
as ( select Num from (values(104),(104),(104),(104),(104)) Num(Num) )
,Postfix(Postfix)
as
(
select 100000001
union all
select Postfix + 1 from Postfix where Postfix < 100002500
)
insert into dbo.Books(ISBN, Title)
select
convert(char(3), Prefix) + ‘-0’ + convert(char(9),Postfix)
,‘Title for ISBN’ + convert(char(3), Prefix) + ‘-0’ + convert(char(9),Postfix)
from Prefix cross join Postfix
option (maxrecursion 0);
– Updating the statistics
update statistics dbo.Books IDX_Books_ISBN with fullscan;
现在,如果再次运行dbcc show_statistics(‘dbo.books’,idx_books_isbn)命令,你将看到图3-2所示的结果。
图3-2。DBCC显示统计信息输出
前缀为104的ISBN值现在有重复项,这会影响柱状图。值得一提的是,第二个结果集中的密度信息也发生了变化。具有重复值的isbn s的密度高于(isbn,bookid)列的组合,这仍然是唯一的。让我们运行select bookid,title from dbo.books,其中isbn类似于“114%”语句,并检查执行计划,如图3-3所示。
图3-3查询执行计划
大多数执行计划操作员都具有两个重要属性。实际行数指示在执行运算符期间处理了多少行。“估计行数”指示在查询优化阶段为该运算符估计的SQL Server行数。在我们的例子中,SQL Server估计有2625行的ISBN S从114开始。如果你查看图3-2所示的柱状图,你将看到步骤10存储了ISBN间隔的数据分布信息,其中包括你选择的值。即使使用线性近似,也可以估计接近SQL Server确定的行数。关于统计,有两件非常重要的事情要记住。1。柱状图只为最左边的统计(索引)列存储有关数据分布的信息。统计中有关于关键值的多列密度的信息,但就是这样。柱状图中的所有其他信息仅与最左边的统计列的数据分布有关。2。无论表的大小和表是否分区,SQL Server在柱状图中最多保留200个步骤。每个柱状图步骤覆盖的间隔随着表的增长而增加。这会导致大型表的统计数据不太准确。对于复合索引,当索引中的所有列在所有查询中都用作谓词时,最好将密度较低/唯一值百分比较高的列定义为索引最左边的列。这将允许SQL Server更好地利用统计信息中的数据分发信息。不过,你应该考虑谓词的可sargability。例如,如果所有查询都在WHERE子句中使用firstname=@firstname和lastname=@lastname谓词,则最好将firstname作为索引中最左边的列。尽管如此,对于类似firstname=@firstname and lastname<>@lastname,其中firstname不可搜索。

统计和执行计划
默认情况下,SQL Server自动创建和更新统计信息。在数据库级别上有两个控制此类行为的选项:
1、自动创建统计信息控制优化器是否自动创建列级统计信息。此选项不影响始终创建的索引级别统计信息。默认情况下,自动创建统计数据数据库选项处于启用状态。
2、启用“自动更新统计信息数据库”选项后,SQL Server会在每次编译或执行查询时检查统计信息是否过期,并根据需要进行更新。默认情况下,也会启用自动更新统计数据数据库选项。
提示:你可以使用统计计算索引选项控制索引级别统计信息的自动更新行为。默认情况下,此选项设置为关闭,这意味着统计信息将自动更新。在索引或表级别更改自动更新行为的另一种方法是使用开机启动管理系统存储过程。
SQL Server在执行的更改数的基础上根据统计数据是否过时来插入、更新、删除和合并影响统计信息列的语句。SQL Server计算更改统计信息列的次数,而不是更改的行数。例如,如果你对同一行进行100次更改,它将被计算为100次更改,而不是1次更改。有三种不同的场景,称为统计更新阈值,有时也称为统计重新编译阈值,其中SQL Server将统计标记为过时。1。当表为空时,在向表中添加数据时,SQL Server将超过统计信息。2。当表的行数少于500时,SQL Server会在统计信息列每更改500次后,将统计信息更新一次。三。在SQL Server 2016之前和在数据库兼容级别<130的SQL Server 2016中:当一个表有500行或更多行时,SQL Server会在统计列的每500+次(表中行总数的20%)更改后超过统计。
在数据库兼容级别为130的SQL Server 2016中:大型表上的统计信息更新阈值将变为动态的,并取决于表的大小。表中的行越多,阈值越低。对于具有数百万甚至数十亿行的大型表,统计更新阈值可能只是表中行总数的一个百分比的一小部分。还可以使用SQL Server 2008R2 SP1及更高版本中的跟踪标志t2371启用此行为。表3-1总结了不同版本的SQL Server中的统计更新阈值行为。
表3-1  统计更新阈值和SQL Server版本
那让我们得到了一个非常重要的结论。对于静态统计信息更新阈值,触发统计信息更新所需的统计信息列更改数与表大小成比例。表越大,自动更新统计信息的频率就越低。例如,对于具有10亿行的表,你需要对统计信息列执行大约2亿次更改,以使统计信息过时。建议尽可能使用动态更新阈值。让我们看看这种行为是如何影响我们的系统和执行计划的。此时,表dbo。书有126500行。让我们用前缀999向表中添加250000行,如清单3-5所示。在本例中,我使用的是未启用t2371的SQL Server 2012。如果在启用动态统计信息更新阈值的情况下运行它,则可以看到不同的结果。此外,SQL Server 2014中引入的新基数估计量也可以改变这种行为。我们将在本章后面讨论。
清单3-5 向dbo.books中添加行

;with Postfix(Postfix)
as
(
select 100000001
union all
select Postfix + 1
from Postfix
where Postfix < 100250000
)
insert into dbo.Books(ISBN, Title)
select
‘999-0’ + convert(char(9),Postfix)
,‘Title for ISBN 999-0’ + convert(char(9),Postfix)
from Postfix
option (maxrecursion 0);
现在,让我们运行selectfrom dbo.books,其中isbn类似于“999%”,它选择具有此类前缀的所有行。
如果检查查询的执行计划(如图3-7所示),你将看到非聚集索引查找和键查找操作,尽管在需要从表中选择几乎20%的行的情况下,它们效率很低。
图3-7  查询的执行计划,选择前缀为999的行
你还将在图3-7中注意到,索引查找运算符的估计行数和实际行数之间存在巨大差异。SQL Server估计表中只有31.4行的前缀为999,即使有25万行的前缀为999。因此,产生了一个效率极低的计划。让我们通过运行dbcc show_statistics(“dbo.books”,idx_books_isbn)命令来查看idx_books_isbn统计信息。输出如图3-8所示。正如你所看到的,即使我们在表中插入了250000行,统计数据也没有更新,并且前缀999的柱状图中也没有数据。第一个结果集中的行数与上次统计更新期间表中的行数相对应。它不包括刚刚插入的250000行。
图3-8  IDX_BOOKS_ISBN数据
现在让我们用UPDATE STATISTICS dbo.Books IDX_Books_ISBN WITH FULLSCAN 更新数据,然后再次运行select
from dbo.books where isbn like’999%'查询。查询的执行计划如图3-9所示。估计的行数现在是正确的,并且SQL Server最终得到了一个更高效的执行计划,该计划使用的聚集索引扫描的I/O读取次数比以前少17倍。
图3-9  统计信息更新后选择前缀为999的行的查询的执行计划
如你所见,不正确的基数估计可能导致执行计划效率极低。过时的统计数据可能是导致基数估计错误的最常见原因之一。你可以通过检查执行计划中的估计行数和实际行数来确定其中的一些情况。这两个值之间的巨大差异往往表明统计数据不正确。更新统计信息可以解决这个问题并生成更有效的执行计划。
统计维护
正如我已经提到的,默认情况下,SQL Server会自动更新统计信息。对于小型表,这种行为通常是可以接受的;但是,对于具有数百万或数十亿行的大型表,不应依赖自动统计更新,除非你使用的SQL Server 2016的数据库兼容级别为130或启用了跟踪标志t2371。按20%的统计信息更新阈值触发统计信息更新所需的更改数将非常高,因此,不会经常触发更新。在这种情况下,建议你手动更新统计信息。在选择最佳统计维护策略时,必须分析表的大小、数据修改模式和系统可用性。例如,如果系统在工作时间之外没有很重的负载,你可以决定每晚更新关键表的统计信息。不要忘记统计信息和/或索引维护会给SQL Server增加额外的负载。必须分析它如何影响同一服务器和/或磁盘阵列上的其他数据库。设计统计维护策略时要考虑的另一个重要因素是如何修改数据。如果索引的键值不断增加或减少,例如索引中最左边的列被定义为标识或用序列对象填充,则需要更频繁地更新统计信息。如你所见,如果特定的键值超出了柱状图范围,SQL Server会大大低估行数。在SQL Server 2014到2016中,这种行为可能有所不同,我们将在本章后面看到。可以使用update statistics命令更新统计信息。当SQL Server更新统计信息时,它读取数据的样本,而不是扫描整个索引。你可以使用fullscan选项来更改该行为,该选项强制SQL Server从索引中读取和分析所有数据。正如你可能猜测的那样,该选项提供了最准确的结果,尽管它可以在大型表的情况下引入大量的I/O活动。
注意 在重建索引时,SQL Server会更新统计信息。我们将在第6章“索引碎片”中更详细地讨论索引维护。
你可以使用sp_updatestats系统存储过程更新数据库中的所有统计信息。建议你使用此存储过程,并在将数据库升级到新版本的SQL Server之后更新数据库中的所有统计信息。你应该与dbcc updateusage存储过程一起运行它,该存储过程更正了目录视图中不正确的页数和行数信息。有一个sys.dm_db_stats_properties dmv,它显示自上次统计信息更新以来对统计信息列所做的修改数。使用该DMV的代码如清单3-9所示。
清单3-9。使用sys.dm_db_stats_属性

select
s.stats_id as [Stat ID], sc.name + ‘.’ + t.name as [Table], s.name as [Statistics]
,p.last_updated, p.rows, p.rows_sampled, p.modification_counter as [Mod Count]
from
sys.stats s join sys.tables t on
s.object_id = t.object_id
join sys.schemas sc on
t.schema_id = sc.schema_id
outer apply
sys.dm_db_stats_properties(t.object_id,s.stats_id) p
where
sc.name = ‘dbo’ and t.name = ‘Books’;
查询结果如图3-11所示,表明自上次统计信息更新以来,对统计信息列进行了250000次修改。你可以构建一个统计维护例程,定期检查sys.dm_db_stats_properties dmv,并使用较大的修改计数器值重建统计。
图3-11  s ys.dm_db_stats_属性输出
另一个与统计信息相关的数据库选项是异步自动更新统计信息。默认情况下,当SQL Server检测到统计信息过时时,它会暂停查询执行,同步更新统计信息,并在统计信息更新完成后生成新的执行计划。通过异步统计信息更新,SQL Server使用基于过时统计信息的旧执行计划执行查询,同时异步更新后台统计信息。建议你保持同步统计信息更新,除非系统有非常短的查询超时,在这种情况下,同步统计信息更新可以使查询超时。最后,在创建新索引时,SQL Server不会自动删除列级统计信息。你应该手动删除冗余的列级统计数据对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值