当前位置:Gxlcms > 数据库问题 > mysql 默认引擎innodb 初探(二)

mysql 默认引擎innodb 初探(二)

时间:2021-07-01 10:21:17 帮助过:2人阅读

master_thread(){ loop: //每秒操作 for(int i-0; i<10; i++){ thread_sleep(1); //线程休眠1秒,当负载很大时,可能会小于1秒 do log buffer flush to disk //刷新日志缓冲到磁盘 //若果上一秒io次数小于5,说明当前io压力较小,进行插入缓冲合并 // if(last_one_second_io_num < 5 ){ 1.0x之前判断方式,硬编码处理 if(last_one_second_io_num < innodb_io_capacity*5% ){ do merge at most 5 insert buffer } //当当前缓冲脏页比例大于配置文件中最大脏页比例时,刷新至多100个脏页到磁盘 if(buf_get_modified_ratio_pct > innodb_max_dirty_page_pct){ // do buffer pool flush 100 dirty pages 1.0x之前,硬编码处理 do buffer pool flush innodb_io_capacity*100% dirty pages }else if(enable_adaptive_flush){ do buffer pool flush desired amount dirty page //1.0x之后引入自适应刷新策略 } //如果当前没有活动用户则切换到后台循环 if( no user activity ){ goto backgroup loop } } //每十秒操作 //如果10秒内io数量小于200,说明当前io压力较小,则刷新100脏页到磁盘 // 1.0x之前,硬编码处理 // if( last_ten_second_io_num < 200 ){ // do buffer pool flush 100 dirty pages // } if(last_tem_second_io_num < innodb_io_capacity){ do buffer pool flush innodb_io_capacity*100% dirty pages } // do merge at most 5 insert buffer 总是合并至多5个插入缓冲 1.0x之前,硬编码处理 do merge at most innodb_io_capacity*5% insert buffer //总是合并至多5个插入缓冲 do log buffer to disk //总是刷新日志缓冲到磁盘 do full purge //总是删除无用undo页 //根据缓冲脏页比例决定刷新100 or 10个脏页到磁盘 if( buf_get_modified_pct > 70% ){ // do buffer pool flush 100 dirty pages 1.0x之前,硬编码处理 do buffer pool flush innodb_io_capacity*100% dirty pages }else{ // do buffer pool flush 10 dirty pages 1.0x之前,硬编码处理 do buffer pool flush innodb_io_capactiy*10% dirty pages } backgroup loop: do full page //删除无用undo页 // do merge 20 insert buffer 合并20个插入缓冲 1.0x之前,硬编码处理 do merge innodb_io_capacity insert buffer //合并插入缓冲 //如果空闲则跳到flush loop,否则跳到loop if( not idle ){ goto loop }else{ goto flush loop } flush loop: // do buffer pool flush 100 dirty page 1.0x之前,硬编码处理 do buffer pool flush innodb_io_capacity*100% dirty page if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct ){ goto flush loop }else{ goto suppend loop } suppend loop: suppend_thread() //挂起master thread waiting event //等待事件 goto loop //跳到主循环 }

由上面一段伪代码可以清楚了解到,innodb1.0x之前,每秒or十秒的io量写死在代码中,由于SSD的出现及磁盘的发展,需要更高的io处理,于是在1.0x版本,引入的innodb_io_capacity参数来动态调节io吞吐量;

innodb_max_dirty_pages_pect 太大(eg: 90%),如果内存太大或者服务器压力过大,则刷新脏页速度会降低,推荐值为75%

1.0x版本,引入了 innodb_adapitive_flushing(自适应刷新)参数,当脏页比例小于innodb_max_dirty_pages_pect时,会进行自动刷新一定数量脏页;(innodb_adapitive_flushing根据产生重做日志redo log的速度决定最适合的刷新脏页数量)

1.0x版本之前,full purge操作,最多回收20个undo页,从1.0x版本,引入了innodb_purge_batch_size参数,可以调控回收undo页数;
show variables like “innodb_purge_batch_size”\G

1.2x版本开始,master thread中刷新脏页操作,分离到单独的page cleaner thread中,减轻master thread负担并进一步提高 系统并发性。


innodb关键特性

  • 插入缓冲(insert buffer) 提高性能
  • 两次写(double write) 提高安全性
  • 自适应哈希索引(adapitive hash index) innodb内部自动优化
  • 异步IO(async IO) 提高并发
  • 刷新领接页(flush neighbor page) 提高性能

插入缓冲

插入缓冲简介

innodb存储引擎,主键是行唯一的标示符,通常是顺序递增的插入,因此插入聚集索引(primary key)一般是顺序存放的,不需要随机离散读取磁盘,速度很快;

但是通常表不止有聚集索引,还会有一些非聚集索引并且不是顺序存放的,非聚集索引的叶子节点的插入是离散的访问非聚集索引页,随机读取导致了性能下降

对于非聚集索引的插入或更新操作,innodb引入了插入缓冲,
插入或更新非聚集索引时,首先判断对于的非聚集索引页是否在缓冲池中,若果存在,则直接插入;
否则,先放入到inssert buffer对象中,然后再一定的频率或特殊情况下,进行insert buffer 和辅助索引页子节点的merge(合并)操作;(如:master thread 中insert buffer merge);

插入缓冲使用条件

  • 索引时辅助索引(非聚集索引)
  • 索引不是唯一的(unique key 每次插入前都会进行查找是否已经存在)

Innodb1.0x开始引入Change Buffer,将insert buffer根据DML操作(insert,update,delete)不同,分别进行insert buffer, purge buffer,delete buffer;

update 操作:

  • 将记录标记为删除
  • 真正删除记录

delete buffer 对应将记录标记删除,purge buffer对应将记录真正删除

ps:
    当数据库写比较密集时,insert buffer会占用过多缓冲池,默认最大可以占用1/2的缓冲池内存

    innodb1.2x开始,可以通过innodb_change_buffer_max_size调节change buffer最大占用缓冲池的百分比
插入缓冲内部实现

insert buffer的数据结构是一颗B+树,默认存放在共享表空间中(ibdata1)【tips:当然缓冲池中页有对应的insert buffer 缓冲区】;

insert buffer B+的非叶子节点存放的是查询key(search key);

技术分享

  • space : 待插入记录所在表的空间id(innodb存储引擎中,每个表有一个唯一space id)
  • marker : 用于兼容老版本的insert buffer,暂忽略
  • offset : 表示页所在的偏移量

当辅助索引要插入到辅助索引页中时,若果该辅助索引页不在缓冲池中,则根据上图 规则构造一个search key并插入到 insert buffer B+树中;

插入叶子节点结构如下:

技术分享

技术分享

  • IBUF_REC_OFFSET_COUNT : 两个字节,用于排序每个记录进入insert buffer的顺序

为了能够保证每次合并插入缓冲时,每个辅助索引页都有足够的空间进行存储,需要一个特殊的页用来标记每个辅助索引页的可用空间,称这个页为 insert buffer bitmap

每个insert buffer bitmap页追踪16384个辅助索引页(256个区,详情后续innodb 数据也结构会进行详细讲解);

技术分享

进行插入缓冲合并情况

merge insert buffer:

  • 辅助索引页被读取到缓冲池
    当select查询时,将辅助索引页读入缓冲池时,需要检查insert buffer bitmap页,然后确认该辅助索引页是否存有记录于insert buffer B+树中,如果存在,则将insert buffer B+树中记录插入到该辅助索引页中

  • insert buffer bitmap页追踪到该辅助索引页已经无可用空间
    insert buffer bitmap页用来追踪每个辅助索引页的可用空间 ,保证至少有1/32页的空间可用,否则会强制进行一次合并,将insert buffer B+树中该页的记录插入到该辅助索引页中

  • master thread
    主线程,每秒 or 每十秒会进行一次 merge insert buffer操作

两次写(double write)

当innodb存储引擎正在写入某个页到表中,只写了部分数据(16k的页,写入4k)时,发生了宕机,称为部分写失效(partial page write);
由于重做日志是基于偏移量进行记录的(如偏移299,写’insert data’),如果想通过重做日志进行恢复,必须要页的一份副本;
写数据之前,保存一份页的副本,即doublewrite

技术分享

double write 有两部分组成:

  • double write buffer (2MB)
  • 物理磁盘上共享表空间连续的128页 (2MB)

当刷新脏页时,先通过memcpy函数将脏页复制到double write buffer,之后分两次,每次将1MB顺序地写入共享表空间的物理磁盘上(由于是顺序写,速度非常快);然后再离散的将脏页刷回到具体对应的页中;

tips : 有些系统文件提供了部分写失效的防范措施(eg:ZFS文件系统) ,可以通过 innodb_doublewrite = OFF 禁用两次写

自适应哈希索引

innodb存储引擎会监控对表上各索引的查询,如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称为 自适应哈希索引(adapitive hash index AHI);

AHI通过缓冲池中B+树构造的,因此建立数据很快;

由于哈希索引只能进行等值查询,不能进行范围查询,一定程度上有局限性

可以通过观察has searches 与 non hash searches 考虑是否禁用该特性,默认为开启

技术分享

异步io(AIO)

当前数据库系统,为了提高磁盘操作性能,都采用了异步IO;

可以通过 show variables like “innodb_use_native_aio”\G 查看是否开启;

刷新邻接页

当刷新一个脏页时,innodb存储引擎会检测该页所在区(extend)的所有页,如果有其他脏页,则一起进行刷新操作;

tips:

  • 对应机械硬盘,邻接页刷新可以将多个IO写操作,通过异步io特性,合并为一个io操作,可以显著提高性能
  • 对应SSD,由于有较高的IOPS,建议关闭此特性

启动 ,关闭和恢复

后记

后续会对 innodb存储引擎相关文件进行探索

mysql 默认引擎innodb 初探(二)

标签:

人气教程排行