SQL Server數據庫中的頁影響數據庫性能的表現
此文章主要向大家描述的是SQL Server數據庫中的頁影響數據庫性能的表現,我們大家都知道無論哪個數據庫,如果你需要對其性能進行優化的話,那么你必須要了解數據庫內部的存儲結構。
否則的話,很多數據庫的優化工作無法展開。對于對于數據庫管理員來說,雖然學習數據庫的內存存儲結構比較單調,但是卻是我們必須攻下的一個堡壘。在SQL Server數據庫中,數據頁是其存儲的最基本單位。系統無論是在保存數據還是在讀取數據的時候,都是以頁為單位來進行操作的。
一、數據頁的基本組成。
如上圖所示,是SQL Server數據庫中頁的主要組成部分。從這個圖中可以看出,一個數據頁基本上包括三部分內容,分別為標頭、數據行和行偏移量。其中數據行存儲的是數據本身,其他的標頭與偏移量都是一些輔助的內容。對于這個數據頁來說,筆者認為數據庫管理員必須要了解如下的內容。
一是要了解數據頁的大小。在SQL Server數據庫中數據頁的大小基本上是固定的,即每個數據頁的大小都為8KB,8192個字節。其中每頁開頭都有一個標頭,其占據了96個字節,用于存儲有關頁的信息。如這個頁被分配到頁碼、頁的類型、頁的可用空間以及擁有這個頁的對象的分配單元ID等等信息。不過值得慶幸的是,這些內容數據庫都會自動管理與更新,不需要數據庫管理員擔心。數據庫管理員只需要知道的是,這個數據頁中最多可以用來保存數據的空間。
每個頁的大小是8192個字節,扣除掉一些必要的開銷(如標頭信息或者偏移量所占用的空間),一般其可以用來實際存儲數據的空間只有8000字節左右。牢記這個數字,對于后續數據庫性能的優化具有很大的作用。詳細的內容筆者在后續行溢出的部分會進行說明。
二是需要注意行的放置順序。在每個數據頁上,數據行緊接著標頭按順序放置。在頁的末尾有一張行偏移表。對于頁中的每一行,每個行偏移表都包含有一個條目。即如果業中的數據行達到100條的話,則在這個行偏移表中就對英100個條目。每個條目記錄中記錄對應行的第一個字節與頁首的距離。
如第二個跳就記錄著第二個數據行的行首字母到數據頁頁首的位置。由于每個數據行的大小都是不同的,為此這個行偏移表中記錄的內容也是沒有規律的。這里需要注意的是,行偏移表中的條目順序與頁中行的順序是相反的。這主要是為了更方便數據庫定位數據行。
二、大數據類型與行。
根據SQLServer數據庫定義的規則,行是不能夠跨頁的。如上圖所示,如果一個字段的數據值非常大,其超過8000字節。此時一個頁已經不能夠容納這個數據。此時數據庫會如何處理呢?雖然說在SQL Server數據庫中,行是不能夠跨頁的。但是可以將行分成兩部分,分別存儲在不同的行中。所以說,對于大數據類型來說,是不受到這個頁大小(或者說行大小)的限制的。根據上面的分析可以看出,一個數據頁其最大可以用的存儲空間在8KB。
如果扣掉一些必要的開銷,其只有8000字節左右。當某條記錄的所有列(包括固定長度的列與可變長度的列其大小超過這個限制的時候,數據庫就會將其進行分行處理,分別存儲在兩個不同的頁中。
當某張表格中列的總大小超過限制的8KB(實際上還還不到一點)字節時,數據庫系統會從最大長度的列開始動態的將一個或多個可變長度列移動到另外一個頁中。簡單的說,就是將某個列超過的部分單獨存放在另一個頁中。并且同時還會存儲一些指針之類的信息,以便在不同頁的記錄中建立關聯。這種現象在SQL Server數據庫中給其取了一個名字,叫做行溢出。
三、行溢出對于數據庫性能的不利影響。
掌握了上面關于數據頁的基本工作原理后,數據庫管理員需要重點理解行溢出對于數據庫性能的不利影響。即需要了解,當所有列(包括固定長度的列與可變長度的列)的累積長度超過一個數據頁(或者一個數據行)的最大承受限度時,會將列的內容分行來進行存放。數據庫如此處理,對數據庫的性能會有不利的影響嗎?如果有的話,該如何避免?
一般來說,每行的記錄超過頁的最大容量時,肯定會對數據庫的性能造成不利的影響。這是毋庸置疑的。因為當超過這個容量時,數據庫系統就需要對這個數據行進行分頁處理。而分頁處理需要數據庫額外的開銷。
如在分頁保存時,需要給數據庫添加額外的指針;在查詢數據的時候,由于分頁情況的存在,為了讀取一條完整的記錄,數據庫系統可能不得不讀取多頁的內容;當進行更新操作,將某個字段的內容變短,導致整行的內容在頁的最大范圍之內,則相關的記錄會被保存在同一個行中。這些操作都需要數據庫額外的開銷。當在同一個時間處理這些作業多了,那么積累起來,對數據庫性能的影響就會很顯著。同理,此時如果對相關的記錄進行排序、統計等操作,由于涉及到多個頁,會延長這些作業的執行時間,即降低數據庫的性能。
其次需要注意的是對一些變長字段的限制。在SQLServre數據庫中,也含有varchar等變長的數據類型。在SQLServer數據庫中對此有最大長度的限制。一般情況下,其最大長度不能夠超過不能夠超過8000字節的限制。不過他們的總寬度可以超過這個8KB的限制。
如果單列的數據長度超過這個限制,那么就不能夠使用普通的數據類型。如對于那些用來保存圖片或者多媒體的數據,必須要使用大對象數據類型。因為只有這些大對象數據類型不受這個長度的限制。數據庫對對于這些大型數據庫類型對象有特殊的處理方法。
四、數據庫設計時的注意事項。
在數據庫運行時,如果存在比較多的行溢出現象,會在很大程度上影響數據庫的性能。所以在數據庫設計時,需要考慮到這種情況。一般的數據類型不會造成行溢出的情況。只有一些varchar nvarchar或者CLR用戶自定義類型的列,比較容易造成這個行溢出現象。
所以在設計數據庫時,數據庫管理員應該根據用戶提供的樣板數據分析可能發生行溢出現象的百分比,以及評估會發生溢出現象的頻率。如果溢出現象發生的百分比或者頻率比較高的話,那么數據庫管理員就需要考慮對表格進行規范化處理,以提高數據庫的性能,減少溢出現象對于數據庫的不利影響。
一般來說,有兩種方法可以顯著的降低這個行溢出現象對數據庫性能的影響。一是假設列定義了varchar或者用戶自定義數據類型等數據類型的時候,如果其長度比較長,很有可能引起行溢出現象的話,那么就干脆使用大對象數據類型。對于大對象數據類型SQL Server數據庫會采取特殊的管理方法,會講這個數據與普通數據分開來管理。所以可以在很大程度上降低行溢出現象對數據庫性能的影響。
不過需要注意的是,管理這些大對象數據類型,數據庫本身就需要花費更多的精力與資源。所以采用這種方式帶來的收益,與行溢出現象帶來的損失就會有一個輕重之分的問題。數據庫管理員要評估由此帶來的收益能夠彌補行溢出對象帶來的損失。如果可以彌補的話,那么可以采用這個方案。如果不可以的話,那就得不償失了。故筆者并不是很推薦使用這種方法。筆者現在采用的是下面要介紹的這種方式。
第二種方法執行起來比較簡單,具有比較強的可執行性。即如果某個表格中有varchar或則用戶自定義的數據類型,而且其最大長度也比較長,很容易造成行溢出現象。此時最好將這些列與表中的其他列分開來存放。即將他們放在兩張不同的表中。然后再通過join語句來進行連接。由于數據頁對單個列的最大長度有限制,所以如此處理的話,就不怎么會發生行溢出的現象。此時如果需要查詢完整的記錄,也需要訪問多個頁。
但是在實際工作中,往往不需要訪問全部的信息。如在更新或者統計操作時,不需要更新varchar數據類型的字段,那么數據庫的效率就會有很大的提升。即使需要訪問完整的記錄,需要訪問多個頁。但是采取join操作也要比行溢出操作性能來的好。如在更新數據時將varchar的列縮短了,此時由于在兩個不同的表中,也不會出現合并行的問題。所以可以在很大程度上節省數據庫的開銷。顯然,這種分表處理的方式更加簡單,很容易操作。所以筆者強烈建議采用這種方式來避免行溢出對SQL Server數據庫造成的不利影響。
【編輯推薦】