日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

SQL SERVER大话存储结构(2)

發(fā)布時間:2025/3/19 数据库 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SQL SERVER大话存储结构(2) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

閱讀目錄(Content)

  • 1 行記錄如何存儲
    • 1.1 堆表
    • 1.2 聚集索引表格
  • 2 非聚集索引結(jié)構(gòu)
  • 3 非聚集索引鍵值內(nèi)容
    • 3.1 堆表上的非聚集索引
    • 3.2 聚集索引表(唯一)的非聚集索引
    • 3.3 聚集索引表(非唯一)的非聚集索引
  • 4 非聚集索引如何查找頁


如果轉(zhuǎn)載,請注明博文來源:?www.cnblogs.com/xinysu/? ?,版權(quán)歸 博客園 蘇家小蘿卜 所有。望各位支持! 本系列上一篇博文鏈接:SQL SERVER大話存儲結(jié)構(gòu)(1)_數(shù)據(jù)頁類型及頁面指令分析

回到頂部(go to top)

1 行記錄如何存儲

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

1.1 堆表

堆表,沒有聚集索引的表格,可以創(chuàng)建一個或者多個非聚集索引。沒有按照某個規(guī)則進行存儲,一般來說,按照行記錄入表的順序,但是由于性能要求,可能會在不同區(qū)域移動入庫數(shù)據(jù)。像一堆沙子一樣,沒有明確的組織順序。 堆的 sys.partitions 中具有一行,對于堆使用的每個分區(qū),都有 index_id = 0。默認情況下,一個堆有一個分區(qū)。 當(dāng)堆有多個分區(qū)時,每個分區(qū)有一個堆結(jié)構(gòu),其中包含該特定分區(qū)的數(shù)據(jù)。例如,如果一個堆有四個分區(qū),則有四個堆結(jié)構(gòu),每個分區(qū)有一個堆結(jié)構(gòu)。 根據(jù)堆中的數(shù)據(jù)類型,每個堆結(jié)構(gòu)將有一個或多個分配單元來存儲和管理特定分區(qū)的數(shù)據(jù)。每個堆中每個分區(qū)至少有一個 IN_ROW_DATA 分配單元。如果堆包含大型對象 (LOB) 列,則該堆的每個分區(qū)還將有一個 LOB_DATA 分配單元。如果堆包含超過 8,060 字節(jié)的行大小限制的變量長度列,則它的每個分區(qū)中還會有一個 ROW_OVERFLOW_DATA 分配單元。 sys.system_internals_allocation_units系統(tǒng)視圖中的列 first_iam_page 指向 IAM 頁鏈中的第一個 IAM 頁,該 IAM 頁鏈可管理分配給特定分區(qū)中的堆的空間。 SQL Server 使用 IAM 頁在堆之間移動。堆內(nèi)的數(shù)據(jù)頁和行沒有任何特定的順序,也不鏈接在一起。數(shù)據(jù)頁之間唯一的邏輯連接是記錄在 IAM 頁內(nèi)的信息。

?

擁有聚集索引的表格,稱為聚集索引表,每個表格按照其聚集索引的排序規(guī)則進行存儲,但是這里注意一點,在一個頁面中,并非 行記錄 按照 其聚集索引排序規(guī)則,而是 行偏移量 按照其排序規(guī)則存儲。

1.2 聚集索引表格

在 SQL Server 中,索引是按 B 樹結(jié)構(gòu)進行組織的。 索引 B 樹中的每一頁稱為一個索引節(jié)點。 B 樹的頂端節(jié)點稱為根節(jié)點。 索引中的底層節(jié)點稱為葉節(jié)點。 根節(jié)點與葉節(jié)點之間的任何索引級別統(tǒng)稱為中間級。 在聚集索引中,葉節(jié)點包含基礎(chǔ)表的數(shù)據(jù)頁。 根節(jié)點和中間級節(jié)點包含存有索引行的索引頁。 每個索引行包含一個鍵值和一個指針,該指針指向 B 樹上的某一中間級頁或葉級索引中的某個數(shù)據(jù)行。 每級索引中的頁均被鏈接在雙向鏈接列表中。 聚集索引在 sys.partitions 中有一行,其中,索引使用的每個分區(qū)的 index_id = 1。 默認情況下,聚集索引有單個分區(qū)。 當(dāng)聚集索引有多個分區(qū)時,每個分區(qū)都有一個包含該特定分區(qū)相關(guān)數(shù)據(jù)的 B 樹結(jié)構(gòu)。 例如,如果聚集索引有四個分區(qū),就有四個 B 樹結(jié)構(gòu),每個分區(qū)中有一個 B 樹結(jié)構(gòu)。 根據(jù)聚集索引中的數(shù)據(jù)類型,每個聚集索引結(jié)構(gòu)將有一個或多個分配單元,將在這些單元中存儲和管理特定分區(qū)的相關(guān)數(shù)據(jù)。 每個聚集索引的每個分區(qū)中至少有一個 IN_ROW_DATA 分配單元。 如果聚集索引包含大型對象 (LOB) 列,則它的每個分區(qū)中還會有一個 LOB_DATA 分配單元。 如果聚集索引包含的變量長度列超過 8,060 字節(jié)的行大小限制,則它的每個分區(qū)中還會有一個 ROW_OVERFLOW_DATA 分配單元。 數(shù)據(jù)鏈內(nèi)的頁和行將按聚集索引鍵值進行排序。 所有插入操作都在所插入行中的鍵值與現(xiàn)有行中的排序順序相匹配時執(zhí)行。 下圖顯式了聚集索引單個分區(qū)中的結(jié)構(gòu)。 由此,可以看出,堆表不存在特定的存儲順序,一般按照INSERT的順序存儲,但是有時因為性能需求,也會四處存放數(shù)據(jù);而聚集索引表的數(shù)據(jù)行按照聚集鍵的排序情況存儲,葉子節(jié)點即為行記錄。 回到頂部(go to top)

