數據庫中的(Bookmark Lookup)

Bookmark Lookup是什麼呢?在SQL Server2000中的聯機叢書中是這樣說的:“Bookmark Lookup邏輯運算符和物理運算符使用書籤(行 ID 或聚集鍵)在表或聚集索引內查找相應的行。Argument 列包含書籤標誌,用於在表或聚集索引內查找行。Argument 列還包含要查找的行所在的表或聚集索引的名稱。如果 WITH PREFETCH 子句出現在 Argument 列內,則表示查詢處理器已決定在表或聚集索引內查找書籤時最好使用異步預提取(預讀)。”看了這樣的解釋,還是不明不白。後來通過查找資料,終於明白了Bookmark Lookup是什麼了,什麼時候會發生Bookmark Lookup,他對查詢的性能有什麼樣的影響,並如何避免Bookup Lookup。現與大家共享。如果有什麼錯誤,也請不吝賜教。如無特殊說明,本文中的Sql Server均指Sql Server 2000。   要弄清楚Bookmark Lookup,需從Sql Server索引和表的存儲架構入手。Sql Server的表使用如下兩種方法組織其數據頁:   聚集表:聚集表就是具有聚集索引的表,它基於聚集索引鍵按順序存儲數據行,索引按B樹索引結構實現。B樹基於聚集索引鍵值對行進行快速檢索。每級索引的頁鏈接在雙向鏈表中,但使用鍵值在各級間進行導航。數據行本身構成聚集索引的最低級別。   堆集:堆集是沒有聚集索引的表,不按任何特殊順序存儲數據行。數據頁不在鏈表內鏈接。非聚集索引有一個與聚集索引中相似的B樹索引結構,但是他對數據行的順序不起作用,其最低行包含非聚集索引的鍵值,每個鍵值項都有指針指向包含該鍵值的數據行。對於堆集,該指針是指向行的指針,對於聚集表,則是聚集索引鍵。該指針叫做行定位器。   SQL Server 的數據文件中有一類是IAM,即索引分配映射表,它存儲有關表和索引所使用的擴展盤區信息。一個堆集在sysindexes內有一行,其indid=0。FirstIAM列指向指向表的數據頁集合的IAM鏈。服務器使用IAM頁查找數據頁集合內的頁。通過掃描IAM頁,可以對堆集進行表掃描或串行讀,以找到這個堆集的頁的擴展盤區。所以,對於沒有任何索引的堆集,不管做什麼樣的查詢,服務器都必須對對錶進行一次掃描。哪怕只返回一行,其IO數都是一樣的,即表的行數。   某個表和視圖的聚集索引在sysindexes內有一行,其indid=1。root列指向聚集索引B樹的頂端。服務器使用B樹查找數據頁。SQL Server沿着聚集索引瀏覽以找到聚集索引鍵對應的行。爲找到鍵的範圍,SQL Server瀏覽索引以找到這個範圍的起始值,然後用向前或向後頁掃描數據頁。爲找到數據頁鏈的頁首,SQL Server從索引的根節點開始沿着最左邊的指針進行掃描。所以,如果用聚集索引查找數據,如果只返回一行,那麼其IO數,就是B樹的頂端到鍵值所在數據行的深度,簡記爲D。如果返回多行,則需要再加上符合條件的頁數,簡記爲P。總的IO數爲D+P。   某個表或視圖的非聚集索引在索引在sysindexes內也有一行,其indid值從2到250,root列指向非聚集索引B樹的頂端。SQL Server在查找數據時,服務器先使用和使用聚集索引相同的查找方法找到該索引的行定位器——Bookmark,然後通過行定位器來找到所需要的數據,這種通過行定位器查找數據的方式就是Bookmark Lookup。如果索引所在的表是堆集,那麼Sql Server使用行指針來找到數據。所以,這種情況下,返回1行的IO數是找到行定位器爲止的B樹的深度D+1。而如果返回多行,則IO數爲D+所有媽祖條件的索引頁的頁數P+返回行數H。如果索引所在的表是聚集,那麼Sql Server使用聚集索引的鍵來找到數據。所以,這種情況下,返回1行的IO數是找到行定位其爲止的B樹的深度D+找到聚集索引的鍵的B樹的深度D1。返回多行的IO數則爲D+P+H*D1。   在基於非聚集索引查找數據時,還有另外一種情形,那就是如果放回的數據列就包含於索引的鍵值中,或者包含於索引的鍵值+聚集索引的鍵值中,那麼就不會發生Bookup Lookup,因爲找到索引項,就已經找到所需的數據了,沒有必要再到數據行去找了。這種情況,叫做索引覆蓋。   好了,現在我們以實例說明。   有一個這樣的表:   Employees (EmployeeID,EmployeeName,Sex,Birthday,PhotoFile,   EnterDate, ProvinceID, CityID, Address, PostCode, IDCardNo) 。   其中EmployeeID爲主鍵,並且按他建立了一個聚集索引PK_EmployeeID,在EmployeeName,Birthday,EnterDate,PostCode,IDCardNo上分別建立了非聚集索引IX_EmployeeName,IX_Birthday,IX_EnterDate,IX_PostCode,IX_IDCardNo。   如果我們用這樣的一個語句進行查詢:   Select * from Employees where EmployeeID=’C054965’   Select EmployeeID from Employees where EmployeeName=’劉永紅’   則不會發生Bookmark Lookup,而如果用下面的語句,則會發生Bookupmark Lookup:   Select Sex from Employees where EmployeeName=’劉永紅’   對照上面的語句,我們再回過頭來看看照聯機叢書中的解釋。   “Bookmark Lookup邏輯運算符和物理運算符使用書籤(行 ID 或聚集鍵)在表或聚集索引內查找相應的行。”   對於語句 select Sex from Employees where EmployeeName=’劉永紅’,服務器先在非聚集索引IX_EmployeeName上找到與“劉永紅”對應的行定位器——“C054965”,然後根據這個值在聚集索引PK_EmployeeID上找到與“C054965”對應的數據行,並返回Sex——“男”這個值。而我們用select EmployeeID from Employees where EmployeeName=’劉永紅’時,因爲EmployeeID包含於聚集索引PK_EmployeeID的鍵值中,所以,不用再進行Bookmark Lookup,而可以直接返回了。   但是對於select Sex from Employees where EmployeeName=’劉永紅’ 就不同了,因爲Sex並沒有包含在PK_EmployeeID的鍵值中,也沒有包含在EmployeeName的鍵值中,所以必須根據行定位器——“C054965”來進一步查找。   如果我們去掉聚集索引PK_EmployeeID,那麼,服務器在執行Select Sex from Employees where EmployeeName=’劉永紅’的時候,先在非聚集索引IX_EmployeeName上找到與“劉永紅”對應的行定位器——指向EmployeeName=‘劉永紅’的對應的數據行的指針,然後返回該行的Sex——“男”。   當然,如果我們執行select * from Employees where Sex=’男’,那麼也不會發生Bookmark Lookup,而是直接的表掃描(Table Scan)了,不管表Employees有沒有建立聚集索引。   從這裏,我們可以得出一些有趣的結論:   在一個聚集表上使用非聚集索引進行查詢,其性能低於在堆集上使用非聚集索引進行查詢。   查詢性能比較:   返回行數較多:索引覆蓋>聚集索引>表掃描>堆集的非聚集索引>聚集的非聚集索引   返回行數較少:索引覆蓋=聚集索引>堆集的非聚集索引>聚集的非聚集索引>表掃描   所以,瞭解表的存儲結構對於我們編寫高效率的查詢和建立高效率的索引有非常重要的意義。

 

 --排序(聚集索引) create clustered index inx_entry_stock_bi on entry_stock_d(entry_stock_bi)

--主鍵 alter table entry_stock_d add primary key nonclustered--主鍵且非聚集 (  entry_stock_bi,aid )

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章