1. 程式人生 > >SQL SERVER大話儲存結構(2)_非聚集索引如何查詢到行記錄

SQL SERVER大話儲存結構(2)_非聚集索引如何查詢到行記錄

1 行記錄如何儲存

    這裡引入兩個概念:堆跟聚集索引表。本部分參考MSDN。

1.1 堆表

    堆表,沒有聚集索引的表格,可以建立一個或者多個非聚集索引。沒有按照某個規則進行儲存,一般來說,按照行記錄入表的順序,但是由於效能要求,可能會在不同區域移動入庫資料。像一堆沙子一樣,沒有明確的組織順序。     堆的 sys.partitions 中具有一行,對於堆使用的每個分割槽,都有 index_id = 0。預設情況下,一個堆有一個分割槽。 當堆有多個分割槽時,每個分割槽有一個堆結構,其中包含該特定分割槽的資料。例如,如果一個堆有四個分割槽,則有四個堆結構,每個分割槽有一個堆結構。     根據堆中的資料型別,每個堆結構將有一個或多個分配單元來儲存和管理特定分割槽的資料。每個堆中每個分割槽至少有一個 IN_ROW_DATA 分配單元。如果堆包含大型物件 (LOB) 列,則該堆的每個分割槽還將有一個 LOB_DATA 分配單元。如果堆包含超過 8,060 位元組的行大小限制的變數長度列,則它的每個分割槽中還會有一個 ROW_OVERFLOW_DATA 分配單元。     sys.system_internals_allocation_units系統檢視中的列 first_iam_page 指向 IAM 頁鏈中的第一個 IAM 頁,該 IAM 頁鏈可管理分配給特定分割槽中的堆的空間。 SQL Server 使用 IAM 頁在堆之間移動。堆內的資料頁和行沒有任何特定的順序,也不連結在一起。資料頁之間唯一的邏輯連線是記錄在 IAM 頁內的資訊。
        擁有聚集索引的表格,稱為聚集索引表,每個表格按照其聚集索引的排序規則進行儲存,但是這裡注意一點,在一個頁面中,並非 行記錄 按照 其聚集索引排序規則,而是 行偏移量 按照其排序規則儲存。

1.2 聚集索引表格

    在 SQL Server 中,索引是按 B 樹結構進行組織的。 索引 B 樹中的每一頁稱為一個索引節點。 B 樹的頂端節點稱為根節點。 索引中的底層節點稱為葉節點。 根節點與葉節點之間的任何索引級別統稱為中間級。 在聚集索引中,葉節點包含基礎表的資料頁。 根節點和中間級節點包含存有索引行的索引頁。 每個索引行包含一個鍵值和一個指標,該指標指向 B 樹上的某一中間級頁或葉級索引中的某個資料行。 每級索引中的頁均被連結在雙向連結列表中。     聚集索引在 sys.partitions 中有一行,其中,索引使用的每個分割槽的 index_id = 1。 預設情況下,聚集索引有單個分割槽。 當聚集索引有多個分割槽時,每個分割槽都有一個包含該特定分割槽相關資料的 B 樹結構。 例如,如果聚集索引有四個分割槽,就有四個 B 樹結構,每個分割槽中有一個 B 樹結構。     根據聚集索引中的資料型別,每個聚集索引結構將有一個或多個分配單元,將在這些單元中儲存和管理特定分割槽的相關資料。 每個聚集索引的每個分割槽中至少有一個 IN_ROW_DATA 分配單元。 如果聚集索引包含大型物件 (LOB) 列,則它的每個分割槽中還會有一個 LOB_DATA 分配單元。 如果聚集索引包含的變數長度列超過 8,060 位元組的行大小限制,則它的每個分割槽中還會有一個 ROW_OVERFLOW_DATA 分配單元。     資料鏈內的頁和行將按聚集索引鍵值進行排序。 所有插入操作都在所插入行中的鍵值與現有行中的排序順序相匹配時執行。     下圖顯式了聚集索引單個分割槽中的結構。    
     由此,可以看出,堆表不存在特定的儲存順序,一般按照INSERT的順序儲存,但是有時因為效能需求,也會四處存放資料;而聚集索引表的資料行按照聚集鍵的排序情況儲存,葉子節點即為行記錄。

