微软工程师的一个工程师曾经对性能调优有一个很是形象的比喻:剥洋葱 。我也很是承认,让咱们来一层一层拨开外面它神秘的面纱。sql
下面祭出的是咱们在给客户分析数据库性能问题最经常使用的图。数据库
看完这个图,你是否是对性能调优有了个基本的概念了.一般来说咱们会依照下面的顺序来进行分析:缓存
硬件能力安全
系统规模性能优化
数据库内部因素架构
软件环境并发
这4个的顺序能够有所调整或者交换,可是对于系统的性能优化必定要从全局出发。切勿一来就深刻到某一个SQL语句的优化,由于可能你花费大量的app
时间吧一个SQL从20s 优化到1s,可是整个系统的卡慢仍然存在。负载均衡
最后才是性能
业务模型及架构
代码设计
不废话了,开整开整,直接上干货。
时间:2018年1月某天
事件:某医院客户 下午4点 忽然出现大面积的卡慢。整个系统出现严重问题,信息中心电话打爆,医院工程师手足无措。
万幸的是咱们给数据库装了‘摄像头’,下面就从监控录像来看看发送了什么。而后加以解决
在问题发生时间段内CPU使用率在20%如下,正常。
从下面的图像显示,内存使用正常。
页生命周期
可用内存
IO队列平均值很低,15.48 左右有个瞬时的高点,可留意这段时间有没有批量的写入。
总的来看,硬件资源是足够的。
问题发生时,每秒的批请求书并非一个上升趋势,反而有所降低。这是由于系统的拥堵,等待 ,影响了系统的吞吐量。
从会话和慢语句的趋势图能够看到,问题发生的时间和客户描述彻底吻合,咱们能够判定自己事故的确是慢在数据库。
检查者个时间段运行中的语句,能够发现下午15.58左右,数据库中开始出现愈来愈多的CMEMTHREAD等待。
一直到1900页16.08分的时候,出现了最高达100个并发同时出现CMEMTHREAD等待
微软官方的描述:在任务正在等待线程安全的内存对象时发生。 当多个任务尝试从同一个内存对象分配内存致使争用时,等待时间可能会增长。
这个描述很晦涩,感受仍是彻底不知道等待类型是怎么回事,应该怎么处理这类问题。
实际上,从官方描述来看是内存争用的问题,可是实际上这个问题的关键在于多个任务的争用,其实是并发的执行的问题。
内存对象有三种类型的(Global,Per Numa Node,Per CPU)。 SQL Server将容许对内存对象进行分段,以便只有同一节点或cpu上的线程具备相同的底层CMemObj,从而减小来自其余节点或cpu的线程交互,从而提升性能和可伸缩性。减小内存的并发争用
SELECT type, pages_in_bytes, CASE WHEN (0x20 = creation_options & 0x20) THEN 'Global PMO. Cannot be partitioned by CPU/NUMA Node. TF 8048 not applicable.' WHEN (0x40 = creation_options & 0x40) THEN 'Partitioned by CPU.TF 8048 not applicable.' WHEN (0x80 = creation_options & 0x80) THEN 'Partitioned by Node. Use TF 8048 to further partition by CPU' ELSE 'UNKNOWN' END from sys.dm_os_memory_objects order by pages_in_bytes desc
若是你发现,Partitioned by Node 的内存开销是排在前面的,可使用TRACE FLAG 8048来减小CMEMTHREAD等待.
从图中能够看到,客户的 Partitioned by Node 是比较靠后的,排在14位。
3. 补丁
这类场景是最多见的。若是在系统中发现出现大量的CMEMTHREAD等待,优先考虑数据库是否是已经安装最新的补丁
2012 ,2014 当您执行许多特殊查询在 SQL Server 2012年或 SQL Server 2014 CMEMTHREAD 等待
目前数据库的版本是 11.0.5556.0 而前面提到的补丁,安装后的版本是:11.0.5623.0
都是相似下面的语句,最高时,并发超过100.
SELECT * INTO #Tmp from TB where 1=2
特色以下:
1.语句简单 开销都小于5不会产生并行
2.都采用了select into #temptable的形式
就像上面分析的同样,CMEMTHREAD等待是一个并发问题,而不是一个内存问题。在其余方案行不通的时候,咱们能够经过调整此类语句的写法,减小CMEMTHREAD等待.
目前系统是单机运行的状态,这实际上是不多见的。存在少许OLAP 和OLTP业务混合的状况。后续咱们会给客户规划 读写分离 或者负载均衡的解决方案。在
至少须要安装前面发的解决等待问题的FIX。建议是直接安装到目前为止最新的2012 SP4补丁。
optimize for ad hoc workloads 从0修改成1 。针对将即席执行计划ad hoc plans 插入到计划缓存中的时候 场景,减小ad hoc 查询占用的内存。
select * into #temptable 会产生大量的闩锁争用,防止在CMEMTHREAD 等待消除后,出现大量的pagelatch 闩锁争用。我经历过不少案例,解决了前面的一个拥堵以后,
后面有产生了新的等待,致使性能更差了。请记住,优化是一个长期的,按部就班的过程。
目前部分tempdb文件放在S,通常分放在D盘。建议都迁移到S盘(存储上面),增长tempdb的响应速度。若是可能的话,使用SSD来最大化tempdb的性能,将会是不错的选择。
修改代码一般都是放在最后面的,由于要牵涉的状况比较多。前面的手段80%的状况下,均可以解决问题。剩下的20%,咱们须要,检查程序中的逻辑,看看这些的语句都是什么业务产生的。什么条件会触发这类业务.对应下面相似的语句都使用存储过程,或者参数化后的方式,减小编译和重编译的次数。另外此类语句都会并发建立临时表,可能经过调整tempdb的设置,加快此类语句的执行速度,减小同一时间此类语句的并发数量。
通过前面的几个优化手段,次日开始,没有再出现过一次CMEMTHREAD的等待。
经过这篇文件你应该已经彻底学会了数据库性能调优的思想。他告诉了咱们出现问题时,怎么动手一步一步的排查问题,就像剥洋葱同样一层一层的剥开。
微软官方博客对这类等待的原理和如何调试How It Works: CMemThread and Debugging Them
SQL Server 2016 对这里问题进行了进一步的优化,详细参考 SQL 2016 – It Just Runs Faster: Dynamic Memory Object (CMemThread) Partitioning