Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 标识(主键聚集)表中的间隙是否会影响数据库的性能?_Sql Server_Database Design_Database Performance_Sql Server 2014_Database Tuning - Fatal编程技术网

Sql server 标识(主键聚集)表中的间隙是否会影响数据库的性能?

Sql server 标识(主键聚集)表中的间隙是否会影响数据库的性能?,sql-server,database-design,database-performance,sql-server-2014,database-tuning,Sql Server,Database Design,Database Performance,Sql Server 2014,Database Tuning,好了,百万美元的问题来了 假设我有下表 SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[tblUsersProfile]( [personId] [int] IDENTITY(1,1) NOT NULL, [personName] [varchar](16) NOT NULL, [personSurName] [varchar](1

好了,百万美元的问题来了

假设我有下表

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[tblUsersProfile](
    [personId] [int] IDENTITY(1,1) NOT NULL,
    [personName] [varchar](16) NOT NULL,
    [personSurName] [varchar](16) NOT NULL,
 CONSTRAINT [PK_tblUsersProfile] PRIMARY KEY CLUSTERED 
(
    [personId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO
现在让我们假设这个表有200万条记录 因此,下一个
标识id
2000001

但是,我正在进行大规模删除,记录数变为45321 但是,下一个
标识id
仍然是
2000001

所以用娱乐重新排列桌子会有什么不同

在第一种情况下,将有45321条记录,但标识id将为2000001

在第二种情况下,将再次出现45321条记录,但标识id将为45322

那么,这两种情况之间是否存在性能、存储等方面的差异

多谢各位


SQL Server 2014

否。因为它是一个主键数据库引擎,将负责这些记录的物理保存方式。

扩展我的评论,进一步解释。为清楚起见,评论是:


不会,不会对性能产生影响。由于这是您的聚类键,因此无论种子是45322还是2000001,记录都将被输入到聚集索引上记录45321之后的下一个可用空间。标识列的值是没有意义的,如果不是,您可能使用不正确。在这样一次大的删除之后,您可能会得到一些索引碎片,但是标识种子与此完全无关

关于碎片,在一个非常简化的示例中,您可能有一个5页的表,每页有100条记录:

  • 第1页(ID 1-100)
  • 第2页(IDs 101-200)
  • 第3页(IDS201-300)
  • 第4页(IDS301-400)
  • 第5页(IDS401-500)
现在,如果执行删除操作,并删除最后一位数字不是1的所有记录,以及ID超过300的所有记录,您将获得:

  • 第1页(ID 1、11、21、31、41、51、61、71、81、91)
  • 第2页(ID 11111121131141151161171181191)
  • 第3页(识别号21、211、221、231、241、251、261、271、281、291)
  • 第4页(空)
  • 第5页(空)
当我们现在插入这个表时,无论下一个标识是291还是501,它都不会改变任何东西。页面必须保持正确的顺序,因此最高ID为291,因此在此之后必须在同一页面上插入下一条记录(如果有空间),否则将创建一个新页面。在这种情况下,第3页上有9个空槽,因此可以在那里插入下一条记录。因为292和500都高于291,所以行为是相同的

在这两种情况下,问题仍然是,删除后,您有3个页面,其中有大量的可用空间(只有10%的空间已满),您现在只有30条记录,很高兴可以放在一个页面上,因此您可以重建索引来完成此操作,因此现在您只需要读取一个页面即可获取所有数据

我再次强调,这是一个非常简单的示例,我不建议重建聚集索引以释放2个页面

还需要强调的是,这种行为是因为ID列是集群键,而不是主键。它们不一定是一个,但是,如果您在标识列之外的其他内容上进行集群,那么在删除之后是否对其重新设定种子,对性能仍然没有影响。标识列的存在纯粹是为了标识,只要您能够唯一地标识一行,实际值就不相关


样本测试代码

-- CREATE TABLE AND FILL WITH 100,000 ROWS
IF OBJECT_ID(N'dbo.DefragTest', 'U') IS NOT NULL
    DROP TABLE dbo.DefragTest;

CREATE TABLE dbo.DefragTest (ID INT IDENTITY(1, 1) PRIMARY KEY, Filler CHAR(1) NULL);
INSERT dbo.DefragTest (Filler)
SELECT TOP 100000 NULL
FROM sys.all_objects AS a, sys.all_objects AS b;


-- CHECK PAGE STATISTICS
SELECT  Stage = 'After Initial Insert',
        IdentitySeed = IDENT_CURRENT(N'dbo.DefragTest'),
        p.rows, 
        a.total_pages,
        a.data_pages,
        AvgRecordsPerPage = CAST(p.rows / CAST(a.data_pages AS FLOAT) AS DECIMAL(10, 2))
FROM    sys.partitions AS p
        LEFT JOIN sys.allocation_units AS a
            ON a.container_id = p.partition_id
WHERE   p.[object_id] = OBJECT_ID(N'dbo.DefragTest', 'U')
AND     p.index_id IN (0, 1); -- CLUSTERED OR HEAP

-- DELETE RECORDS
DELETE  dbo.DefragTest
WHERE   ID % 10 != 1
OR      ID > 50000;

-- CHECK PAGE STATISTICS
SELECT  Stage = 'After Delete',
        IdentitySeed = IDENT_CURRENT(N'dbo.DefragTest'),
        p.rows, 
        a.total_pages,
        a.data_pages,
        AvgRecordsPerPage = CAST(p.rows / CAST(a.data_pages AS FLOAT) AS DECIMAL(10, 2))
FROM    sys.partitions AS p
        LEFT JOIN sys.allocation_units AS a
            ON a.container_id = p.partition_id
WHERE   p.[object_id] = OBJECT_ID(N'dbo.DefragTest', 'U')
AND     p.index_id IN (0, 1); -- CLUSTERED OR HEAP  

-- RESEED (REMOVED FOR ONE RUN)
DBCC CHECKIDENT ('dbo.DefragTest', RESEED, 50000);

--INSERT ROWS TO SEE EFFECT ON PAGE
INSERT dbo.DefragTest (Filler)
SELECT TOP 10000 NULL
FROM sys.all_objects AS a;

-- CHECK PAGE STATISTICS
SELECT  Stage = 'After Second Insert',
        IdentitySeed = IDENT_CURRENT(N'dbo.DefragTest'),
        p.rows, 
        a.total_pages,
        a.data_pages,
        AvgRecordsPerPage = CAST(p.rows / CAST(a.data_pages AS FLOAT) AS DECIMAL(10, 2))
FROM    sys.partitions AS p
        LEFT JOIN sys.allocation_units AS a
            ON a.container_id = p.partition_id
WHERE   p.[object_id] = OBJECT_ID(N'dbo.DefragTest', 'U')
AND     p.index_id IN (0, 1); -- CLUSTERED OR HEAP  

-- CHECK READS REQUIRED FOR FULL TABLE SCAN
SET STATISTICS IO ON;
SELECT COUNT(Filler)
FROM dbo.DefragTest;

-- REBUILD INDEX
ALTER INDEX PK_DefragTest__ID ON dbo.DefragTest REBUILD;

-- CHECK PAGE STATISTICS
SELECT  Stage = 'After Index Rebuild',
        IdentitySeed = IDENT_CURRENT(N'dbo.DefragTest'),
        p.rows, 
        a.total_pages,
        a.data_pages,
        AvgRecordsPerPage = CAST(p.rows / CAST(a.data_pages AS FLOAT) AS DECIMAL(10, 2))
FROM    sys.partitions AS p
        LEFT JOIN sys.allocation_units AS a
            ON a.container_id = p.partition_id
WHERE   p.[object_id] = OBJECT_ID(N'dbo.DefragTest', 'U')
AND     p.index_id IN (0, 1); -- CLUSTERED OR HEAP  

-- CHECK READS REQUIRED FOR FULL TABLE SCAN

SELECT COUNT(Filler)
FROM dbo.DefragTest;

SET STATISTICS IO OFF;  
重新设定种子的输出:

Stage                   IdentitySeed    rows    total_pages     data_pages  AvgRecordsPerPage
After Initial Insert    100000          100000  178             174         574.71
After Delete            100000          5000    90              87          57.47
After Second Insert     52624           7624    98              91          83.78
After Index Rebuild     52624           7624    18              14          544.57
Stage                   IdentitySeed    rows    total_pages     data_pages  AvgRecordsPerPage
After Initial Insert    100000          100000  178             174         574.71
After Delete            100000          5000    90              87          57.47
After Second Insert     102624          7624    98              91          83.78
After Index Rebuild     52624           7624    18              14          544.57
表'DefragTest'。扫描计数1,逻辑读取93(重建前计数)

表'DefragTest'。扫描计数1,逻辑读取16(重建后计数)

不重新设定种子的输出:

Stage                   IdentitySeed    rows    total_pages     data_pages  AvgRecordsPerPage
After Initial Insert    100000          100000  178             174         574.71
After Delete            100000          5000    90              87          57.47
After Second Insert     52624           7624    98              91          83.78
After Index Rebuild     52624           7624    18              14          544.57
Stage                   IdentitySeed    rows    total_pages     data_pages  AvgRecordsPerPage
After Initial Insert    100000          100000  178             174         574.71
After Delete            100000          5000    90              87          57.47
After Second Insert     102624          7624    98              91          83.78
After Index Rebuild     52624           7624    18              14          544.57
表'DefragTest'。扫描计数1,逻辑读取93(重建前计数)

表'DefragTest'。扫描计数1,逻辑读取16(重建后计数)


如您所见,在每种情况下都没有差异,在数据的存储或读取方式上,只有
IDENT_INCR()
的值会发生变化,在这两种情况下,重建聚集索引会大大减少页数,这反过来又提高了查询性能,因为获得相同数据量的逻辑读取更少。

否,不会对性能产生影响。由于这是您的聚类键,因此无论种子是45322还是2000001,记录都将被输入到聚集索引上记录45321之后的下一个可用空间。标识列的值是没有意义的,如果不是,您可能使用不正确。在这样一次大的删除之后,您可能会得到一些索引碎片,但标识种子与此完全无关。@GarethD ty for reply所以在索引碎片整理之后,它们应该具有相同的性能,对吗?在索引碎片整理之前,它们将具有相同的性能。在删除一个表的大部分之后,索引碎片整理只是一个值得考虑的问题。@ GarethD这么大删除之后,我想SQL Server物理上将所有剩余的记录重新排序正确吗?尽量不要混淆<代码>主键< /代码>和<代码>聚集索引< /代码>,尽管在这个问题的情况下它们是相同的,数据库负责记录的物理保存方式,因为这是主键,这一说法不正确。这是因为它是集群密钥,而集群密钥恰好也是主键。这是一个重要的区别。