2 非聚集索引結構

    無論是堆表還是聚集索引表格,都可以建立非聚集索引。非聚集索引頁也是B-TREE結構,但是,有幾點不同:非聚集索引不影響基礎表的儲存順序,其葉子節點是有索引頁組成而非資料頁組成。          當需要通過非聚集索引尋找行記錄時,先是在非聚集索引所在的B-TREE樹查詢,找到相應的葉子節點後,在根據該鍵值上的相應 行定位器 去查詢其所指向的 行記錄位置。       那麼,行定位器是怎麼樣的呢?
      這個還需要去分析 非聚集索引的鍵值內容,才可以清晰瞭解,詳見下文的分析案例。

3 非聚集索引鍵值內容

    建立3個表格:堆表、聚集索引非唯一表及聚集索引唯一表,並且建立非聚集索引,同時INSERT 部分資料。 --建立堆表 create table tb_heap(id int ,name varchar(100),age int) --建立聚集索引(非唯一)表 create table tb_clu_no_unique(id int identity(1,1) ,name varchar(100),age int) create CLUSTERED  index ix_clu_id on tb_clu_no_unique(id) --建立聚集索引且鍵值唯一表 create table tb_pk(id int primary key identity(1,1) ,name varchar(100),age int) --建立非聚集索引 create index ix_tb_pk_name on tb_pk(name) create index ix_tb_heap_name on tb_heap(name) create index ix_tb_clu_no_unique_name on tb_clu_no_unique(name) --造資料 insert into tb_pk(name,age) select name,cast(rand()*100 as int) from master.dbo.spt_values where name is not null insert into tb_clu_no_unique(name,age) select name,age from tb_pk insert into tb_heap(id,name,age) select id,name,age from tb_pk

3.1 堆表上的非聚集索引

#會話視窗檢視ind,需要開啟 3604跟蹤 dbcc traceon(3604) dbcc ind('dbpage','tb_heap',2)         可以得出這些結論:
  • pageid=238是IAM頁,判斷依據是:IAMFID=NULL;
  • tb_heap上的非聚集索引ix_tb_heap_name的B tree結構有2層,判斷依據是:IndexLevel最大值為1;
  • B-tree樹中,根頁為 pageid=239,葉子節點的最左節點葉是 235    
    依據IndexLevel、NextPagePid及PrevPagePid,可以畫出 ix_tb_heap_name 的資料結構如下(畫圖工具崩了,用自帶畫圖小工具話的,這圖醜出天際):     選取pageid=235,來分析非聚集索引頁上的結構。 dbcc traceon(3604) dbcc page('dbpage',1,235,3)     檢視 ` 訊息`  ,可以看到,這個是索引頁,目前上面儲存260行索引鍵值,該頁空閒空間12個位元組,空閒空間從第7660位元組開始。         檢視 `結果` ,如下:       可以看到在這個頁面上,每一行的行記錄情況,可以看到 非聚集索引的鍵值有2部分:name 跟 HEAD RID,name因為是非聚集索引的列,所以理應儲存,RID是什麼呢?   RID除了可以從dbcc page中查詢,也可以通過偽列查詢:%%physloc%%。 select *,%%physloc%% as RID from tb_heap       RID實際上是用來 唯一標識 堆表中的每一行資料,佔8個位元組,按以下格式標識行:{ file id }:{ page id }:{ slot id},檔案號:資料頁號:槽位,從儲存的角度唯一表示了一行資料。     但是從dbcc的結果看,這是一個16進位制的數值,該如何轉化呢?     轉換規則:分為8個位元組->前4bytes為page id->中間2bytes為file id->最後2bytes為slot id->反序排列->取10進位制     用中的RID來實驗下如何反解析。 --1 分為8個位元組 E9 00 00 00 01 00 95 00 --2 前4bytes為page id E9 00 00 00 --3 中間2bytes為file id 01 00 --4 最後2bytes為slot id 95 00 --5 反序排列並取10進位制 pageid,反序後為 00 00 00 E9,十進位制為16*14+9=233 fileid,反序後為 00 01,十進位制為 1 slotid,反序後為 00 95,十進位制為 149 則可以推算出,name='backup device'中,有一行行資料儲存在 第一個檔案中的第233頁面的149槽位 dbcc page('dbpage',1,233,3)       由此,可以推出:在堆表中,非聚集索引的鍵值包含兩部分:索引列 以及 RID,RID用於查詢索引鍵值對應的行記錄。

