今天這篇文章我想談下堆表上特有的性能問題:轉發記錄(Forwarding Records)。首先我們要澄清下什么是堆表:堆表就是沒有聚集索引定義的表。它對插入新記錄非???,但當你讀取數據時非常慢。讀取數據會在你的存儲子系統上引入隨機存?。╮andom I/O),有時候當你碰到轉發記錄,它會進一步降低你的讀取性能。
為什么會有轉發記錄?當堆表里的記錄需要移動到不同的物理位置時,SQL Server使用轉發記錄。假設你有一個變長列的表,首先你在堆表里插入一些記錄,這個時候你沒有在變長列里存儲任何數據:
1 -- Create a table to demonstrate forwarding records 2 CREATE TABLE HeapTable 3 ( 4 Col1 INT IDENTITY(1, 1), 5 Col2 CHAR(2000), 6 Col3 VARCHAR(1000) 7 ) 8 GO 9 10 -- Insert 4 records - those will fit into one page11 INSERT INTO HeapTable VALUES12 (13 REPLICATE('1', 2000),14 ''15 ),16 (17 REPLICATE('2', 2000),18 ''19 ),20 (21 REPLICATE('3', 2000),22 ''23 ),24 (25 REPLICATE('4', 2000),26 ''27 )28 GO
當你在變長列執行UPDATE語句時,想象下會發生什么?在那個情況下SQL Server可能需要擴展這個記錄,因為記錄大小更長了,其他記錄必須從同個數據頁移走。
1 -- Let's update the table and expand each row of the table2 UPDATE HeapTable3 SET Col3 = REPLICATE('5', 1000)4 GO
在那個情況下,SQL Server在原始位置留下稱為轉發記錄,它指向記錄最終存儲的新位置。
SQL Server需要使用這個方法來避免更新同個表上所有非聚集索引。你可能知道,當你在堆表上創建非聚集索引時,在葉子層,非聚集索引指向記錄數據存儲的物理位置。沒有抓發記錄的話,所有這些指針都要改變,這會大幅度降低你的性能。
如何修正轉發記錄?為了找出表里是否包含轉發記錄,你可以使用DMFsys.dm_db_index_physical_stats。當你在堆表上調用這個函數并傳入DETAILED模式,SQL Server通過forwarded_record_count列告訴你表上的轉發記錄數。
1 -- Check the forwarding record count through sys.dm_db_index_physical_stats 2 SELECT 3 index_type_desc, 4 page_count, 5 avg_page_space_used_in_percent, 6 avg_record_size_in_bytes, 7 forwarded_record_count 8 FROM sys.dm_db_index_physical_stats 9 (10 DB_ID('ALLOCATIONDB'), 11 OBJECT_ID('HeapTable'),12 NULL, 13 NULL, 14 'DETAILED'15 )16 GO
可以看到,表上有2條轉發記錄,在4條記錄之外。為了去掉這些轉發記錄,你可以重建表。
1 -- Rebuild the heap table to get rid of the Forwarding Records2 ALTER TABLE HeapTable REBUILD3 GO
再次運行剛才的查詢,你會發現轉發記錄已經消失。
1 -- Check the forwarding record count through sys.dm_db_index_physical_stats 2 SELECT 3 index_type_desc, 4 page_count, 5 avg_page_space_used_in_percent, 6 avg_record_size_in_bytes, 7 forwarded_record_count 8 FROM sys.dm_db_index_physical_stats 9 (10 DB_ID('ALLOCATIONDB'), 11 OBJECT_ID('HeapTable'),12 NULL, 13 NULL, 14 'DETAILED'15 )16 GO
DBA總會考慮到索引碎片,索引重建和索引重新組織操作。但沒有人想過堆表里的轉發記錄。如果你維護數據庫,經常檢查下堆表上的轉發記錄數,保證始終有最好的性能,這是個非常好的想法。
小結在這個文章里你看到了堆表上轉發記錄是咋樣,它是如何降低的記錄讀取,因為需要額外的邏輯讀。當我為數據庫進行健康檢查時,我在檢查堆表時,總會看下轉發記錄數。
相信我:數據庫里會有巨大數量的堆表,在生產系統里也會有很多轉發記錄,但DBA們并沒意識到這個副作用。作為第一經驗,我經常推薦在表上建立聚集索引來避免轉發記錄。當然在一些特定場景里,例如最后頁插入加鎖競爭(Last Page Insert Latch Contention),在這里你可以使用堆表來避免這個問題,但大多數情況下,在表上建立聚集索引還是非常有用的。
新聞熱點
疑難解答