2012年2月6日 星期一

效能調整 - 填滿因數對資料庫的影響

索引是資料庫中非常重要的一環,它可以有效的加快查詢時的速度,但是相對的,可能也會造成新增資料時的負擔,在SQL Server中有許多相關的參數中其實可以進行調整,並針對各個不同的公司的使用情況來加以調整,藉以讓索引發揮更大的效能。

而 Fill Factor 是在索引中非常重要的一個參數設定,詳細的說明整理如下 (來自 MSDN),簡單的說如果你的系統如果為 OLAP 類型時,那你就可以將設定值設定為 0 或 100 (0 為預設值,且 0 與 100 的設定皆相同),因為執行的語法類型全部都是查詢,但如果是 OLTP 則可以自行觀察並調整設定值,底下我分成幾個部份進行實驗,藉以讓大家更了解 Fill Factor 參數調整與 IO的影響。

Fill Factor
使用 fill factor 選項,主要指定 Microsoft SQL Server 在使用現有的資料建立新索引時,應該在每一頁填滿多少空間。填滿因數會影響效能,因為當頁面填滿之後,SQL Server 就必須花費時間進行頁面的分割。

PS:
A、填滿因數的預設值為 0;有效值介於 0 到 100 之間。當 FILL FACTOR 設定為 0 或 100 時,分葉層級會完全填滿。
B、填滿因數只會用於建立或重建索引時。所有分頁都不會保持在特定的填滿程度。

重建索引:
ALTER INDEX IX_SalesOrderDetail ON Sales.SalesOrderDetail
REBUILD WITH (FILLFACTOR = 50, SORT_IN_TEMPDB = ON,
              STATISTICS_NORECOMPUTE = ON);


頁面分割
選擇正確的填滿因數值,可以減少可能的分頁分割,因為當基礎資料表中加入資料時,將有足夠的空間來進行索引擴充。

將新的資料列加入全文檢索頁時,Database Engine 會將幾乎一半的資料列移到新的分頁,以留出空間給新的資料列。這個重組動作稱為分頁分割。分頁分割可留出空間給新的記錄,但是執行時需要時間,而且是一項耗用大量資源的作業。此外,它也可能會造成資料片段過多,因而增加 I/O 作業。如果分頁分割次數過於頻繁,可以使用新的或現有的填滿因數值重建索引,藉以重新分配資料。

雖然 0 以外的較低填滿函數值能夠減少索引成長時進行分頁分割的需求,但是索引將需要更多的儲存空間,而且可能會降低讀取效能。即使對於會大量執行插入和更新作業的應用程式而言,讀取資料庫的次數通常比寫入資料庫的次數還要高,因數為 5 比 10。因此,指定預設值以外的填滿因數,將可能以與填滿因數設定呈反比的數量,降低資料庫的讀取效能。例如,一個 50 的填滿因數值,可能會使資料庫的讀取效能降低兩倍。讀取效能降低是因為索引包含更多的分頁,造成擷取資料所需的磁碟 IO 作業增加所致。


實驗測試:
1、使用空間上的比較:
sp_spaceused 'Sales.SalesOrderDetail';

fill factor -> 50%

fill factor -> 100%

在 fill factor 設定為50%的時候,當有新的資料加入時,為了保留額外空間給日後 Index 異動時快速加入,所以在 Index 的部份,你會看到系統會佔用了較大的空間。


2、資料讀取上的比較
查詢語法:
SELECT TOP 1000 [SalesOrderID]
      ,[SalesOrderDetailID]
      ,[CarrierTrackingNumber]
      ,[OrderQty]
      ,[ProductID]
      ,[SpecialOfferID]
      ,[UnitPrice]
      ,[UnitPriceDiscount]
      ,[LineTotal]
      ,[rowguid]
      ,[ModifiedDate]
  FROM [AdventureWorks2008R2].[Sales].[SalesOrderDetail]
  where ProductID = 776 and OrderQty > 2

I/O分析:
fill factor -> 50%
89 row(s) affected)
Table 'SalesOrderDetail'. Scan count 1, logical reads 285, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

fill factor -> 100%
(89 row(s) affected)
Table 'SalesOrderDetail'. Scan count 1, logical reads 283, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

從上述的結果來看,由於 Fill Factory 設為50%時,為了預留較多的空間,所以索引會較為分散,當然在查詢時,logical reads 的次數就會比較多,相對的也會比較慢,根據官方的說法,實際上可能會有2倍的落差,所以請小心評估後,再設定這個值。


3、資料新增上的比較:
新增語法:
INSERT INTO [AdventureWorks2008R2].[Sales].[SalesOrderDetail]
           ([SalesOrderID]
           ,[CarrierTrackingNumber]
           ,[OrderQty]
           ,[ProductID]
           ,[SpecialOfferID]
           ,[UnitPrice]
           ,[UnitPriceDiscount]
           )
     VALUES
           (43909
           ,'9467-4A37-A4'
           ,5
           ,776
           ,1
           ,100.114
           ,0)