3.2 聚集索引表(唯一)的非聚集索引

#會話視窗檢視ind,需要開啟 3604跟蹤 dbcc traceon(3604) dbcc ind('dbpage','tb_pk',2)     根據2.1的推論,一樣可以得出這些結論:
  • pageid=121是IAM頁,判斷依據是:IAMFID=NULL;
  • tb_pk上的非聚集索引ix_tb_pk_name的B tree結構有2層,判斷依據是:IndexLevel最大值為1;
  • B-tree樹中,根頁為 pageid=126,葉子節點的最左節點葉是 120。
      依據IndexLevel、NextPagePid及PrevPagePid,可以畫出 ix_tb_pk_name 的資料結構如下:       選取pageid=120,來分析非聚集索引頁上的結構。 dbcc traceon(3604) dbcc page('dbpage',1,120,3)     檢視 ` 訊息`  ,可以看到,這個是索引頁,目前上面儲存296行索引鍵值,該頁空閒空間86個位元組,空閒空間從第7514位元組開始。       檢視 ` 結果`  ,可發現,在 聚集索引且唯一的表格裡邊,非聚集索引有2部分:鍵值列+主鍵列。這個相對比較好理解,因為在建立了聚集唯一索引的表格裡邊,其聚集索引鍵值可以唯一標識每一行的行記錄,所以,在非聚集索引上,只需要包含這兩部分。  

3.3 聚集索引表(非唯一)的非聚集索引

#會話視窗檢視ind,需要開啟 3604跟蹤 dbcc traceon(3604) dbcc ind('dbpage','tb_clu_no_unique',2)     根據2.1的推論,一樣可以得出這些結論:
  • pageid=172是IAM頁,判斷依據是:IAMFID=NULL;
  • tb_pk上的非聚集索引tb_clu_no_unique的B tree結構有2層,判斷依據是:IndexLevel最大值為1;
  • B-tree樹中,根頁為 pageid=174,葉子節點的最左節點葉是 171   
  選取pageid=171,來分析非聚集索引頁上的結構。 dbcc traceon(3604) dbcc page('dbpage',1,171,3)     檢視 ` 訊息`  ,可以看到,這個是索引頁,目前上面儲存298行索引鍵值,該頁空閒空間4個位元組,空閒空間從第7592位元組開始。          檢視 ` 結果`  ,注意列後面括號'(key)',這個表明為鍵值對組成部分,這裡,發現有之前沒有看到的鍵值列 UNIQUIFIER列。            那麼,UNIQUIFIER列,這一列是用來做什麼的呢?    這裡,為了更好的理解UNIQUIFIER列,需要新建一個新表,INSERT少量重複聚集索引鍵值的行記錄。 create table tb_clu_no_unique_2(id int  ,name varchar(100),age int) create CLUSTERED  index ix_clu_i_2 on tb_clu_no_unique_2(id) CREATE INDEX IX_tb_clu_no_unique_2_NAME ON tb_clu_no_unique_2(NAME) INSERT INTO tb_clu_no_unique_2(ID,NAME,AGE) SELECT 1,'A',3; INSERT INTO tb_clu_no_unique_2(ID,NAME,AGE) SELECT 1,'B',3; INSERT INTO tb_clu_no_unique_2(ID,NAME,AGE) SELECT 2,'C',3; INSERT INTO tb_clu_no_unique_2(ID,NAME,AGE) SELECT 2,'D',3; INSERT INTO tb_clu_no_unique_2(ID,NAME,AGE) SELECT 2,'E',3; DBCC TRACEON(3604) DBCC IND('dbpage','tb_clu_no_unique_2',2) DBCC PAGE('dbpage',1,306,3)       可發現,在 聚集索引且非唯一的表格裡邊,非聚集索引有3部分:鍵值列+主鍵列+UNIQUIFIER列。建立了聚集非唯一索引,表的儲存順序按照聚集索引順序,但是僅靠聚集索引無法唯一標識每一行的行記錄,所以,需要新增 UNIQUIFIER列來唯一標識。     總結:
  • 堆表 的 非聚集索引 鍵值內容:索引列+RID
  • 聚集且唯一索引表 的非聚集索引 鍵值內容:索引列+主鍵列
  • 聚集且非唯一索引表 的非聚集索引 鍵值內容:索引列+主鍵列+UNIQUIFIER列

