升序关键问题 - 标记为“Stationary”的领先列 - SQL Server

Nik*_*Nik 9 sql-server statistics sql-server-2012

我一直在研究我们数据库中运行缓慢的查询,并得出结论,这是一个经典的升序关键问题。由于几乎不断插入新行,并且每 30 分钟运行一次从数据库中提取最新数据的给定 SQL,每 30 分钟更新一次统计信息的第一个选项听起来可能会浪费资源。

因此,我查看了 Trace Flag 2389 原则上应该会有所帮助,但是这需要将 Lead 列标记为 Ascending,并且当我使用 Trace Flag 2388 检查(PK)索引统计信息时,我看到前导列实际上是标记为 Stationary - 因为它是同时更新的其他表上的几个 PK 索引。

在此处输入图片说明

似乎没有太多关于什么会导致固定品牌的指导,但是我确实发现KB2952101说如果少于 90% 的插入物大于旧的最大值,它将被归类为固定。我们所有的插入都是新提交的,前导列是一个 bigint IDENTITY 列,因此 100% 的插入应该大于先前的最大值。

所以我的问题是,当它明显是升序时,为什么该列会被标记为静止?

早期尝试为某些日常运行的 SQL(效果很好)解决此问题导致设置了一项作业来每晚更新此表的统计信息。更新不做全扫描,所以可能是采样扫描有时会丢失新行,所以它并不总是显示为升序?

我能想到的唯一可能会影响这一点的另一件事是,我们有一个存档作业在后台运行,删除超过特定年龄的行。这会影响品牌吗?

服务器是 SQL Server 2012 SP1。

更新:另一天,另一个统计数据更新 - 相同的固定品牌。自上次统计更新以来,已经有 28049 个新插入。每行都有一个插入时间的时间戳,所以如果我从表中选择 max(id) 其中 timestamp < '20161102' 我得到 23313455 同样,如果我在今天更新统计数据时这样做,我得到 23341504。

它们之间的区别在于 28049 个新插入,所以如您所见,所有新插入都被赋予了新的升序键(如预期),这表明前导列应该被标记为升序而不是固定。

在同一时期,我们的归档作业删除了 213,629 行(我们正在慢慢清除旧数据)。减少行数是否有可能有助于固定品牌?我之前测试过这个,它看起来没有任何区别。

更新 2:另一天,另一个统计数据更新,该列现在被标记为升序!根据有关删除影响的理论,我检查了与删除相比,插入更新的百分比,昨天 13% 是插入,而前两天插入约占 12%。我不认为这会给我们任何结论。

有趣的是,一个相关表平均每插入 4 行插入到这个主表中,并且同时更新了它的统计信息,它的 IDENTITY PK 列是否仍然是静止的!?

更新 3:在周末我们得到了更多的插入。今天早上领先的专栏回到了Stationary。在上一次统计更新中,我们有 46840 次插入,只有 34776 次删除。

同样,有趣的是,我上面提到的相关表现在将其领先列标记为升序。没有文件可以解释这个吗?

更新 4:现在已经一周左右了,归档作业已经清除了积压,所以我们一直在删除大约三分之二的插入行数。统计数据在相关表格中显示出混合结果,一个显示固定,两个显示上升,尽管它们都按类似的比例更新。

Sea*_*ser 4

似乎没有太多关于什么会导致固定式品牌的指导,但我确实发现 KB2952101 说,如果少于 90% 的插入件大于旧的最大值,它将被归类为固定式。我们所有的插入都是新提交的,并且前导列是 bigint IDENTITY 列,因此 100% 的插入应该大于先前的最大值。

所以我的问题是,当该列明显是升序时,为什么会被标记为“固定”?

如果像您已经说过的那样,如果 10% 或更多的插入物不上升,它将被标记为固定。如果 100% 的插入都如您所说...那么您可能不会遇到此问题,直到您删除,但随后它会返回未知状态。

这是您的问题的重现:

use master;
go
-- create a database for this to test
create database AscendingKey;
go

use AscendingKey;
go
-- create a test table
create table dbo.AscendingKeyTableTest
(
    SomeData        char(100) default('A'),
    AscendingKey    bigint not null,
);
go

-- insert some dummy data
set nocount on
go

declare @i int = 1

while(@i <= 1000)
begin
    insert into AscendingKeyTableTest(AscendingKey) VALUES (@i);
    set @i += 1
end
go

-- create stats on the ascendingkey column
create statistics AscendingKeyStats on dbo.AscendingKeyTableTest(AscendingKey);
go

-- look at the stats
dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);
-- unknown

-- now insert a few more ascending
declare @i int;
declare @j int = 1;

SELECT @i = max(ascendingkey) from dbo.AscendingKeyTableTest;

while(@j <= 10)
begin
    insert into AscendingKeyTableTest(AscendingKey) VALUES (@i+@j);
    set @j += 1
end
go

-- check again
dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);
-- unknown

-- update the stats
update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;

-- now insert a few more ascending
declare @i int;
declare @j int = 1;

SELECT @i = max(ascendingkey) from dbo.AscendingKeyTableTest;

while(@j <= 10)
begin
    insert into AscendingKeyTableTest(AscendingKey) VALUES (@i+@j);
    set @j += 1
end
go

-- update the stats
update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;

-- check again
dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);

-- now insert a few more ascending
declare @i int;
declare @j int = 1;

SELECT @i = max(ascendingkey) from dbo.AscendingKeyTableTest;

while(@j <= 10)
begin
    insert into AscendingKeyTableTest(AscendingKey) VALUES (@i+@j);
    set @j += 1
end
go

-- update the stats
update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;

-- check again
dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);
-- ascending!
-- we hit the 3x stats updates to have it 'learn'

-- what happens if we insert more than 10% that isn't ascending
declare @i int = 1;

while(@i <= 10)
begin
    insert into AscendingKeyTableTest(AscendingKey) VALUES (@i);
    set @i += 1
end
go

-- still says ascending... but...
dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);
go
-- what if we update again?
update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;
go
-- stationary
dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);
go
-- get it back to ascending
declare @i int;

SELECT @i = max(ascendingkey) from dbo.AscendingKeyTableTest;

insert into AscendingKeyTableTest(AscendingKey) VALUES (@i+1);
update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;

insert into AscendingKeyTableTest(AscendingKey) VALUES (@i+2);
update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;

insert into AscendingKeyTableTest(AscendingKey) VALUES (@i+3);
update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;
go

dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);
go

-- what about the deletes?
delete from AscendingKeyTableTest where AscendingKey % 3 = 0
go

update statistics ascendingkeytabletest(ascendingkeystats) with fullscan;
go

dbcc traceon(2388);
dbcc show_statistics('dbo.ascendingkeytabletest', ascendingkeystats);
dbcc traceoff(2388);
go
-- back to unknown

-- cleanup
use master
go

drop database AscendingKey
go
Run Code Online (Sandbox Code Playgroud)