以前对于各种引擎也稍微有点理解,可是却并没有深入研究过,最近打算看看Innodb引擎,

 InnoDB 存储引擎

前言:

数据库:物理操作系统文件或其他形式文件类型的集合,

数据库实例:有数据库后台进程/线程以及一个共享内存区组成

mysql被设计成了一个单进程多线程架构的数据库

开始:

1、默认的InnoDB存储引擎的后台线程有7个,4个IO thread ,1个master thread 1个锁监控 thread 1个错误监控thread,IO thread 的数量由配置文件的innodb_file_io_threads参数控制,默认是4,linux下面不可以调整,但是window下面可以

show engine innodb status \G;(root用户,或者你的用户有查看权限)

show variables like 'innodb_version' \G;

show variables like 'innodb_%io_threads' \G;

注释:我十分建议大家安装独立的mysql,不要用集成环境,因为出现问题会后悔死的

2、innodb存储引擎内存有以下部分:

buffer pool  缓冲池
redo log buffer  重做日志缓冲池
additional memory pool 额外内存池

配置文件的innodb:
# Comment the following if you are using InnoDB tables
#skip-innodb
innodb_data_home_dir = "D:/xampp/mysql/data"
innodb_data_file_path = ibdata1:10M:autoextend
innodb_log_group_home_dir = "D:/xampp/mysql/data"
#innodb_log_arch_dir = "D:/xampp/mysql/data"
## You can set .._buffer_pool_size up to 50 - 80 %
## of RAM but beware of setting memory usage too high
innodb_buffer_pool_size = 16M
innodb_additional_mem_pool_size = 2M
## Set .._log_file_size to 25 % of buffer pool size
innodb_log_file_size = 5M
innodb_log_buffer_size = 8M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50

这是my.ini的配置,更多的InnoDB的配置,可以看my_innodb_heavy_4G.ini


注释:配置文件的值可能会根据不同的环境更改,上面的配置文件是在我安装之后默认的

3、缓冲池是用来存放各种数据的缓存,InnoDB存储引擎的工作方式是将数据库文件按页(每页16K)读取到缓冲池,然后按照最近最少使用(LRU)的算法保留在缓冲池中的缓存数据

 输入:show  engine innodb status\G;

显示:


Dictionary memory allocated 22124
Buffer pool size   1024  //表示有多少缓冲帧 buffer frame 每个buffer frame 16k
Free buffers       876   //当前空闲的 buffer frame
Database pages     147   //已经使用的 buffer frame
Old database pages 0
Modified db pages  0     //表示脏页的数量
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 144, created 3, written 37
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.


 

注释:show engine innodb status 的命令显示的不是当前的状态,而是过去某个范围内的InnoDB存储引擎的状态


4、buffer pool里面的缓存的数据页类型有:
索引页
数据页
undo页
插入缓冲 insert buffer
自适应哈希索引 adaptive hash index
InnoDB存储的锁信息 lock info
数据字典信息 data dictionary (DD)
and so on;

5、在32位win下,参数innodb_buffer_pool_awe_mem_mb,可以启动地址窗口扩展(AWE)功能,突破32位下对内存使用的限制,但是一旦启动了AWE,InnoDB存储引擎将自动禁用adaptive hash index

6、日志缓冲将重做日志信息先放入这个缓冲区,然后按照一定的平率将其刷新到重做日志文件,不需要设置很大,保证每秒的产生事务量在这个缓冲大小即可

7、额外的内存池, 在Innodb存储引擎中,对内存的管理师通过一种被称为内存堆(heap)的方式进行的,对一些数据结构本身分配内存时,需要先从额外的内存池中申请,当内存不够时,才会从缓冲池中申请,

innoDB的instance 会申请innodb_buffer_bool的space,

但是每个buffer pool 中的frame  buffer(帧缓冲)还有对应的缓冲控制对象,buffer control block 因此当你的buffer pool足够大的时候,额外内存池也应该增大

 

128G内存mariadb调优配置 mariadb 内存占用_缓冲池


128G内存mariadb调优配置 mariadb 内存占用_数据_02

 

 

adaptive hash index :默认是开启的,哈希索引只能用来搜索等值的查询.对于其他查找类型,如范围查找,是不能使用的.