4 非聚集索引如何查詢頁

    根據第二部分,可以很清楚每型別的非聚集索引的組成部分。     在堆表中,非聚集索引根據其鍵值內的RID列,直接進行物理查詢,從fileid找到pageid,在找到slotid來定位到行記錄,這個也就是所謂的書籤查詢,根據RID查詢。     在聚集且唯一的索引表中,非聚集索引根據其鍵值內部的 聚集索引列,找到聚集索引的B-TREE,根據 B-TREE 樹找到聚集索引的鍵值,鍵值下的葉子節點則為行記錄。     在聚集其非唯一索引表中,非聚集索引根據其鍵值內部的 聚集索引列,找到聚集索引的B-TREE,根據 B-TREE 樹找到聚集索引的鍵值,這裡會有些不一樣了,根據找到的鍵值,鍵值下的葉子節點可能會有多行記錄,這個時候,就需要uniquifier來識別行記錄。 參考文件: 《SQL Server效能調優實戰》

相關推薦

SQL SERVER大話儲存結構2_聚集索引如何查詢記錄

1 行記錄如何儲存     這裡引入兩個概念:堆跟聚集索引表。本部分參考MSDN。 1.1 堆表     堆表,沒有聚集索引的表格,可以建立一個或者多個非聚集索引。沒有按照某個規則進行儲存,一般來說,按照行記錄入表的順序,但是由於效能要求,可能會在不同區域移動入庫資料

SQL SERVER大話儲存結構3_資料結構

    一行資料是如何來儲存的呢?     變長列與定長列,NULL與NOT NULL,實際是如何整理存放到 8k的資料頁上呢?     對錶格進行增減列,修改長度,新增預設值等DDL SQL,對行儲存結構又會有怎麼樣的影響呢?     什麼是大物件,什麼是行溢位,儲存引擎是如何處理它們呢?

SQL SERVER大話儲存結構1_資料頁型別及頁面指令分析

    SQLServer的資料頁大小是8kb,8個連續的物理頁組成一個區。區分混合區跟統一區,混合區內可以儲存不同資料庫物件的內容,通常這個資料庫物件較小;統一區表示區內連續的8個數據頁儲存的都是同一個資料庫物件的內容。     SQL SERVER的磁碟讀寫是按頁級進行,每次I/O操作的資料最小

全廢話SQL Server統計信息2——統計信息基礎

position amp 要去 fault href 過程 字符串 最大 實用 接上文:http://blog.csdn.net/dba_huangzj/article/details/52835958我想在大地上畫滿窗子,

java呼叫sql server儲存過程dbutils

一般我們在寫介面或者後臺的時候,java用的是springmvc框架,而連線資料庫則用到的是jdbc(原始的),為了更加方便,通過配置檔案的方式連線資料庫(sql server2008),通過兩天的學習,和自己的揣摩,終於弄出來了,為了給一些需要的又剛好沒有找到這個方法的程

SQL SERVER大話存儲結構3_數據結構

bits 基礎 就會 mar ant rain 版權 bpa 一個數 一行數據是如何來存儲的呢? 變長列與定長列,NULL與NOT NULL,實際是如何整理存放到 8k的數據頁上呢? 對表格進行增減列,修改長度,添加默認值等DDL S

大話資料結構——線性表順序儲存結構的java實現

    在看《大話資料結構》的時候,裡面詼諧的語言和講解吸引了我,但是這本書是用C來實現的,但是作為一個手擼java的人就想著用java來實現一下這些資料結構,於是就有了這些大話資料結構之java實現。哈哈,感覺這樣會讓自己的理解加深不少。 &n

通用的可帶查詢條件的SQL語句的分頁儲存過程2

程式碼二: CREATE PROCEDURE pagination @tblName varchar(255), – 表名 @strGetFields varchar(1000) = ‘*’, – 需要返回的列 @fldName varch

sql server建立儲存過程有參,無參,有輸出