2 非聚集索引結(jié)構(gòu)

無論是堆表還是聚集索引表格,都可以創(chuàng)建非聚集索引。非聚集索引頁也是B-TREE結(jié)構(gòu),但是,有幾點不同:非聚集索引不影響基礎(chǔ)表的存儲順序,其葉子節(jié)點是有索引頁組成而非數(shù)據(jù)頁組成。 當(dāng)需要通過非聚集索引尋找行記錄時,先是在非聚集索引所在的B-TREE樹查找,找到相應(yīng)的葉子節(jié)點后,在根據(jù)該鍵值上的相應(yīng) 行定位器 去查找其所指向的 行記錄位置。 那么,行定位器是怎么樣的呢? 這個還需要去分析 非聚集索引的鍵值內(nèi)容,才可以清晰了解,詳見下文的分析案例。 回到頂部(go to top)

3 非聚集索引鍵值內(nèi)容

創(chuàng)建3個表格:堆表、聚集索引非唯一表及聚集索引唯一表,并且創(chuàng)建非聚集索引,同時INSERT 部分數(shù)據(jù)。 --創(chuàng)建堆表 create table tb_heap(id int ,name varchar(100),age int) --創(chuàng)建聚集索引(非唯一)表 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) --創(chuàng)建聚集索引且鍵值唯一表 create table tb_pk(id int primary key identity(1,1) ,name varchar(100),age int) --創(chuàng)建非聚集索引 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) --造數(shù)據(jù) 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) 可以得出這些結(jié)論:
  • pageid=238是IAM頁,判斷依據(jù)是:IAMFID=NULL;
  • tb_heap上的非聚集索引ix_tb_heap_name的B tree結(jié)構(gòu)有2層,判斷依據(jù)是:IndexLevel最大值為1;
  • B-tree樹中,根頁為 pageid=239,葉子節(jié)點的最左節(jié)點葉是 235? ??
依據(jù)IndexLevel、NextPagePid及PrevPagePid,可以畫出?ix_tb_heap_name 的數(shù)據(jù)結(jié)構(gòu)如下(畫圖工具崩了,用自帶畫圖小工具話的,這圖丑出天際): 選取pageid=235,來分析非聚集索引頁上的結(jié)構(gòu)。 dbcc traceon(3604) dbcc page('dbpage',1,235,3) 查看 ` 消息`? ,可以看到,這個是索引頁,目前上面存儲260行索引鍵值,該頁空閑空間12個字節(jié),空閑空間從第7660字節(jié)開始。 查看 `結(jié)果` ,如下: 可以看到在這個頁面上,每一行的行記錄情況,可以看到 非聚集索引的鍵值有2部分:name 跟 HEAD RID,name因為是非聚集索引的列,所以理應(yīng)存儲,RID是什么呢? RID除了可以從dbcc page中查詢,也可以通過偽列查詢:%%physloc%%。 select *,%%physloc%% as RID from tb_heap RID實際上是用來 唯一標識 堆表中的每一行數(shù)據(jù),占8個字節(jié),按以下格式標識行:{ file id }:{ page id }:{ slot id},文件號:數(shù)據(jù)頁號:槽位,從存儲的角度唯一表示了一行數(shù)據(jù)。 但是從dbcc的結(jié)果看,這是一個16進制的數(shù)值,該如何轉(zhuǎn)化呢? 轉(zhuǎn)換規(guī)則:分為8個字節(jié)->前4bytes為page id->中間2bytes為file id->最后2bytes為slot id->反序排列->取10進制 用中的RID來實驗下如何反解析。 --1 分為8個字節(jié) 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'中,有一行行數(shù)據(jù)存儲在 第一個文件中的第233頁面的149槽位 dbcc page('dbpage',1,233,3)

?

由此,可以推出:在堆表中,非聚集索引的鍵值包含兩部分:索引列 以及 RID,RID用于查找索引鍵值對應(yīng)的行記錄。

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

