[SQL]我不是胖,只是肌肉比較厚而已…
自從上次放陷阱的事件之後,似乎這種需要扮演當柯南的案件就很莫名其妙的的找上我了,而這次是某客戶的資料庫效能慢的出奇,但明明客戶的設備是非常高檔,但執行起來卻還是跟這些設備的身價不成比例,於是就開始另外一個效能調教的案件了….
首先我們先透過活動監視器 ( 可以參考 MSDN : http://msdn.microsoft.com/zh-tw/library/ms175518.aspx ),找到比較耗時的幾個 SQL 指令,再將這些指令放到 SSMS 上面,透過顯示實際執行計畫 ( 可以參考 MSDN : http://msdn.microsoft.com/zh-tw/library/ms189562.aspx )。在經過一翻比對測試之後,發現到這些比較慢的 SQL 查詢命令,都沒有我們以往常發生的 TABLE SCAN 或者是 INDEX SCAN 的情況,資料表上面也都有配合建立 INDEX,因此大部分的也都是採用 INDEX SEEK 的方式來找資料。但是這當中這些指令再關聯到其中一個 TABLE 的時候,速度就明顯往下降,因此我們決定從該 TABLE 來下手。
當看到上面這樣的數據的時候,不知道大家是否會跟我一樣狐疑,這個資料表會不會太胖了一點啊,平均一筆資料將近要 0.7MB ( 700KB ) ?! 正常狀況下就算一筆資料使用到一個 Page,那也只應該需要 8K 啊 ? ( 此部分可以參考 MSDN : http://msdn.microsoft.com/zh-tw/library/ms186981(v=sql.105).aspx ),於是這個時候我們就來查看一下相關的 欄位大小,我們會利用到 INFORMATION_SCHEMA.COLUMNS 這個 VIEW 來查看,指令如下:
SELECT
TABLE_NAME,DATA_TYPE,COUNT(*) COLUMN_COUNT, SUM(CHARACTER_OCTET_LENGTH) COLUMN_SIZE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'd_customer'
GROUP BY TABLE_NAME,DATA_TYPE
查看之後發覺主要的欄位都是 nvarchar 型態居多,總 Size 大約 8K;另外也透過另外一篇( http://www.dotblogs.com.tw/jamesfu/archive/2012/08/01/tableszie.aspx ) 所介紹的指令來查看 SIZE,狀況看起來應該比較像是資料佔用太多的 Page,此時我們也另外做一個實驗,利用統計資訊來驗證一下我們的假設是否正確:
SET STATISTICS IO ON
SET STATISTICS TIME ON
select * from d_customer
SET STATISTICS TIME OFF
SET STATISTICS IO ON
輸出結果如下:
從上述的跡象看起來,看來我們的假設是正確的,的確是資料散落在太多的 Page ,因此問題看起來應該就只是單純資料存放的問題。所以我們很簡單的利用一個方式來驗證我們的想法,我們利用 SELECT INTO 的方式,將原本的 d_customer 的 Table 給 SELECT INTO 到另外一個 Table,這時候會發現新產生的 Table 只有 3MB,跟原本幾乎要 2GB 的大小實在差異太多了。
這下知道問題的狀況,但是為什麼會這樣呢 ? 還有我們要如何解決呢 ? 就小弟個人的經驗,這個狀況從 SQL 2000 開始就會有的狀況,當您在設計的資料表如果沒有 CLUSTER INDEX ,而且機器效能非常好的狀況下。當我們要大量匯入資料到這個 TABLE 的時候,由於沒有 CLUSTER INDEX 限制資料存放順序下,SQL Server 就會儘可能一次先產生很多 PAGE,讓資料可以快速地平行放入,造成 TABLE 的資料會散落在很多的地方,加上如果這個 TABLE 又異動非常頻繁的情況下,就很容易造成虛胖的狀況,因此要避免這樣的情況下,只要讓 TABLE 有一個 Index 是 Cluster Index 就可以了。而微軟似乎也有發現到這樣的狀況,在 SQL Server 2008 之後,當你建立 Primary Key 的時候,如果沒有其他 Cluster Index 的狀況下,預設 PK 就是 Cluster Index,因此就比較不會再發生這樣的情況了。
除了上述介紹的方式之外,如果您的 SQL Server 是 Enterprise 的版本,有支援 Data Compression 功能的話 ( 可以參考 MSDN http://msdn.microsoft.com/zh-tw/library/cc280449.aspx ),也可以利用壓縮的方式,讓資料存放的時候不要那麼鬆散,減少資料讀取的 I/O,也會有不少的效能提升。