Sql-Server

將身份列重新設置為 0 會導致頁面拆分嗎?

  • October 11, 2018

以標識表作為聚集主鍵的簡單表。我們意外地達到了該列的最大值,因此我要求我們的 DBA 將表重新設置為 0,以便給我們一些時間來檢查更新該列。該表也會根據時間被清除,因此在我們與任何東西發生碰撞之前,我們有很大的 Id 差距可以用完。由於這是一個高插入表,DBA 提出了分頁的可能性,因為我們不再在表的末尾插入。

我的問題是,當標識列的值環繞時,SQL Server 將如何表現?插入間隙會導致性能問題嗎?我想考慮創建一個序列,該序列將自動循環而不是從 id 列向上循環到 bigint。

我的問題是,當標識列的值環繞時,SQL Server 將如何表現?

假設索引是 100% 滿的,新鍵值處的前幾次插入將導致一到兩次頁面拆分,其中將整頁上的一半行複製到新頁面,然後將新行插入其中一個半滿頁。

但是,在那之後*,*您將恢復低成本的插入和頁面分配。當頁面填滿時,分配一個新的空頁面並將其插入到葉子頁面的雙向鍊錶中。沒有行被複製到新頁面,新行被寫入新頁面。

這與索引結尾插入之間的唯一區別是在新頁面之後存在*下一頁。*並且必須使用指向新頁面的反向頁面指針來更新下一頁。

有關詳細資訊,請參閱此文章:良好的頁面拆分和順序 GUID 密鑰生成

插入間隙會導致性能問題嗎?

不。

隨著 SQL 繼續插入到索引的“中間”,當插入的目標頁面已滿時,SQL 將始終分配一個新頁面。它不會將該行放在下一個現有頁面上,即使該頁面未滿。聚集索引的非葉級儲存每個葉級頁面的起始值,因此 SQL 無法在不重寫索引的非葉級的情況下將新行插入到下一個現有頁面。

您可以通過強制頁面鎖定並查看插入的鎖定佔用空間來輕鬆查看此行為(或者使用 DBCC PAGE 不太容易)。在下面的範例中,一個頁面上可以恰好容納 4 行的聚集索引表採用一系列中間索引插入。

假設每頁可以容納 4 行。您有整頁現有行,其鍵值從 10 到 20 遞增,現在您要插入鍵值 1-9。

use master
drop database ps_test
go
create database ps_test
go
use ps_test 
go
drop table if exists ps_test
set nocount on


go

--8,060=4*(4+2011)
create table ps_test(id int primary key, data char(2011) not null default '')

go
DBCC TRACEON(3604) 
go

insert into ps_test(id) values (10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21)--,22

go

dbcc ind('ps_test','ps_test',1)
--three data pages
/*

PageFID PagePID         PageType
------- ------- / /     --------
1       336             10      
1       328             1       
1       329             2       
1       330             1       
1       331             1       
*/


insert into ps_test(id) values (1)

dbcc ind('ps_test','ps_test',1)
--four data pages 332 has been inserted between 328 and 330
/*

PageFID PagePID        PageType PrevPagePID NextPagePID 
------- ---------/ /   -------- ----------- ----------- 
1       336            10       0           0           
1       328            1        0           332         
1       329            2        0           0           
1       330            1        332         331         
1       331            1        330         0           
1       332            1        328         330         
*/

dbcc page(ps_test,1,328,3)
-- 328 has rows 1,10 and 4048 bytes free
-- the page is split
/*
PAGE: (1:328)
...
pminlen = 2019                      m_slotCnt = 2                       m_freeCnt = 4048
...
Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 1                              
...
Slot 1 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 10                             

DBCC execution completed. If DBCC printed error messages, contact your system administrator.
*/

dbcc page(ps_test,1,332,3)
--page 332 has 11,12,13 and 2024 bytes free
/*

PAGE: (1:332)
...
pminlen = 2019                      m_slotCnt = 3                       m_freeCnt = 2024
...
Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 11                             
...
Slot 1 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 12                             
...
Slot 2 Column 1 Offset 0x4 Length 4 Length (physical) 4
id = 13                             
...
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
*/

insert into ps_test(id) values (2)
insert into ps_test(id) values (3)

dbcc ind('ps_test','ps_test',1)
--no page split.  
/*

PageFID PagePID     PageType IndexLevel NextPageFID NextPagePID PrevPageFID PrevPagePID
------- ----------- -------- ---------- ----------- ----------- ----------- -----------
1       336         10       NULL       0           0           0           0
1       328         1        0          1           332         0           0
1       329         2        1          0           0           0           0
1       330         1        0          1           331         1           332
1       331         1        0          0           0           1           330
1       332         1        0          1           330         1           328

the new rows went on page 328, which now has

pminlen = 2019                      m_slotCnt = 4                       m_freeCnt = 2024
*/

所以現在我們的第 328 頁已滿,而下一頁 332 只有 3 行。但是 332 上的最小值是 11。那麼當我們插入值 4 時,是否會繼續 332,這會在插入 5 時導致另一個壞的頁面拆分?

insert into ps_test(id) values (4)
dbcc ind('ps_test','ps_test',1)

/*
A new page 333 has been inserted in the list between 328 and 332

PageFID PagePID     PageType IndexLevel NextPageFID NextPagePID PrevPageFID PrevPagePID
------- ----------- -------- ---------- ----------- ----------- ----------- -----------
1       336         10       NULL       0           0           0           0
1       328         1        0          1           333         0           0
1       329         2        1          0           0           0           0
1       330         1        0          1           331         1           332
1       331         1        0          0           0           1           330
1       332         1        0          1           330         1           333
1       333         1        0          1           332         1           328

*/

dbcc page(ps_test,1,333,3)

/*

PAGE: (1:333)
...
pminlen = 2019                      m_slotCnt = 1                       m_freeCnt = 6072
...
Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 10                             
...
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

*/

所以另一個“壞頁拆分”,但這個只移動了 id=10 的行。並將第 328 頁填滿,值為 1,2,3,4

dbcc page(ps_test,1,328,3)
/*
PAGE: (1:328)
...
pminlen = 2019                      m_slotCnt = 4                       m_freeCnt = 0
...
Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 1  
...
Slot 1 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 2 
...
Slot 2 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 3  
...
Slot 3 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 4                              
...                        
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

*/

現在所有較大的鍵值都在單獨的頁面上,從這裡開始不再有壞的頁面拆分。下一個插入需要一個新頁面,並且只有新行在新頁面上:

   insert into ps_test(id) values (5)
   dbcc ind('ps_test','ps_test',1)

/*

PageFID PagePID      PageType IndexLevel NextPageFID NextPagePID PrevPageFID PrevPagePID
------- -----------  -------- ---------- ----------- ----------- ----------- -----------
1       336          10       NULL       0           0           0           0
1       328          1        0          1           334         0           0
1       329          2        1          0           0           0           0
1       330          1        0          1           331         1           332
1       331          1        0          0           0           1           330
1       332          1        0          1           330         1           333
1       333          1        0          1           332         1           334
1       334          1        0          1           333         1           328

*/

新頁面 334,只有最後插入的行。

dbcc page(ps_test,1,334,3)
/*
PAGE: (1:334)
...
pminlen = 2019                      m_slotCnt = 1                       m_freeCnt = 6072
...
Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 5     
...
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
id = 5                              


*/

因此,在需要移動行的幾個初始“壞頁拆分”之後,隨後新頁被分配並拼接到葉頁的雙向鍊錶的中間。當新行不適合前一頁並且沒有行遷移到新頁時,分配新頁。

引用自:https://dba.stackexchange.com/questions/219699