如何判断MSSQL数据库磁盘出现了瓶颈

如题所述

      具体问题具体分析,举例来说明为什么磁盘IO成瓶颈数据库的性能急速下降了。

     为什么当磁盘IO成瓶颈之后, 数据库的性能不是达到饱和的平衡状态,而是急剧下降。为什么数据库的性能有非常明显的分界点,原因是什么?

      相信大部分做数据库运维的朋友,都遇到这种情况。 数据库在前一天性能表现的相当稳定,数据库的响应时间也很正常,但就在今天,在业务人员反馈业务流量没有任何上升的情况下,数据库的变得不稳定了,有时候一个最简单的insert操作, 需要几十秒,但99%的insert却又可以在几毫秒完成,这又是为什么了?

        dba此时心中有无限的疑惑,到底是什么原因呢? 磁盘IO性能变差了?还是业务运维人员反馈的流量压根就不对? 还是数据库内部出问题?昨天不是还好好的吗?

        当数据库出现响应时间不稳定的时候,我们在操作系统上会看到磁盘的利用率会比较高,如果观察仔细一点,还可以看到,存在一些读的IO. 数据库服务器如果存在大量的写IO,性能一般都是正常跟稳定的,但只要存在少量的读IO,则性能开始出现抖动,存在大量的读IO时(排除配备非常高速磁盘的机器),对于在线交易的数据库系统来说,大概性能就雪崩了。为什么操作系统上看到的磁盘读IO跟写IO所带来的性能差距这么大呢? 

        如果亲之前没有注意到上述的现象,亲对上述的结论也是怀疑。但请看下面的分解。

        在写这个文章之前,作者阅读了大量跟的IO相关的代码,如异步IO线程的相关的,innodb_buffer池相关的,以及跟读数据块最相关的核心函数buf_page_get_gen函数以及其调用的相关子函数。为了将文章写得通俗点,看起来不那么累,因此不再一行一行的将代码解析写出来。

     咱们先来提问题。 buf_page_get_gen函数的作用是从Buffer bool里面读数据页,可能存在以下几种情况。

      提问. 数据页不在buffer bool 里面该怎么办? 

     回答:去读文件,将文件中的数据页加载到buffer pool里面。下面是函数buffer_read_page的函数,作用是将物理数据页加载到buffer pool, 图片中显示

buffer_read_page函数栈的顶层是pread64(),调用了操作系统的读函数。

buf_read_page的代码

   如果去读文件,则需要等待物理读IO的完成,如果此时IO没有及时响应,则存在堵塞。这是一个同步读的操作,如果不完成该线程无法继续后续的步骤。因为需要的数据页不再buffer 中,无法直接使用该数据页,必须等待操作系统完成IO .

      再接着上面的回答提问:

       当第二会话线程执行sql的时候,也需要去访问相同的数据页,它是等待上面的线程将这个数据页读入到缓存中,还是自己再发起一个读磁盘的然后加载到buffer的请求呢?   代码告诉我们,是前者,等待第一个请求该数据页的线程读入buffer pool。

      试想一下,如果第一个请求该数据页的线程因为磁盘IO瓶颈,迟迟没有将物理数据页读入buffer pool, 这个时间区间拖得越长,则造成等待该数据块的用户线程就越多。对高并发的系统来说,将造成大量的等待。 等待数据页读入的函数是buf_wait_for_read,下面是该函数相关的栈。


    通过解析buf_wait_for_read函数的下层函数,我们知道其实通过首先自旋加锁pin的方式,超过设定的自旋次数之后,进入等待,等待IO完成被唤醒。这样节省不停自旋pin时消耗的cpu,但需要付出被唤起时的开销。

    再继续扩展问题: 如果会话线程A 经过物理IO将数据页1001读入buffer之后,他需要修改这个页,而在会话线程A之后的其他的同样需要访问数据页1001的会话线程,即使在数据页1001被入读buffer pool之后,将仍然处于等待中。因为在数据页上读取或者更新的时候,同样需要上锁,这样才能保证数据页并发读取/更新的一致性。

     由此可见,当一个高并发的系统,出现了热点数据页需要从磁盘上加载到buffer pool中时,造成的延迟,是难以想象的。因此排在等待热点页队列最后的会话线程最后才得到需要的页,响应时间也就越长,这就是造成了一个简单的sql需要执行几十秒的原因。

再回头来看上面的问题,mysql数据库出现性能下降时,可以看到操作系统有读IO。 原因是,在数据库对数据页的更改,是在内存中的,然后通过检查点线程进行异步写盘,这个异步的写操作是不堵塞执行sql的会话线程的。所以,即使看到操作系统上有大量的写IO,数据库的性能也是很平稳的。但当用户线程需要查找的数据页不在buffer pool中时,则会从磁盘上读取,在一个热点数据页不是非常多的情况下,我们设置足够大的innodb_buffer_pool的size, 基本可以缓存所有的数据页,因此一般都不会出现缺页的情况,也就是在操作系统上基本看不到读的IO。  当出现读的IO时,原因时在执行buf_read_page_low函数,从磁盘上读取数据页到buffer pool, 则数据库的性能则开始下降,当出现大量的读IO,数据库的性能会非常差。