#會話窗口查看ind,需要打開 3604跟蹤 dbcc traceon(3604) dbcc ind('dbpage','tb_pk',2) 根據(jù)2.1的推論,一樣可以得出這些結(jié)論:
  • pageid=121是IAM頁,判斷依據(jù)是:IAMFID=NULL;
  • tb_pk上的非聚集索引ix_tb_pk_name的B tree結(jié)構(gòu)有2層,判斷依據(jù)是:IndexLevel最大值為1;
  • B-tree樹中,根頁為 pageid=126,葉子節(jié)點的最左節(jié)點葉是 120。

?

依據(jù)IndexLevel、NextPagePid及PrevPagePid,可以畫出?ix_tb_pk_name 的數(shù)據(jù)結(jié)構(gòu)如下: 選取pageid=120,來分析非聚集索引頁上的結(jié)構(gòu)。 dbcc traceon(3604) dbcc page('dbpage',1,120,3) 查看 ` 消息`? ,可以看到,這個是索引頁,目前上面存儲296行索引鍵值,該頁空閑空間86個字節(jié),空閑空間從第7514字節(jié)開始。 查看 ` 結(jié)果`? ,可發(fā)現(xiàn),在 聚集索引且唯一的表格里邊,非聚集索引有2部分:鍵值列+主鍵列。這個相對比較好理解,因為在建立了聚集唯一索引的表格里邊,其聚集索引鍵值可以唯一標識每一行的行記錄,所以,在非聚集索引上,只需要包含這兩部分。

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

#會話窗口查看ind,需要打開 3604跟蹤 dbcc traceon(3604) dbcc ind('dbpage','tb_clu_no_unique',2) 根據(jù)2.1的推論,一樣可以得出這些結(jié)論:
  • pageid=172是IAM頁,判斷依據(jù)是:IAMFID=NULL;
  • tb_pk上的非聚集索引tb_clu_no_unique的B tree結(jié)構(gòu)有2層,判斷依據(jù)是:IndexLevel最大值為1;
  • B-tree樹中,根頁為 pageid=174,葉子節(jié)點的最左節(jié)點葉是 171? ?
選取pageid=171,來分析非聚集索引頁上的結(jié)構(gòu)。 dbcc traceon(3604) dbcc page('dbpage',1,171,3) 查看 ` 消息`? ,可以看到,這個是索引頁,目前上面存儲298行索引鍵值,該頁空閑空間4個字節(jié),空閑空間從第7592字節(jié)開始。 查看 ` 結(jié)果`? ,注意列后面括號'(key)',這個表明為鍵值對組成部分,這里,發(fā)現(xiàn)有之前沒有看到的鍵值列 UNIQUIFIER列。 ?? 那么,UNIQUIFIER列,這一列是用來做什么的呢? 這里,為了更好的理解UNIQUIFIER列,需要新建一個新表,INSERT少量重復(fù)聚集索引鍵值的行記錄。 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) 可發(fā)現(xiàn),在 聚集索引且非唯一的表格里邊,非聚集索引有3部分:鍵值列+主鍵列+UNIQUIFIER列。建立了聚集非唯一索引,表的存儲順序按照聚集索引順序,但是僅靠聚集索引無法唯一標識每一行的行記錄,所以,需要添加 UNIQUIFIER列來唯一標識。 總結(jié):
  • 堆表 的 非聚集索引 鍵值內(nèi)容:索引列+RID
  • 聚集且唯一索引表 的非聚集索引 鍵值內(nèi)容:索引列+主鍵列
  • 聚集且非唯一索引表 的非聚集索引 鍵值內(nèi)容:索引列+主鍵列+UNIQUIFIER列
回到頂部(go to top)

4 非聚集索引如何查找頁

根據(jù)第二部分,可以很清楚每類型的非聚集索引的組成部分。 在堆表中,非聚集索引根據(jù)其鍵值內(nèi)的RID列,直接進行物理查找,從fileid找到pageid,在找到slotid來定位到行記錄,這個也就是所謂的書簽查找,根據(jù)RID查找。 在聚集且唯一的索引表中,非聚集索引根據(jù)其鍵值內(nèi)部的 聚集索引列,找到聚集索引的B-TREE,根據(jù) B-TREE 樹找到聚集索引的鍵值,鍵值下的葉子節(jié)點則為行記錄。 在聚集其非唯一索引表中,非聚集索引根據(jù)其鍵值內(nèi)部的 聚集索引列,找到聚集索引的B-TREE,根據(jù) B-TREE 樹找到聚集索引的鍵值,這里會有些不一樣了,根據(jù)找到的鍵值,鍵值下的葉子節(jié)點可能會有多行記錄,這個時候,就需要uniquifier來識別行記錄。 參考文檔: https://msdn.microsoft.com/zh-cn/library/mt786796.aspx 《SQL Server性能調(diào)優(yōu)實戰(zhàn)》

轉(zhuǎn)載于:https://www.cnblogs.com/zhaolizhe/p/6953866.html

總結(jié)

以上是生活随笔為你收集整理的SQL SERVER大话存储结构(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。