注释:我的数据时模拟的百万级数据,但是我还是没有捕捉到adaptive hash index 的有关应用


128G内存mariadb调优配置 mariadb 内存占用_128G内存mariadb调优配置_03

 

二、master Thread 源码分析:

1、master thread n内部组成:
主循环(loop) 
后台循环(background loop)
刷新循环(flush loop)
暂停循环(suspend loop)

master thread会在这几个状态中切换,在负载很大的时候可能造成延迟delay,

每秒一次的操作包括:

日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)

合并插入缓冲(Maybe)

至多刷新100个Innodb的缓冲池中的脏页到磁盘(Maybe)

如果当前用户没有活动,则切换到background loop(Maybe)

脏页比例: buf_get_modified_ratio_pct 是否超过了innodb_max_dirty_pages_pct这个参数,默认是90(90%) 如果超过了这个阀值,InnoDB认为需要做磁盘同步操作,将100脏页写入磁盘

full purge :删除无用的Undo页

在full purge 的过程中,先判断当前事务系统中已被删除的行是否可以删除,如果可以,会即可删除,

full purge 的时候,每次最多删除20个Undo页

1_second :loop

sleeps:睡眠

background:background loop

flush:flush loop;

InnoDB会对其内部进行一些优化,当压力大时并不是总是等待1秒钟,因为我们不可以认为1_seconds 和sleeps相同

某些情况下,二者只差可以被看做是数据库的负载压力


InnoDB的关键特性:

插入缓冲(insert buffer) //和data page 一样,是物理页的一个组成部分

两次写(double write)

自适应哈希索引(adaptive hash index)

InnoDB的主键建立聚集(聚簇)索引,如果你的表不需要特殊的聚簇索引,一个好的做法就是使用代理主键(surrogate key)。

如果你的表没有主键,如果你的表里面存在not null并且 unique index,那么innodb就会选择该索引作为主键,否则就自己内建一个类似的rowid的列,6个字节的隐形字段

show table status from dbName;//查看数据库表的info

innodb_force_recovery :1-6;

1:SRV_FORCE_IGNORE_CORRUPT :忽略检查到corrupt页
2:SRV_FORCE_NO_BACKGROUND:阻止主线程的运行,如主线程需要full purge 操作,会导致crash
3:SRV_FORCE_NO_TRX_UNDO:不执行事务回滚操作
4:SRV_FORCE_NO_IBUF_MERAGE:不执行插入缓冲的合并操作
5:SRV_FORCE_NO_UNDO_LOG_SCAN:不查看撤销日志(undo log),Innodb存储引擎会将未提交的事务视为已提交
6:SRV_FORCE_NO_LOG_REDO:不执行前滚操作

if(innodb_force_recovery ){
    you can do select ,create ,drop for table;
}

show variables like 'innodb_version'\G;查看innodb的varsion info



在数据库数据处理中, 缓冲在改善性能方面扮演着很重要的角色, 为了保证性能, innodb 维护了自己的缓冲池。 文章大体介绍一下innodb缓冲区实现和管理策略。

在innodb中,需要用到数据页(需要保存到磁盘的数据)均是从这个缓冲池里分配出来的, 因此,可以说,缓冲池在对innodb的性能有很大的影响。

几个基本的概念

AWE:地址窗口化扩展,允许在 32 位版本的 Windows 操作系统上使用 4 GB 以上的物理内存。最多可支持 64 GB 的物理内存。更多信息请看 http://baike.baidu.com/view/1390438.htm; innodb是支持AWE内存管理的

Frame;帧,16K的虚拟地址空间, 在缓冲池的管理上,整个缓冲区是是以大小为16k的frame(可以理解为数据块)为单位来进行的,frame是innodb中页的大小。

Page: 页,16K的物理内存, page上存的是需要保存到磁盘上的数据, 这些数据可能是数据记录信息, 也可以是索引信息或其他的元数据等;