student表已經有了,“建立”在中間,測試在最下面! select * from student; sid sname sex age tel s001 豐登兒 男 35 13527542451 s002 班克爾

LoadRunner內部結構2

res 文件 tar ngs attr 關聯 exec -s telnet LoadRunner內部結構(2) 接著(1)的內容: 17.默認的LRReport文件夾創建在本地分析機器的My Documents文件夾下來存儲分析會話文件. 18.可以使用HTML

SQL SERVER的鎖機制——概述鎖的種類與範圍

row 定性 針對 共享 互斥 drop 問題 停止 共享鎖 SQL SERVER的鎖機制系列: SQL SERVER的鎖機制(一)——概述(鎖的種類與範圍) SQL SERVER的鎖機制(二)——概述(鎖的兼容性與可以鎖定的資源) SQL SERVER的鎖機制(三)

C語言程序設計第六次作業——循環結構2.

是什麽 絕對值 方法 程序設計 輸入一個數 系列 發生 自己 很好 (一)改錯題 序列求和:輸入一個正實數eps,計算序列部分和 1 - 1/4 + 1/7 - 1/10 + ... ,精確到最後一項的絕對值小於eps(保留6位小數)。   輸入輸出樣例:   Input

C語言程序設計第六次作業——循環結構2

但是 n-1 輸入輸出 printf 控制 運行 進行 定義 small (一)改錯題 序列求和:輸入一個正實數eps,計算序列部分和 1 - 1/4 + 1/7 - 1/10 + ... ,精確到最後一項的絕對值小於eps(保留6位小數)。   輸入輸出樣例:   Inp

SQL Server 2012安裝配置Part1

數據庫 SQL Server 1 安裝前準備安裝 SQL Server 2012 服務器及客戶端前,需要提前做以下兩項準備:SQL Server2012 依賴於.Net Framework 3.5.1 組件。Windows Server 2012 缺省未安裝該組件,所以需要手動安裝。為保證SQL Se

SQL Server 2012安裝配置Part2

數據庫 SQL Server SQL Server 2012安裝配置(Part1 )2. 服務器安裝運行安裝程序後,首先進入 SQL Server 安裝中心。選擇左側導航樹中的“安裝”菜單項。圖2-1 SQL Server 安裝中心 在右側菜單中點擊“全新 SQL Server 獨立安裝或向現有安裝添

SQL Server 2012安裝配置Part3

數據庫 SQL Server SQL Server 2012安裝配置(Part1 ) SQL Server 2012安裝配置(Part2 ) ? 3 客戶端安裝 3.1 安裝客戶端 功能選擇之前的操作與 SQL Server 安裝方法相同,在此不再贅述,下面將直接從功能選擇開始。 圖3-1 功能

SQL Server 2012安裝配置Part4

數據庫 SQL Server SQL Server 2012安裝配置(Part1)SQL Server 2012安裝配置(Part2)SQL Server 2012安裝配置(Part3 )SQL Server 2012安裝配置(Part4 ) 5 卸載 點擊系統“開始”按鈕,選擇“控制面板”菜單項,

SQL Server中有關約束constraint的一些細節

並發 自動生成 fault 自動 方式 view str 自己 數據庫 原文:SQL Server中有關約束(constraint)的一些細節 本文出處:http://www.cnblogs.com/wy123/p/7350265.html (保留出處並非什麽原創作品權

大話資料結構——棧的兩種java實現方式

在我們生活當中經常會看到這樣一種操作,比如我們往一個空羽毛球盒子裡面放羽毛球(個人比較喜歡羽毛球,嘿嘿),放完後再將羽毛球一個一個取出的時候會發現,最先放進去的羽毛球往往最後才取出來,相反,最後放入的羽毛球往往最先取出。這個例子形象的說明了棧的操作方式,下面我們來看看什麼是棧,以及棧的一些操

大話資料結構——雙向連結串列的java實現

在實現了單向連結串列後,我們在使用單向連結串列中會發現一個問題:在單向連結串列中查詢某一個結點的下一個結點的時間複雜度是O(1),但是查詢這個結點的上一個結點的時候,時間複雜度的最大值就變成了O(n),因為在查詢這個指定結點的上一個結點時又需要從頭開始遍歷。 那麼該如何解決這個困難呢?