即使您回滚事务,更新和插入也可能导致索引碎片

时间:2014-09-30 12:40:15

标签: sql-server sql-server-2008 sql-server-2005 sql-server-2008-r2 sql-server-2012

我知道,索引碎片的一些原因是:

非顺序插入 - 在执行非顺序插入时,SQL Server将大约50%的数据从旧页面移动到新分配的页面。这将导致页面拆分,每个页面都有来自旧页面的大约50%的数据。

使用较大的值更新现有行值,该值不适合同一页面

我听说即使您回滚事务,碎片仍然存在,但我找不到相关文档。

是否有人有这方面的文件或证明这一点的脚本?

1 个答案:

答案 0 :(得分:0)

今天我做了一些测试,结果并不完全符合我的预期。

环境:

Microsoft SQL Server 2008(SP2) - 10.0.4000.0(X64)2010年9月16日19:43:16版权所有(c)1988-2008 Microsoft Corporation企业版(64位)在Windows NT 6.0上(Build 6002:Service)包2)

首先,我找了一张已经碎片的表。 令人惊讶的是,在我的DBA数据库中,我找到了一个名为tableSizeBenchmark的表。

USE [DBA]
GO

CREATE TABLE [dbo].[tableSizeBenchmark](
[lngID] [bigint] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[dbName] [varchar](100) NOT NULL,
[tableName] [varchar](100) NOT NULL,
[creationDate] [smalldatetime] NOT NULL,
[numberOfRows] [bigint] NULL,
[spaceUsedMb] [numeric](18, 0) NULL,
 CONSTRAINT [PK_tableSizeBenchmark] PRIMARY KEY CLUSTERED 
(
[lngID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
 ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) 
  ON [PRIMARY]
) ON [PRIMARY]

GO
USE [DBA]
GO
CREATE UNIQUE NONCLUSTERED INDEX [UIXtableSizeBenchmark] ON [dbo].[tableSizeBenchmark] 
(
[dbName] ASC,
[tableName] ASC,
[creationDate] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, 
 IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 
 ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

这是在进行任何测试之前的碎片级别:

enter image description here

您需要创建这两个程序才能进行相同的测试。 基本上我使用随机字符串生成器和随机数生成器,只是因为我想插入10,000条记录并查看如何使碎片变得更糟,稍后在ROLLBACK事务中看到真实,如果碎片仍然存在或消失。

--DROP PROCEDURE GetRandomString
--GO
--DROP PROCEDURE GetRandomNumber
--GO
create procedure GetRandomString (@STR VARCHAR(100) OUTPUT)
as
begin

-- generates a random string
-- marcelo miorelli
-- 01-oct-2014

-- one of the other features that makes this more flexible:
-- By repeating blocks of characters in @CharPool, 
-- you can increase the weighting on certain characters so that they are more likely to be chosen.

DECLARE @z INT
      , @i INT
      , @MIN_LENGTH INT
      , @MAX_LENGTH INT


DECLARE @CharPool VARCHAR(255)
DECLARE @RandomString VARCHAR(255)
DECLARE @PoolLength INT

SELECT @MIN_LENGTH = 20
SELECT @MAX_LENGTH = 100

--SET @z = RAND() * (@max_length - @min_length + 1) + @min_length
SET @Z = 50
-- define allowable character explicitly - easy to read this way an easy to 
-- omit easily confused chars like l (ell) and 1 (one) or 0 (zero) and O (oh)
SET @CharPool = 
    'abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789.,-_!$@#%^&*'

SET @CharPool = 
    'ABCDEFGHIJKLMNPQRSTUVWXYZ'
SET @PoolLength = Len(@CharPool)

SET @i = 0
SET @RandomString = ''

WHILE (@i < @z) BEGIN
    SELECT @RandomString = @RandomString + 
        SUBSTRING(@Charpool, CONVERT(int, RAND() * @PoolLength), 1)
    SELECT @i = @i + 1
END


SELECT @STR = @RandomString

end
GO

create procedure GetRandomNumber (@number int OUTPUT)
as
begin


-- generate random numbers
-- marcelo miorelli
-- 01-oct-2014

DECLARE @maxval INT, @minval INT
select @maxval=10000,@minval=500

SELECT @Number = CAST(((@maxval + 1) - @minval) *
    RAND(CHECKSUM(NEWID())) + @minval AS INT)


end 
go

创建上述过程后,请参阅下面用于运行此测试的代码:

SELECT object_id AS ObjectID,
  object_NAME (Object_id) as Table_NAME,
  index_id AS IndexID,
  avg_fragmentation_in_percent AS PercentFragment,
  fragment_count AS TotalFrags,
  avg_fragment_size_in_pages AS PagesPerFrag,
  page_count AS NumPages

FROM sys.dm_db_index_physical_stats(DB_ID('dba'),
  NULL, NULL, NULL , 'DETAILED')
WHERE OBJECT_ID = OBJECT_ID('tableSizeBenchmark')
and avg_fragmentation_in_percent > 0

所以结果如下: 在运行上述脚本但BEFORE ROLLBACK或COMMIT之后,事务仍处于打开状态:

enter image description here

请注意碎片已经增加。 在我们回滚此交易后,这种碎片会重新发生还是消失?

让我在这里发帖,我用来查看碎片级别的脚本:

SELECT object_id AS ObjectID,
  object_NAME (Object_id) as Table_NAME,
  index_id AS IndexID,
  avg_fragmentation_in_percent AS PercentFragment,
  fragment_count AS TotalFrags,
  avg_fragment_size_in_pages AS PagesPerFrag,
  page_count AS NumPages

FROM sys.dm_db_index_physical_stats(DB_ID('dba'),
  NULL, NULL, NULL , 'DETAILED')
WHERE OBJECT_ID = OBJECT_ID('tableSizeBenchmark')
and avg_fragmentation_in_percent > 0

以及该实验的结果:

enter image description here

正如您所看到的,碎片级别在交易之前回到了原始状态。

希望这有帮助

马塞洛