MysqlINNODB存储引擎详解.docx
《MysqlINNODB存储引擎详解.docx》由会员分享,可在线阅读,更多相关《MysqlINNODB存储引擎详解.docx(23页珍藏版)》请在冰豆网上搜索。
MysqlINNODB存储引擎详解
MysqlInnoDB存储引擎详解
介绍省了……………….
2.2InnoDB体系架构
2.2 InnoDB体系架构
通过第1章我们了解了MySQL的体系结构,现在可能你想更深入地了解InnoDB的架构模型。
图2-1简单显示了InnoDB的存储引擎的体系架构。
InnoDB有多个内存块,你可以认为这些内存块组成了一个大的内存池,负责如下工作:
q维护所有进程/线程需要访问的多个内部数据结构。
q缓存磁盘上的数据,方便快速地读取,并且在对磁盘文件的数据进行修改之前在这里缓存。
q重做日志(redolog)缓冲。
……
图2-1 InnoDB体系结构
后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。
此外,将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常情况下InnoDB能恢复到正常运行状态。
2.2.1 后台线程
由于Oracle是多进程的架构(Windows下除外),因此可以通过一些很简单的命令来得知Oracle当前运行的后台进程,如ipcs命令。
一般来说,Oracle的核心后台进程有CKPT、DBWn、LGWR、ARCn、PMON、SMON等。
很多DBA问我,InnoDB存储引擎是否也是这样的架构,只不过是多线程版本的实现后,我决定去看InnoDB的源代码,发现InnoDB并不是这样对数据库进程进行操作的。
InnoDB存储引擎是在一个被称做masterthread的线程上几乎实现了所有的功能。
默认情况下,InnoDB存储引擎的后台线程有7个—4个IOthread,1个masterthread,1个锁(lock)监控线程,1个错误监控线程。
IOthread的数量由配置文件中的innodb_file_io_threads参数控制,默认为4,如下所示。
mysql>showengineinnodbstatus\G;
***************************1.row***************************
Type:
InnoDB
Name:
Status:
=====================================
10071921:
34:
03INNODBMONITOROUTPUT
=====================================
Persecondaveragescalculatedfromthelast50seconds
……
--------
FILEI/O
--------
I/Othread0state:
waitingfori/orequest(insertbufferthread)
I/Othread1state:
waitingfori/orequest(logthread)
I/Othread2state:
waitingfori/orequest(readthread)
I/Othread3state:
waitingfori/orequest(writethread)
Pendingnormalaioreads:
0,aiowrites:
0,
ibufaioreads:
0,logi/o's:
0,synci/o's:
0
Pendingflushes(fsync)log:
0;bufferpool:
0
45OSfilereads,562OSfilewrites,412OSfsyncs
0.00reads/s,0avgbytes/read,0.00writes/s,0.00fsyncs/s
……
----------------------------
ENDOFINNODBMONITOROUTPUT
============================
1rowinset(0.00sec)
可以看到,4个IO线程分别是insertbufferthread、logthread、readthread、writethread。
在Linux平台下,IOthread的数量不能进行调整,但是在Windows平台下可以通过参数innodb_file_io_threads来增大IOthread。
InnoDBPlugin版本开始增加了默认IOthread的数量,默认的readthread和writethread分别增大到了4个,并且不再使用innodb_file_io_threads参数,而是分别使用innodb_read_io_threads和innodb_write_io_threads参数,如下所示。
mysql>showvariableslike'innodb_version'\G;
***************************1.row***************************
Variable_name:
innodb_version
Value:
1.0.6
1rowinset(0.00sec)
mysql>showvariableslike'innodb_%io_threads'\G;
***************************1.row***************************
Variable_name:
innodb_read_io_threads
Value:
4
***************************2.row***************************
Variable_name:
innodb_write_io_threads
Value:
4
2rowsinset(0.00sec)
mysql>showengineinnodbstatus\G;
***************************1.row***************************
Type:
InnoDB
Name:
Status:
=====================================
10071921:
55:
26INNODBMONITOROUTPUT
=====================================
Persecondaveragescalculatedfromthelast36seconds
......
--------
FILEI/O
--------
I/Othread0state:
waitingfori/orequest(insertbufferthread)
I/Othread1state:
waitingfori/orequest(logthread)
I/Othread2state:
waitingfori/orequest(readthread)
I/Othread3state:
waitingfori/orequest(readthread)
I/Othread4state:
waitingfori/orequest(readthread)
I/Othread5state:
waitingfori/orequest(readthread)
I/Othread6state:
waitingfori/orequest(writethread)
I/Othread7state:
waitingfori/orequest(writethread)
I/Othread8state:
waitingfori/orequest(writethread)
I/Othread9state:
waitingfori/orequest(writethread)
Pendingnormalaioreads:
0,aiowrites:
0,
ibufaioreads:
0,logi/o's:
0,synci/o's:
0
Pendingflushes(fsync)log:
0;bufferpool:
0
3229856OSfilereads,7830947OSfilewrites,1601902OSfsyncs
reads/s,0avgbytes/read,0.00writes/s,0.00fsyncs/s
......
----------------------------
ENDOFINNODBMONITOROUTPUT
============================
1rowinset(0.01sec)
在Windows下,我们还可以通过VisualStudio来调试MySQL,并设置断点来观察所有的线程信息,如图2-2所示。
图2-2 Windows下InnoDB存储引擎的线程
2.2.2 内存
InnoDB存储引擎内存由以下几个部分组成:
缓冲池(bufferpool)、重做日志缓冲池(redologbuffer)以及额外的内存池(additionalmemorypool),分别由配置文件中的参数innodb_buffer_pool_size和innodb_log_buffer_size的大小决定。
以下显示了一台MySQL数据库服务器,它将InnoDB存储引擎的缓冲池、重做日志缓冲池以及额外的内存池分别设置为2.5G、8M和8M(分别以字节显示)。
mysql>showvariableslike'innodb_buffer_pool_size'\G;
***************************1.row***************************
Variable_name:
innodb_buffer_pool_size
Value:
2621440000
1rowinset(0.00sec)
mysql>showvariableslike'innodb_log_buffer_size'\G;
***************************1.row***************************
Variable_name:
innodb_log_buffer_size
Value:
8388608
1rowinset(0.00sec)
mysql>showvariableslike'innodb_additional_mem_pool_size'\G;
***************************1.row***************************
Variable_name:
innodb_additional_mem_pool_size
Value:
8388608
1rowinset(0.00sec)
缓冲池是占最大块内存的部分,用来存放各种数据的缓存。
因为InnoDB的存储引擎的工作方式总是将数据库文件按页(每页16K)读取到缓冲池,然后按最近最少使用(LRU)的算法来保留在缓冲池中的缓存数据。
如果数据库文件需要修改,总是首先修改在缓存池中的页(发生修改后,该页即为脏页),然后再按照一定的频率将缓冲池的脏页刷新(flush)到文件。
可以通过命令SHOWENGINEINNODBSTATUS来查看innodb_buffer_pool的具体使用情况,如下所示。
mysql>showengineinnodbstatus\G;
***************************1.row***************************
Status:
=====================================
09092110:
55:
03INNODBMONITOROUTPUT
=====================================
Persecondaveragescalculatedfromthelast24seconds
......
----------------------
BUFFERPOOLANDMEMORY
----------------------
......
Bufferpoolsize 65536
Freebuffers 51225
Databasepages 12986
Modifieddbpages 8
......
在BUFFERPOOLANDMEMORY里可以看到InnoDB存储引擎缓冲池的使用情况,bufferpoolsize表明了一共有多少个缓冲帧(bufferframe),每个bufferframe为16K,所以这里一共分配了65536*16/1024=1G内存的缓冲池。
Freebuffers表示当前空闲的缓冲帧,Databasepages表示已经使用的缓冲帧,Modifieddbpages表示脏页的数量。
就当前状态看来,这台数据库的压力并不大,因为在缓冲池中有大量的空闲页可供数据库进一步使用。
注意:
showengineinnodbstatus的命令显示的不是当前的状态,而是过去某个时间范围内InnoDB存储引擎的状态,从上面的示例中我们可以看到,Persecondaveragescalculatedfromthelast24seconds表示的信息是过去24秒内的数据库状态。
具体来看,缓冲池中缓存的数据页类型有:
索引页、数据页、undo页、插入缓冲(insertbuffer)、自适应哈希索引(adaptivehashindex)、InnoDB存储的锁信息(lockinfo)、数据字典信息(datadictionary)等。
不能简单地认为,缓冲池只是缓存索引页和数据页,它们只是占缓冲池很大的一部分而已。
图2-3很好地显示了InnoDB存储引擎中内存的结构情况。
参数innodb_buffer_pool_size指定了缓冲池的大小,在32位Windows系统下,参数innodb_buffer_pool_awe_mem_mb还可以启用地址窗口扩展(AWE)功能,突破32位下对于内存使用的限制。
但是,在使用这个参数的时候需要注意,一旦启用AWE功能,InnoDB存储引擎将自动禁用自适应哈希索引(adaptivehashindex)的功能。
图2-3 InnoDB存储引擎内存结构
日志缓冲将重做日志信息先放入这个缓冲区,然后按一定频率将其刷新到重做日志文件。
该值一般不需要设置为很大,因为一般情况下每一秒钟就会将重做日志缓冲刷新到日志文件,因此我们只需要保证每秒产生的事务量在这个缓冲大小之内即可。
额外的内存池通常被DBA忽略,认为该值并不是十分重要,但恰恰相反的是,该值其实同样十分重要。
在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。
在对一些数据结构本身分配内存时,需要从额外的内存池中申请,当该区域的内存不够时,会从缓冲池中申请。
InnoDB实例会申请缓冲池(innodb_buffer_pool)的空间,但是每个缓冲池中的帧缓冲(framebuffer)还有对应的缓冲控制对象(buffercontrolblock),而且这些对象记录了诸如LRU、锁、等待等方面的信息,而这个对象的内存需要从额外内存池中申请。
因此,当你申请了很大的InnoDB缓冲池时,这个值也应该相应增加。
2.3masterthread
2.3 masterthread
通过对前一小节的学习我们已经知道,InnoDB存储引擎的主要工作都是在一个单独的后台线程masterthread中完成的。
这一节我们将具体解释该线程的具体实现以及该线程可能存在的问题。
2.3.1 masterthread源码分析
masterthread的线程优先级别最高。
其内部由几个循环(loop)组成:
主循环(loop)、后台循环(backgroundloop)、刷新循环(flushloop)、暂停循环(suspendloop)。
masterthread会根据数据库运行的状态在loop、backgroundloop、flushloop和suspendloop中进行切换。
loop称为主循环,因为大多数的操作都在这个循环中,其中有两大部分操作:
每秒钟的操作和每10秒的操作。
伪代码如下:
voidmaster_thread(){
loop:
for(inti=0;i<10;i++){
dothingoncepersecond
sleep1secondifnecessary
}
dothingsoncepertenseconds
gotoloop;
}
可以看到,loop循环通过threadsleep来实现,这意味着所谓的每秒一次或每10秒一次的操作是不精确的。
在负载很大的情况下可能会有延迟(delay),只能说大概在这个频率下。
当然,InnoDB源代码中还采用了其他的方法来尽量保证这个频率。
每秒一次的操作包括:
q日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)。
q合并插入缓冲(可能)。
q至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能)。
q如果当前没有用户活动,切换到backgroundloop(可能)。
即使某个事务还没有提交,InnoDB存储引擎仍然会每秒将重做日志缓冲中的内容刷新到重做日志文件。
这一点是必须知道的,这可以很好地解释为什么再大的事务commit的时间也是很快的。
合并插入缓冲(insertbuffer)并不是每秒都发生。
InnoDB存储引擎会判断当前一秒内发生的IO次数是否小于5次,如果小于5次,InnoDB认为当前的IO压力很小,可以执行合并插入缓冲的操作。
同样,刷新100个脏页也不是每秒都在发生。
InnoDB存储引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中innodb_max_dirty_pages_pct这个参数(默认为90,代表90%),如果超过了这个阈值,InnoDB存储引擎认为需要做磁盘同步操作,将100个脏页写入磁盘。
总结上述3个操作,伪代码可以进一步具体化,如下所示:
voidmaster_thread(){
gotoloop;
loop:
for(inti=0;i<10;i++){
thread_sleep
(1)//sleep1second
dologbufferflushtodisk
if(last_one_second_ios<5)
domergeatmost5insertbuffer
if(buf_get_modified_ratio_pct>innodb_max_dirty_pages_pct)
dobufferpoolflush100dirtypage
if(nouseractivity)
gotobackgroudloop
}
dothingsoncepertenseconds
backgroundloop:
dosomething
gotoloop:
}
接着来看每10秒的操作,包括如下内容:
q刷新100个脏页到磁盘(可能)。
q合并至多5个插入缓冲(总是)。
q将日志缓冲刷新到磁盘(总是)。
q删除无用的Undo页(总是)。
q刷新100个或者10个脏页到磁盘(总是)。
q产生一个检查点(总是)。
在以上的过程中,InnoDB存储引擎会先判断过去10秒之内磁盘的IO操作是否小于200次。
如果是,InnoDB存储引擎认为当前有足够的磁盘IO操作能力,因此将100个脏页刷新到磁盘。
接着,InnoDB存储引擎会合并插入缓冲。
不同于每1秒操作时可能发生的合并插入缓冲操作,这次的合并插入缓冲操作总会在这个阶段进行。
之后,InnoDB存储引擎会再执行一次将日志缓冲刷新到磁盘的操作,这与每秒发生的操作是一样的。
接着InnoDB存储引擎会执行一步fullpurge操作,即删除无用的Undo页。
对表执行update、delete这类操作时,原先的行被标记为删除,但是因为一致性读(consistentread)的关系,需要保留这些行版本的信息。
但是在fullpurge过程中,InnoDB存储引擎会判断当前事务系统中已被删除的行是否可以删除,比如有时候可能还有查询操作需要读取之前版本的Undo信息,如果可以,InnoDB会立即将其删除。
从源代码中可以发现,InnoDB存储引擎在操作fullpurge时,每次最多删除20个Undo页。
然后,InnoDB存储引擎会判断缓冲池中脏页的比例(buf_get_modified_ratio_pct),如果有超过70%的脏页,则刷新100个脏页到磁盘;如果脏页的比例小于70%,则只需刷新10%的脏页到磁盘。
最后,InnoDB存储引擎会产生一个检查点(checkpoint),InnoDB存储引擎的检查点也称为模糊检查点(fuzzycheckpoint)。
InnoDB存储引擎在checkpoint时并不会把所有缓冲池中的脏页都写入磁盘,因为这样可能会对性能产生影响,而只是将最老日志序列号(oldestLSN)的页写入磁盘。
现在,我们可以完整地把主循环(mainloop)的伪代码写出来了,内容如下:
voidmaster_thread(){
gotoloop;
loop:
for(inti=0;i<10;i++){
thread_sleep
(1)//sleep1second
dologbufferflushtodisk
if(last_one_second_ios<5)
domergeatmost5insertbuffer
if(buf_get_modified_ratio_pct>innodb_max_dirty_pages_pct)
dobufferpoolflush100dirtypage
if(nouseractivity)
gotobackgroudloop
}
if(last_ten_second_ios<20