I/O分析:
fill factor -> 50%
Table 'SpecialOfferProduct'. Scan count 0, logical reads 2, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SalesOrderHeader'. Scan count 0, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SalesOrderDetail'. Scan count 0, logical reads 17, physical reads 3, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

fill factor -> 100%
Table 'SpecialOfferProduct'. Scan count 0, logical reads 2, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SalesOrderHeader'. Scan count 0, logical reads 3, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SalesOrderDetail'. Scan count 0, logical reads 22, physical reads 6, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

佔用空間:
fill factor -> 50%
table name count Reserved data index_size unused
SalesOrderDetail  121317 20072 KB 9888 KB 9432 KB 752 KB
121318 20080 KB 9888 KB 9440 KB 752 KB

fill factor -> 100%
table name count Reserved data index_size unused
SalesOrderDetail  121317 18032 KB 9888 KB 7400 KB 744 KB
121318 18048 KB 9888 KB 7416 KB 744 KB

執行時間:
Time Statistics
Fill Factor 100% 50%
Client processing time 12 11
Total execution time 43 31
Wait time on server replies 31 20

在如同上述的 I/O 分析,你可以看出在 Insert 的狀況下, Fill Factory 50% 可以很快的加入表格中, logical reads 只需要17即可,比 Fill Factory 100% 的 22還要快,這是因為 50% 的情況下,頁面未填滿,所以可以很快的將 index 加入。


4、頁面分散程度分析:
select * from sys.dm_db_index_physical_stats
 (DB_ID(N'AdventureWorks2008R2'), OBJECT_ID(N'SalesOrderDetail'),
 NULL, NULL , 'DETAILED');

fill
factor
name avg
fragmentation
in_percent
fragment
count
avg_page
space_used
in_percent
50% IX_SalesOrderDetail 0.196850394 2 50.13369162
IX_SalesOrderDetail 0 1 72.15221151
IX_SalesOrderDetail 0 1 0.543612553
100% IX_SalesOrderDetail 0 2 99.89869039
IX_SalesOrderDetail 0 1 72.43637262

透過 sys.dm_db_index_physical_stats 可以查出索引的分散程度,你可以看出 avg_page_space_used_in_percent 就是大約你設定的索引分散程度,當然你可以透過索引的重組或重建來進行調整,關於詳細的細節,請參考 [索引分析與維護建議]。

5、索引頁面分散程度監視
當 fill factor 設定太低時,可能會造成索引頁面過於分散,可以透過效能監視器中的 SQLServer:Access Methods -> Page Splits/sec來進行觀察,此值建議要低於20以下,如果這個值太高的話,建議你可以降低 fill factor 的值,藉以改善問題。

Object: - SQLServer:Access Methods
Counter: - Page Splits/sec
Preferred Value: - < 20

總結:
從上述的幾個章節,你可以很快的了解到 fill factor 參數對整體效能的影響,當然你可以依舊使用原本的預設值,依然可以有很好的表現,但是由於每一個系統的讀寫頻率比重不一,所以建議你可以監視資料表的使用情況,再加以調整後會讓你的系統運作的更好。


相關文章:
維護記錄檔與索引的技巧
http://caryhsu.blogspot.com/2011/05/blog-post.html
SQL Server 上大型資料表的索引效能調整與維護
http://caryhsu.blogspot.com/2011/08/sql-server.html
效能調整 - 填滿因數對資料庫的影響
http://caryhsu.blogspot.com/2012/02/blog-post.html
索引分析與維護建議
http://caryhsu.blogspot.com/2012/02/blog-post_14.html

參考連結:
SQL Server 的 Access Methods 物件
http://technet.microsoft.com/zh-tw/library/ms177426.aspx
What is a page split? What happens? Why does it happen? Why worry?
 http://sqlblogcasts.com/blogs/tonyrogerson/archive/2007/06/28/what-is-a-page-split-what-happens-why-does-it-happen-why-worry.aspx
How to check Fragmentation on SQL Server 2005
http://blogs.msdn.com/b/jorgepc/archive/2007/12/09/how-to-check-fragmentation-on-sql-server-2005.aspx
sys.dm_db_index_physical_stats (Transact-SQL)
http://msdn.microsoft.com/zh-tw/library/ms188917.aspx
Understanding SQL Server Index Fill Factor Setting
http://www.mssqltips.com/sqlservertip/1940/understanding-sql-server-index-fill-factor-setting/
SET STATISTICS IO (Transact-SQL)
http://technet.microsoft.com/zh-tw/library/ms184361.aspx
填滿因數
http://technet.microsoft.com/zh-tw/library/ms177459(SQL.90).aspx
Performance Counters
http://sqlserverplanet.com/sql-optimization/performance-counters



關鍵字:SQL ServerFill FactorIndexIO Statistics填滿因數

2 則留言:

  1. Page Splits/sec->每秒由於索引頁面溢位造成的頁面分隔數
    這個值太高的話,是否應為調'低' fill factor 的值
    以避免分頁分割?

    回覆刪除
  2. 樓上的朋友,謝謝你的來信,的確如果Page Splits/sec過高的話,應該調低fill factor的值,這部份是我寫錯了,謝謝你的更正了。

    回覆刪除