Nee*_*rma 8 performance sql-server database-internals
即使在REBUILD
索引之后,碎片聚集索引也表现不佳。如果索引是,REORGANIZED
则给定表/索引的性能增加。
我只在 SQL Server 2016 及更高版本上看到这种异常行为,我已经在不同的硬件和不同的版本上测试了这种情况(所有个人机器都具有传统的旋转硬盘)。如果需要更多信息,请告诉我。
这是 SQL Server 2016 及更高版本中的错误吗?
如果有人愿意,我可以提供完整的详细信息和脚本分析,但现在不提供,因为脚本非常大并且会在问题中占用大量空间。
如果您有 SQL Server 2016 及更高版本,请在您的 DEV 环境中测试从下面提供的链接中获取的示例脚本的较短版本。
-- SECTION 1
/*
Create a Test Folder in the machine and spefiy the drive in which you created
*/
USE MASTER
CREATE DATABASE RebuildTest
ON
( NAME = 'RebuildTest',
FILENAME = 'F:\TEST\RebuildTest_db.mdf',
SIZE = 200MB,
MAXSIZE = UNLIMITED,
FILEGROWTH = 50MB )
LOG ON
( NAME = 'RebuildTest_log',
FILENAME = 'F:\TEST\RebuildTest_db.ldf',
SIZE = 100MB,
MAXSIZE = UNLIMITED,
FILEGROWTH = 10MB ) ;
GO
BEGIN TRAN
USE RebuildTest
select top 1000000
row_number () over ( order by (Select null)) n into Numbers from
sys.all_columns a cross join sys.all_columns
CREATE TABLE [DBO].FRAG3 (
Primarykey int NOT NULL ,
SomeData3 char(1000) NOT NULL )
ALTER TABLE DBO.FRAG3
ADD CONSTRAINT PK_FRAG3 PRIMARY KEY (Primarykey)
INSERT INTO [DBO].FRAG3
SELECT n , 'Some text..'
FROM Numbers
Where N/2 = N/2.0
Update DBO.FRAG3 SET Primarykey = Primarykey-500001
Where Primarykey>500001
COMMIT
-- SECTION 2
SELECT @@VERSION
/* BEGIN PART FRAG1.1 */
----- BEGIN CLEANBUFFER AND DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER AND DATABASE AND MEASURE TIME
-------------BEGIN PART FRAG1.2: REBUILD THE INDEX AND TEST AGAIN
--BEGIN Rebuild the Index
Alter Table [DBO].[FRAG3] REBUILD
--END Rebuild the Index
----- BEGIN CLEANBUFFER FROM DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER FROM DATABASE AND MEASURE TIME
--BEGIN REORGANIZE the Index
ALTER INDEX ALL ON [DBO].[FRAG3] REORGANIZE ;
--END REORGANIZE the Index
----- BEGIN CLEANBUFFER FROM DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER FROM DATABASE AND MEASURE TIME
-------------BEGIN PART FRAG1.4: REBUILD THE INDEX AND TEST AGAIN
--BEGIN Rebuild the Index
Alter Table [DBO].[FRAG3] REBUILD
--END Rebuild the Index
----- BEGIN CLEANBUFFER FROM DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER FROM DATABASE AND MEASURE TIME
-------------END PART FRAG1.4: REBUILD THE INDEX AND TEST AGAIN
Run Code Online (Sandbox Code Playgroud)
我在 SQL Server 2016 及更高版本上看到存储引擎(可能)的异常行为,我创建了一个高度碎片化的表(读取碎片问题)演示目的,然后重建它。
即使在重建索引后,性能也没有按预期增加。为了确保数据访问模式应该按关键顺序而不是 IAM 驱动(分配顺序扫描),我使用了范围谓词。
最初,我认为 SQL Server 2016 及更高版本可能更适合大型扫描。为了检查这一点,我调整了页数和行数,但性能模式没有改变。我在个人系统上测试了所有内容,所以我可以说没有其他用户活动正在进行。
我也在其他硬件上测试了这种行为(都有传统的旋转硬盘)。性能模式几乎相同。
我已经检查了那里的等待统计数据似乎都很正常PAGELATCH_IO
(使用 Paul Randal 脚本)。我使用 DMV 检查了数据页sys.dm_db_database_page_allocations
,看起来也不错。
如果我重组表或将所有数据移动到具有相同索引定义的新表,磁盘 IO 性能会增加。我已经用 perfmon 进行了检查,似乎重组索引 / 新表可以利用顺序 IO 和重建索引仍然使用随机读取,尽管两者都有几乎相同的内部和外部碎片数据页。
我在我捕获的系统上附加了完整的查询和结果。如果你们有 SQL Server 2016 及更高版本的 DEV 框,请检查这个并分享你的结果。
警告:此测试包含一些未记录的命令,DROPCLEANBUFFERS
因此根本不在生产服务器上运行。
如果这真的是一个错误,我想我应该提交它。
所以问题是:它真的是一个错误还是我遗漏了什么;)
Dan*_*man 13
有问题的查询使用SQL Server 预读功能。通过预读性能优化,SQL Server 存储引擎在扫描期间预取数据,以便在查询需要时页面已经在缓冲区缓存中,从而减少查询执行期间等待数据的时间。
预读的执行时间差异反映了存储系统和 Windows API 处理大 IO 大小的好坏程度以及 SQL Server 预读行为的差异(因版本而异)。较旧的 SQL Server 版本(上述文章中的 SQL Server 2008 R2)将预取限制为 512K IO 大小,而 SQL Server 2016 及更高版本发出更大大小的预读 IO,以利用现代生产级商品硬件(RAID 和 SSD)的功能。请记住,SQL Server 通常经过优化,可在发布时在当前一代硬件上运行,利用更大的处理器缓存、NUMA 架构和存储系统 IOPS/带宽能力。此外,企业/开发人员版本还比较小版本更积极地执行预取,以更大限度地提高吞吐量。
为了更好地理解 SQL 2008 R2 与更高版本相比性能不同的原因,我在具有不同 SQL Server Developer Edition 版本的旧物理机上执行了脚本的修改版本。该测试盒同时具有 7200 RPM HDD 和 SATA SSD,允许在同一台机器上针对不同的存储系统和 SQL 版本运行相同的测试。我在每次测试期间使用扩展事件跟踪捕获了 file_read 和 file_read_completed 事件,以便对 IO 和时序进行更详细的分析。
结果显示,除了 SQL Server 2012 和更高版本在集群索引重建后,在单个 HDD 轴上的所有 SQL Server 版本和存储系统类型的性能大致相当。有趣的是,XE 跟踪仅在 SQL Server 2008 R2 中的预读扫描期间显示“连续”模式;跟踪显示所有其他版本都使用了“Scatter/Gather”模式。我不能说这种差异是否有助于提高性能。
此外,跟踪数据的分析显示 SQL 2016 在预读扫描期间发出更大的读取,平均 IO 大小因存储类型而异。这并不一定意味着 SQL Server 会根据物理硬件调整预读 IO 大小,而是它可能会根据未知测量值调整大小。存储引擎使用的启发式方法没有记录,可能因版本和补丁级别而异。
以下是测试时间的摘要。当我有更多时间时,我将添加从跟踪中收集的更多信息(不幸的是,IO 大小在 SQL Server 2008 R2 XE 中不可用)。总之,IO 配置文件因版本和存储类型而异。在这些测试中,SQL Server 2014 版本的平均 IO 大小从未超过 512K,而 SQL Server 2016 在单个 IO 中读取超过 4MB。SQL 2016 测试中未完成读取的数量也少得多,因为 SQL Server 完成相同工作的 IO 请求较少。
SQL_Version Storage Device Test Duration
SQL 2008 R2 HDD initial table 00:00:03.686
SQL 2012 HDD initial table 00:00:03.725
SQL 2014 HDD initial table 00:00:03.706
SQL 2016 HDD initial table 00:00:03.654
SQL 2008 R2 HDD fragmented table 00:00:07.796
SQL 2012 HDD fragmented table 00:00:08.026
SQL 2014 HDD fragmented table 00:00:07.837
SQL 2016 HDD fragmented table 00:00:06.097
SQL 2008 R2 HDD after rebuild 00:00:06.962
SQL 2012 HDD after rebuild 00:00:21.129
SQL 2014 HDD after rebuild 00:00:19.501
SQL 2016 HDD after rebuild 00:00:21.377
SQL 2008 R2 HDD after reorg 00:00:04.103
SQL 2012 HDD after reorg 00:00:03.974
SQL 2014 HDD after reorg 00:00:04.076
SQL 2016 HDD after reorg 00:00:03.610
SQL 2008 R2 HDD after reorg and rebuild 00:00:07.201
SQL 2012 HDD after reorg and rebuild 00:00:21.839
SQL 2014 HDD after reorg and rebuild 00:00:20.199
SQL 2016 HDD after reorg and rebuild 00:00:21.782
SQL 2008 R2 SATA SSD initial table 00:00:02.083
SQL 2012 SATA SSD initial table 00:00:02.071
SQL 2014 SATA SSD initial table 00:00:02.074
SQL 2016 SATA SSD initial table 00:00:02.066
SQL 2008 R2 SATA SSD fragmented table 00:00:03.134
SQL 2012 SATA SSD fragmented table 00:00:03.129
SQL 2014 SATA SSD fragmented table 00:00:03.129
SQL 2016 SATA SSD fragmented table 00:00:03.113
SQL 2008 R2 SATA SSD after rebuild 00:00:02.065
SQL 2012 SATA SSD after rebuild 00:00:02.097
SQL 2014 SATA SSD after rebuild 00:00:02.071
SQL 2016 SATA SSD after rebuild 00:00:02.078
SQL 2008 R2 SATA SSD after reorg 00:00:02.064
SQL 2012 SATA SSD after reorg 00:00:02.082
SQL 2014 SATA SSD after reorg 00:00:02.067
SQL 2016 SATA SSD after reorg 00:00:02.072
SQL 2008 R2 SATA SSD after reorg and rebuild 00:00:02.078
SQL 2012 SATA SSD after reorg and rebuild 00:00:02.087
SQL 2014 SATA SSD after reorg and rebuild 00:00:02.087
SQL 2016 SATA SSD after reorg and rebuild 00:00:02.079
Run Code Online (Sandbox Code Playgroud)
我还在具有 HDD 支持的 SAN 的 VM 上运行这些测试,并观察到与 SATA SSD 相似的性能。所以最重要的是,这个性能问题只发生在单轴 HDD 数据文件中,这仅在 PC 上常见,而不是现代生产系统。这是否应该被视为性能回归错误是有问题的,但我会联系看看我是否可以获得更多信息。
编辑
我联系了我的联系人,Erland Sommarskog 指出 sys.dm_db_index_physical_stats 在更高版本中报告的更高的片段计数。深入挖掘,我注意到 REBUILD 语句是一个并行查询。这意味着并行重建实际上可能会增加碎片计数(甚至在没有碎片的表上引入碎片),因为空间分配是并行完成的。除了预读扫描外,这通常不会影响性能,并且如测试所示,对于单轴旋转介质尤其如此。这是所有 SQL 版本中的一个考虑因素。
Paul Randal 指出这是设计使然,并参考了此文档以获取更多信息。对于将利用预读扫描(例如数据仓库)的工作负载进行索引重建的最佳实践是重建WITH (MAXDOP = 1)
;