温馨提示:答案为网友推荐,仅供参考
第1个回答  2017-01-18
问题:

我们可能经常会遇到SQLServer数据库频繁关闭的情况。在分析了内存和CPU使用情况后,我们需要继续调查根源是否在I/O。我们应该如何识别SQLServer是否有I/O相关的瓶颈?

解决:

当数据页经常从缓冲池中移进移出的时候,I/O子系统就会成为SQLServer性能问题的关键因素之一。事务日志和tempdb同样也会产生重大的I/O压力。因此,你必须确保你的I/O子系统能按照预期运行。否则你将会成为响应时间增长和频繁超时的受害者。在这篇文章中,将描述如何使用内置工具识别I/O相关瓶颈,并提供一些磁盘配置的方法:

性能计数器(Performance Monitor):

可以使用性能计数器来检查I/O子系统的负荷。下面的计数器可用于检查磁盘性能:

PhysicalDisk Object:Avg.DiskQueue
Length:计算从物理磁盘中的平均读和写的请求队列。过高的值代表磁盘操作处于等待状态。当这个值在SQLServer峰值时长期超过2,证明需要注意了。如果有多个硬盘,就需要把这些数值除以2。比如,有4个硬盘,且队列为10,那么平均值就是10/4=2.5,虽然也证明需要关注,但不能使用10这个值。

Avg.Disk Sec/Read和Avg.Disk
Sec/Write:显示从磁盘读或者写入磁盘的平均时间。10ms内是很好的表现,20以下还算能接受。高于此值证明存在问题。

Physical Disk:%Disk
Time:在磁盘忙于读或者写请求的时候持续时间的比率。根据拇指定律,此值应该小于50%。

Disk Reads/Sec和Disk
Writes/Sec计数器显示出在磁盘中读写操作的速率。这两个值应该小于磁盘能力的85%。当超过此值,磁盘的访问时间将以指数方式增长。

可以通过以下方式来计算逐渐增长的负载的能力。一种方法是使用SQLIO。你应该找到吞吐量比较稳定,但缓慢增长。

可以使用以下公式来计算RAID配置:

Raid 0: I/O per disk = (reads + writes) / number
ofdisks
Raid 1: I/O per disk = [reads + (writes*2)] /
2
Raid 5: I/O per disk = [reads + (writes*4)] / number of
disks
Raid
10: I/O per disk = [reads +
(writes*2)] / number of disks

比如:对于RAID 1,如果得到下面的计数器:

Disk Reads/sec = 90
Disk
Writes/sec =75

根据公式:[reads + (writes*2)] / 2 or [90 + (75*2)] /
2 = 120I/Os每个磁盘。

动态管理视图(DMVs):

有很多游泳的DMVs可以用于检查I/O瓶颈:

当一个页面被用于读或者写访问且页面在缓冲池中不存在或不可用时,会引发一个I/O闩锁等待(I/O
latch),它会在PAGEIOLATCH_EX/PAGEIOLATCH_SH(具体根据请求类型而定)。这些等待表明一个I/O瓶颈。可以使用sys.dm_os_wait_stats找到闩锁等待的信息。如果你保存了SQLServer正常运行下的waiting_task_counts和wait_time_ms值,并且于此次的值做对比,可以识别出I/O问题:

select *

from sys.dm_os_wait_stats

where wait_type like
'PAGEIOLATCH%'

order by wait_type asc

挂起的I/O请求可以在下面查询中查到,并且用于识别那个磁盘负责的这个瓶颈:
select database_id,
file_id,
io_stall,
io_pending_ms_ticks,
scheduler_address
from sys.dm_io_virtual_file_stats(NULL, NULL) iovfs,
sys.dm_io_pending_io_requests as iopior
where iovfs.file_handle = iopior.io_handle

磁盘碎片(Disk Fragmentation):

建议你检查磁盘碎片和配置用于SQLServer实例的磁盘。在NTFS文件系统中的碎片会产生严重的性能影响。磁盘需要经常整理碎片并且指定整理碎片计划。研究表明,一些情况下SAN在整理碎片后性能更差。因此,SAN必须根据实际情况对待。

NTFS上的索引碎片同样能引起高I/O好用。但是这和在SANs中的效果是不一样的。

磁盘配置/最佳实践:

常规情况,你应该把日志文件和数据文件分开存放以获得更好的性能。对于重负载的数据文件(包括tempdb)的I/O特性是随机读取。对于日志文件,是顺序访问的,除非事务需要回滚。

对于内置磁盘仅仅可以用于数据库日志文件,因为它们对顺序I/O有很好的性能,但是对随机I/O性能低下。

数据库的数据和日志文件应该放在对应专用的磁盘中。确保良好的性能。建议日志文件放在两个内置磁盘,并配置为RAID
1。数据文件驻留在仅用于给SQLServer访问的SAN系统中,并只被查询和报表控制。特殊访问应该被禁止。

写缓冲在可能的情况下应该被允许,并保证断电也能使用。

为了尽可能保证对于OLTP系统的I/O瓶颈影响最小化,不应该把OLAP和OLTP环境混合。并且保证你的代码优化及有合适的索引来避免不必要的I/O。本回答被网友采纳
相似回答