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

来源:http://www.cnblogs.com/xinysu/archive/2017/05/10/6831012.html
-Advertisement-
Play Games

如果轉載,請註明博文來源: www.cnblogs.com/xinysu/ ,版權歸 博客園 蘇家小蘿蔔 所有。望各位支持! 1 行記錄如何存儲 這裡引入兩個概念:堆跟聚集索引表。本部分參考MSDN。 1.1 堆表 堆表,沒有聚集索引的表格,可以創建一個或者多個非聚集索引。沒有按照某個規則進行存儲, ...


 

   

      如果轉載,請註明博文來源: www.cnblogs.com/xinysu/   ,版權歸 博客園 蘇家小蘿蔔 所有。望各位支持!   

 

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來識別行記錄。   參考文檔: https://msdn.microsoft.com/zh-cn/library/mt786796.aspx 《SQL Server性能調優實戰》  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們開發中常用到圖片上傳,比如頭像上傳之類的,還有類似發佈空間說說時發佈多張圖片等等,我在這裡通過借鑒網路資源,並依賴於七牛雲存儲做了一個圖片上傳的小結。 我首先封裝了一個圖片上傳的工具類,繼承自nsobject,命名為QiniuTool;再多圖上傳時,還需要單張圖片的上傳結果處理,因此,我在這裡有 ...
  • 該View轉自 http://blog.csdn.net/Kalwang/article/details/4708721 ,感謝這位大神。 ...
  • 此次掃碼功能以iOS系統原生的AVFoundation框架為基礎。 廢話不多說,直接上代碼 #import <UIKit/UIKit.h> @interface ScanViewController : UIViewController @end 在.m文件中創建對象 #import "ScanVi ...
  • 1.原代碼提示快捷鍵為:Ctrl+空格,與Windows輸入法衝突,所以將代碼提示快捷鍵設置為:Ctrl+反斜杠。 ...
  • 入行快10年,有點積蓄,三年前買了代步車。於是乎,汽車油耗開銷就成了每個月都必須關註的問題。三年來,用過了無數油耗記錄軟體,比如最知名的“小熊油耗”,從第一次用,一直到最新一版,感覺越來越“臃腫”,功能實在是太豐富了,甚至都做到“大數據”這一層面,作為一個小白車主,其實,我只關心:我的車油耗如何。 ...
  • RunLoop是iOS線程相關的比較重要的一個概念,無論是主線程還是子線程,都對應一個RunLoop,如果沒有RunLoop,線程會馬上被系統回收。 本文主要CFRunLoop的源碼解析,並簡單闡述一下CFRunLoop的原理。 CFRunLoop是開源的,開源地址在:http://opensour ...
  • //將字元串轉化成Drawable public synchronized static Drawable StringToDrawable(String icon) { if (icon == null || icon.length() < 10) return null; byte[] img ... ...
  • 小狼咕咕最近開啟了微信小程式開發的徵程,由於微信小程式的前後臺通信必須通過https協議,所以小狼咕咕第一件要做的事就是配置一個能夠通過https訪問的後臺服務。小狼咕咕用的是阿裡雲ECS伺服器,Linux系統,安裝的tomcat。 打開阿裡雲盾——CA證書服務,找不到的朋友也可以在下圖的菜單中直接 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...