mysql性能下降怎么办 mysql 性能调优( 二 )


如果亲之前没有注意到上述的现象,亲对上述的结论也是怀疑 。但请看下面的分解 。
在写这个文章之前,作者阅读了大量跟的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,数据库的性能会非常差 。

推荐阅读