Control Block:控制块,对于每个frame, 有一个block, block上的信息是专门用于进行frame控制的管理信息, 但是这些信息不需要记录到磁盘,而是根据读入数据块在内存中的状态动态生成的, 主要包括: 1. 页面管理的普通信息,互斥锁, 页面的状态, awe(windows平台上awe机制的管理信息)等 2. 脏回写(flush)管理信息3. lru控制信息 4. 快速查找的管理信息, 为了便于快速的超找某一个block或frame, 缓冲区里面的block被组织到一些hash表中; 缓冲区中的block的数量是一定得, innodb缓冲区对所管理的block用lru策略进行替换。

互斥访问

缓冲池的整个缓冲区有一个数据结构buf_pool进行管理和控制, 有一个专门的mutex保护着, 这个mutex是用来保护buf_pool这个控制结构中的数据域的, 并不保护缓冲区中的数据frame以及用于管理的block, 缓冲区里block或者frame中的访问是由专门的读写锁来保护的, 每个block/frame有一个。在5.1以前, 每个block是没有专门的mutex保护的,如果需要进行互斥保护,直接使用缓冲区的mutex, 结果导致很高的争用; 5.1以后,每个block有一个mutex对其进行保护, 从而在很大程度上解缓了对buf_pool的mutex的争用。

缓冲池管理用到的几个重要的列表

在缓冲区的管理中, 有几个重要的block列表(双向链表):

LRU列表: 用来进行lru管理的列表, 列表里的每个block所控制的数据都是当前有效的数据;列表中的block基本是按照访问的顺序排列的;最近被访问的放在最前面, 最先被方位的放在最后;lru列表中维护中维护了一个LRU_old, 大概指向整个列表的倒数3/8左右, 当增加一个新的block(主语与最近被访问的block的区别)进来的时候, 把新的块刚好放到这个点附近, 具体是前还是后取决于这个LRU_old目前的位置, 这么做的目的是使新增加进来的block放到一个合适的位置, 不至于放到最先(最近被使用过)或最后(最老)

flush列表: 列表block是那些所管理的数据被修改但是还没有更新到磁盘的脏frame, 根据修改的先后顺序排列的block列表, 最老的放最后。innodb起来后, 主线程会定期去检查

缓冲区中可用空闲block的数量的比例, 一旦大于srv_max_buf_pool_modified_pct(90%), 就会试图把一些脏页flush到磁盘;除了主线程会定期做这个事情外,工作线程在进行数据操作时 ,如果发现没有如果的block, 也会通过flush一些脏页来腾出空间。

free list, 所有空闲block的列表, 当需要分配一个block时, 从中取出一个block。

awe_LRU_free_mapped: 用于方便awe的block列表, 这些block所管理的page已经映射到了frame(物理内存有对应的虚拟内存空间),其中的元素必定也处于free列表或lru列表中。 这个列表会在当分配一个awe页面时用到。

adaptive hash search: 缓冲区中的页面是通过双向列表的方式组织起来的, 如果需要查找根据页号查找某个页面block的话, 速度不会快,尤其是数据块多的时候; 为了加速查找过程,在用双向列表组织block的时候,也采用了adaptive hash的数据组织, hash的键值(key)是页号(数据页在所在表空间的编号), 这个在一定程度上能加速block的查找, 但是当以awe方式管理内存的话,这种

hash查找方式不会启用

当读入页面的时候, 首先需要找到一个可用的block, 这个block或者来自于free list或者lru-list; 在读入数据块的时候, block会加上排他锁以防止其他线程再次使用这个block,

会在block中标记出这个块正处于io状态, 然后把io请求加入到io调用请求对列; 完成读入操作时, 再释放block上的拍它锁更新io状态。

数据页面的读取写入

缓冲池中的数据基本是采用同步aio的方式,这里的同步aio的意思是: 工作线程发出读或写请求后,请求会被放到一个读写请求队列中,由专门的io线程负责写盘和读盘,而工作线程则等待io的完成,

注意, 这里是等待完成,而不会放弃执行,因而称作为同步aio.

提前读(预读)机制

为了优化数据读入性能, 缓冲区读入采取了提前读的机制(当然,这个机制可以配置成不激活),当然, 提前读的机制是基于数据局部性的原理来的,

这就是为什么采用取值过于随机的字段作为主键会导致性能降低的原因之一:过于随机的主键会导致提前读不起作用, 而且会导致更多的换页行为; 这个预读取对于上层的功能如索引管理是透明的,

