针对非唯一聚簇索引的搜索的逻辑读取

时间:2011-05-05 15:53:27

标签: sql-server performance indexing

表格定义

CREATE  TABLE Accounts
(
AccountID INT ,
Filler CHAR(1000)
)

包含21行(每个AccountId值4,6,7为7行)。

它有1个根页面和4个叶子页面

index_depth page_count           index_level
----------- -------------------- -----------
2           4                    0
2           1                    1

根页面如

FileId      PageId      ROW         LEVEL       ChildFieldId ChildPageId AccountId (KEY) UNIQUIFIER (KEY) KeyHashValue
----------- ----------- ----------- ----------- ------------ ----------- --------------- ---------------- ------------------------------
1           121         0           1           1            119         NULL            NULL             NULL
1           121         1           1           1            151         6               0                NULL
1           121         2           1           1            175         6               3                NULL
1           121         3           1           1            215         7               1                NULL

这些页面上AccountId记录的实际分布是​​

AccountID   page_id     Num
----------- ----------- -----------
4           119         7
6           151         3
6           175         4
7           175         1
7           215         6

查询

SELECT AccountID 
FROM Accounts 
WHERE AccountID IN (4,6,7) 

提供以下IO统计信息

Table 'Accounts'. Scan count 3, logical reads 13

为什么?

我认为每次搜索它会寻找可能包含该值的第一页,然后(如果需要)continue along the linked list,直到找到第一行不等于搜索值。

然而,这最多只能增加10次页面访问

4)  Root Page -> Page 119 -> Page 151             (Page 151 Contains a 6 so should stop)
6)  Root Page -> Page 119 -> Page 151 -> Page 175 (Page 175 Contains a 7 so should stop)
7)  Root Page -> Page 175 -> Page 215             (No more pages)      

那么附加3的原因是什么?

重现的完整脚本

USE tempdb

SET NOCOUNT ON;

CREATE  TABLE Accounts
(
AccountID INT ,
Filler CHAR(1000)
)

CREATE CLUSTERED INDEX ix ON Accounts(AccountID)


INSERT INTO Accounts(AccountID)
SELECT C
FROM (SELECT 4 UNION ALL SELECT 6 UNION ALL SELECT 7) Vals(C)
CROSS JOIN (SELECT TOP (7) 1 FROM master..spt_values) T(X)

DECLARE @AccountID INT

SET STATISTICS IO ON
SELECT @AccountID=AccountID FROM Accounts WHERE AccountID IN (4,6,7) 
SET STATISTICS IO OFF

SELECT index_depth,page_count,index_level
FROM
sys.dm_db_index_physical_stats (2,OBJECT_ID('Accounts'), DEFAULT,DEFAULT, 'DETAILED')

SELECT AccountID, P.page_id, COUNT(*) AS Num
FROM Accounts
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%) P
GROUP BY AccountID, P.page_id
ORDER BY AccountID, P.page_id

DECLARE @index_info  TABLE
(PageFID  VARCHAR(10), 
  PagePID VARCHAR(10),   
  IAMFID   TINYINT, 
  IAMPID  INT, 
  ObjectID  INT,
  IndexID  TINYINT,
  PartitionNumber TINYINT,
  PartitionID BIGINT,
  iam_chain_type  VARCHAR(30),    
  PageType  TINYINT, 
  IndexLevel  TINYINT,
  NextPageFID  TINYINT,
  NextPagePID  INT,
  PrevPageFID  TINYINT,
  PrevPagePID INT, 
  PRIMARY KEY (PageFID, PagePID));

INSERT INTO @index_info 
    EXEC ('DBCC IND ( tempdb, Accounts, -1)'  ); 

DECLARE @DynSQL NVARCHAR(MAX) = 'DBCC TRACEON (3604);'
SELECT @DynSQL = @DynSQL + '
DBCC PAGE(tempdb, ' + PageFID + ', ' + PagePID + ', 3); '
FROM @index_info     
WHERE IndexLevel = 1

SET @DynSQL = @DynSQL + '
DBCC TRACEOFF(3604); '

CREATE TABLE #index_l1_info  
(FileId  INT, 
  PageId INT,   
  ROW   INT, 
  LEVEL  INT, 
  ChildFieldId  INT,
  ChildPageId INT,
  [AccountId (KEY)] INT,
  [UNIQUIFIER (KEY)] INT,
  KeyHashValue  VARCHAR(30));

INSERT INTO #index_l1_info  
EXEC(@DynSQL)


SELECT *
FROM #index_l1_info

DROP TABLE #index_l1_info
DROP TABLE Accounts

2 个答案:

答案 0 :(得分:3)

只是以答案的形式提供答案而不是评论中的讨论......

由于预读机制而产生附加读取。这将扫描叶级别的父页面,以防它需要发出异步IO以将叶级别页面放入缓冲区高速缓存中,以便在范围搜索到达它们时它们就绪。

可以使用跟踪标志652来禁用该机制(服务器范围),并验证读取的数量现在正好是预期的10个。

答案 1 :(得分:2)

根据我从DBCC IND的输出中看到的,有1个根页(type = 10),1个关键页(type = 2)和4个叶页(type = 1 ),总共6页。

所以每次扫描都为root -> key -> leaf -> … -> final leaf,为47提供4次读取,为6提供5次读取,总计为4 + 4 + 5 = 13