一直以来,因为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相关的培训或是优化,架构等方面的需求能够联系我.为了小蓝天,为了家要更拼些.