一直以來,因爲SQL Server中沒有位圖索引使得面對一些場景,從業人員在索引選擇上受限,飽受詬病.其實熟悉SQL Server的朋友應該知道,SQL Server雖然沒有位圖索引,但在特定環境下仍是會採用位圖(Bitmap)過濾的,此次就爲你們介紹下SQL Server的位圖過濾.html
概念:關於位圖索引的概念我就不作過多介紹了,感興趣的朋友能夠看下wikipedia算法
http://en.wikipedia.org/wiki/Bitmap_index數據庫
優點:在重複率高,數據不多被更新的場景中(如一年以內的年齡,汽車車型等)過濾高效.數組
SQL Server的位圖過濾採用的布隆過濾(bloom filter)方式,這裏我簡單說下布隆過濾的實現方式.架構
實現方式:經過構建一個長度X的位數組(bit array)(全部位爲0),將要匹配的集合經過哈希函數映射到位數組中的相應點中(相應位爲1),當判斷一個值是否存在時找bit array中對應位是否爲1就能夠了.這個過程由SQL Server內部本身完成.併發
如圖1-1所示,我將須要匹配的集合{神仙?,妖怪?,謝謝!}映射到bit array中,當有一條新記錄{悟空..}我判斷他是否在個人集合中,只需判斷相應的位是不是1就能夠了,圖中能夠看出{悟空..}並非全部位都爲1,因此悟空並不在個人集合中.oracle
圖1-1運維
具體到SQL Server中是如何實現的呢?咱們仍是經過一個實例來看.ide
測試環境腳本函數
USE AdventureWorks GO SELECT p.ProductID + (a.number * 1000) AS ProductID, p.Name + CONVERT(VARCHAR, (a.number * 1000)) AS Name, p.ProductNumber + '-' + CONVERT(VARCHAR, (a.number * 1000)) AS ProductNumber, p.MakeFlag, p.FinishedGoodsFlag, p.Color, p.SafetyStockLevel, p.ReorderPoint, p.StandardCost, p.ListPrice, p.Size, p.SizeUnitMeasureCode, p.WeightUnitMeasureCode, p.Weight, p.DaysToManufacture, p.ProductLine, p.Class, p.Style, p.ProductSubcategoryID, p.ProductModelID, p.SellStartDate, p.SellEndDate, p.DiscontinuedDate INTO T1 FROM Production.Product AS p CROSS JOIN master..spt_values AS a WHERE a.type = 'p' AND a.number BETWEEN 1 AND 50 GO SELECT ROW_NUMBER() OVER ( ORDER BY x.TransactionDate, (SELECT NEWID()) ) AS TransactionID, p1.ProductID, x.TransactionDate, x.Quantity, CONVERT(MONEY, p1.ListPrice * x.Quantity * RAND(CHECKSUM(NEWID())) * 2) AS ActualCost INTO T2 FROM ( SELECT p.ProductID, p.ListPrice, CASE WHEN p.productid % 26 = 0 THEN 26 WHEN p.productid % 25 = 0 THEN 25 WHEN p.productid % 24 = 0 THEN 24 WHEN p.productid % 23 = 0 THEN 23 WHEN p.productid % 22 = 0 THEN 22 WHEN p.productid % 21 = 0 THEN 21 WHEN p.productid % 20 = 0 THEN 20 WHEN p.productid % 19 = 0 THEN 19 WHEN p.productid % 18 = 0 THEN 18 WHEN p.productid % 17 = 0 THEN 17 WHEN p.productid % 16 = 0 THEN 16 WHEN p.productid % 15 = 0 THEN 15 WHEN p.productid % 14 = 0 THEN 14 WHEN p.productid % 13 = 0 THEN 13 WHEN p.productid % 12 = 0 THEN 12 WHEN p.productid % 11 = 0 THEN 11 WHEN p.productid % 10 = 0 THEN 10 WHEN p.productid % 9 = 0 THEN 9 WHEN p.productid % 8 = 0 THEN 8 WHEN p.productid % 7 = 0 THEN 7 WHEN p.productid % 6 = 0 THEN 6 WHEN p.productid % 5 = 0 THEN 5 WHEN p.productid % 4 = 0 THEN 4 WHEN p.productid % 3 = 0 THEN 3 WHEN p.productid % 2 = 0 THEN 2 ELSE 1 END AS ProductGroup FROM bigproduct p ) AS p1 CROSS APPLY ( SELECT transactionDate, CONVERT(INT, (RAND(CHECKSUM(NEWID())) * 100) + 1) AS Quantity FROM ( SELECT DATEADD(dd, number, '20050101') AS transactionDate, NTILE(p1.ProductGroup) OVER ( ORDER BY number ) AS groupRange FROM master..spt_values WHERE type = 'p' ) AS z WHERE z.groupRange % 2 = 1 ) AS x
實例Code
select * from t1 inner join t2 on t1.productid=t2.ProductID where t1.ProductID<1510
執行計劃如圖1-2所示,再掃描t2表時實際上經過t1表的匹配結果集生成bit array(bitmap1008)進行過濾,從而使得20多萬的數據能夠高效過濾,進而提高語句的總體效率.
圖1-2
也許有人會說,既然Bitmap過濾如此強悍爲何這個運算符在平常執行計劃中並不常見呢?的確SQL Server在Bitmap過濾上有限制.只有在並行hash join,merge join的情形中才會使用這個技術(實際串行計劃hash join中也有可能採用,但不顯示).
其實位圖過濾(位圖索引)的應用場景我感受仍是很多的,因爲 SQL Server沒有位圖索引,針對優化器自身使用的Bitmap 過濾又有種種限制,這個限制了這個優秀算法的使用空間,爲此我還專門給微軟SQL Server團隊提了建議,建議放寬/可控bitmap過濾的使用.
注:關於位圖索引的使用,你們能夠參考oracle中的技術文檔
http://www.oracle.com/technetwork/articles/sharma-indexes-093638.html
關於布隆過濾器的使用能夠參考wikipedia
http://en.wikipedia.org/wiki/Bloom_filter
也許你們有疑問既然SQL Server中Bitmap這麼不容易出現,那對咱們調優還有什麼幫助呢?
這個給你們講個咱們實際生產過程當中的應用.在我寫的 SQL Server優化技巧之SQL Server中的"MapReduce"博客中,很多朋友對我調整的那個系統參數興趣極大:),這裏大概講下相關的調整過程.
背景:雙11活動中,公司網站訪問量明顯增長,發現某臺數據庫實例資源消耗上升明顯.經過DMV捕獲其中消耗資源的語句發現資源大多被個別高併發的語句消耗.
語句執行計劃截圖圖2-1
圖2-1
能夠看出絕大多數消耗被Sort佔據.
因爲Sort是典型的計算密集型操做,消耗CPU的同時消耗大量內存.
在沒有溢出到tempdb的sort採用的算法是快速排序,內存消耗將至少是排序結果集的200%以上,本例中單條查詢的內存消耗在600MB以上,高併發,加上語句執行週期長(2s以上)使得單條語句長期佔用內存,影響Buffer Pool的穩定,進而影響吞吐.同時帶來很差的用戶體驗.
經過對語句實際分析,發現若是採用並行執行,優化器是能夠利用Bitmap過濾,進而改善總體查詢.
語句執行計劃截圖圖2-2
圖2-2
能夠看出在並行執行計劃中因爲採用了Bitmap過濾,使得並行響應時間縮短爲不到0.3s,同時CPU時間縮短爲1s而且內存的消耗由600MB+減小至不到300M,這樣減小資源使用的同時也提高了用戶體驗,而且因爲響應時間不到0.3s使得查詢內存的佔用時間明顯縮短,保證了Buffer Pool的穩定,進而確保吞吐基本穩定.
調整方案的抉擇
實際上要優化器針對某些查詢使用並行執行計劃,咱們是有幾種方案供選擇的
Plan Guide, Trace Flag , cost threshold for parallelism
因爲當時的語句是個複雜的拼串語句,在query cache中發現針對相關語句存在很多不一樣的query_hash,此時若是使得Plan Guide調整複雜,不肯定因素多,所以未採用.
針對特定的語句採用Trace flag(8649)對特定語句調整實際上是最具針對性的,可是考慮到代碼中其實是須要研發同事參與的,在特定的時間窗口(雙11)能不給別人找事兒就是運維人員最主要的出發點(同時也是運維人員價值的側面直觀體現).
所以決定採用並行閾值,使系統自動出發並行,並調整合適的並行度.調整並行閾值時我當時並未採用通常的二分法進行定位調整,考慮到並行閾值調整是實例級調整,會清空plan cache,影響很大,多一次調整就多一次性能抖動(甚至多一次意外).這時在一個時間段內我對實例的高消耗,出鏡率高的查詢進行採樣,分別統計他們的subtree cost,進而大概肯定了最小影響的閾值區間,並進行調整.因爲本人人品不錯:),一次調整就OK了.
以後CPU降低明顯,訪問量繼續升高.
結語:不管是平常,仍是特殊時段的運維,都須要咱們確保頭腦冷靜的同時依靠本身掌握的知識選擇最合理的解決方案.
/*******************************************************************/
再次奉上我兒子小藍天的靚照.
小寶貝出生了,壓力增長,動力更強了,哪些朋友若是有SQL Server相關的培訓或是優化,架構等方面的需求能夠聯繫我.爲了小藍天,爲了家要更拼些.