对于上层的功能来说, 需要提供的信息是需要读取的页是否有后继页和前置页。 有两种预读机制: 线性预读和随机预读

线性预读: 当第一读缓冲区中某一个已经存在(注意,这里必须是已经存在于缓冲区额数据块)的数据块时, 会检查这个数据块是不是处于所谓的线性预读区域(比如, 区域大小是64, 当前读入的也是100,那么所在的预读区域是65 ~ 128),如果是, 则统计一下这个区域中目前没有被访问过的页面,如果数量多于预读机制设定的预读数量,则放弃本次预读, 这个其实是检查目前的区域是否还有大量的页面没有被访问过, 如果是的话, 自然没有必要去做预读了; 否则, 取得读取页面的后继页和前置页(按照数据页的自然顺序?,然后检查后继页或者前置页是否是一个新区域的边界,如果是, 则发出读取

该区域里面的数据页面的异步请求。

随机读: 当读取一个页面时, 根据所读取页的位置计算出该页面所在的随机预读区域, 区域的计算也基于设定的区域页面数量来计算的(如前面关于线性预读的例子), 然后根据lru对列的信息计算该区域中有多少块最近被访问到了, 如果被访问的数量达到一定的额度(这个额度是根据预读区域的大小计算出来的, 5 + 预读区域大小 / 8),则预读取该区域中目前还没有读取到缓冲区的块.

show innodb status 中关于buffer pool的输出

Buffer pool size 262144 整个缓冲池中的页的数量, 包括flush列表中的和flush列表中的,以及被分配出去的页的数量

Free buffers 0           free列表中的页的数量

Database pages 258053    分配出去, 正在被使用页的数量

Modified db pages 37491 flush列表中的数量

Pending reads 0          发出了请求但没有完成的io读个数

Pending writes: LRU 0, flush list 0, single page 0 发出了请求但没有完成的io读个数在各个列表上的体现

Pages read 57973114, created 251137, written 10761167, 从磁盘上读取出来的页数, 在内存中建立了页面但是没有从磁盘上读取出来的页面数以及写入了的页面数

9.79 reads/s, 0.31 creates/s, 6.00 在刚过去的时间间隔里, 平均每秒的读取数和新建数

Buffer pool hit rate 999 / 1000       命中率, 缓冲区中读到的页 / 总共发出的读页数

与缓冲池相关的状态变量及含义

| Innodb_buffer_pool_pages_data      分配出去, 正在被使用页的数量

| Innodb_buffer_pool_pages_dirty     脏页但没有被flush除去的页面数

| Innodb_buffer_pool_pages_flushed     已经flush的页面数

| Innodb_buffer_pool_pages_free        当前空闲页面数

| Innodb_buffer_pool_pages_latched     当前被锁住的页面数

| Innodb_buffer_pool_pages_misc        用于管理功能的页面数, 如adaptive hash等

| Innodb_buffer_pool_pages_total    缓冲区总共的页面数

| Innodb_buffer_pool_read_ahead_rnd     随机预读的次数

| Innodb_buffer_pool_read_ahead_seq 线性预读的次数

| Innodb_buffer_pool_read_requests 总共从缓冲池中缓存的页面中读取出的页数

| Innodb_buffer_pool_reads             从磁盘上一页一页的读取的页数,从缓冲池中读取页面, 但缓冲池里面没有, 就会从磁盘读取

| Innodb_buffer_pool_wait_free        缓冲池等待空闲页的次数, 当需要空闲块而系统中没有时, 就会等待空闲页面

| Innodb_buffer_pool_write_requests 缓冲池总共发出的写请求次数

| Innodb_data_fsyncs                    总共完成的fsync次数

| Innodb_data_pending_fsyncs           innodb当前等待的fsync次数

| Innodb_data_pending_reads          innodb当前等待的读的次数

| Innodb_data_pending_writes           innodb当前等待的写的次数

| Innodb_data_read                       |    总共读入的字节数

| Innodb_data_reads                      innodb完成的读的次数

| Innodb_data_writes                   innodb完成的写的次数

| Innodb_data_written                 总共写出的字节数

关于缓冲池的关键的配置变量

innodb_buffer_pool_size: 缓冲池的大小