MySQL 5.6.12的Innodb性能改进

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:
简单的记录下,在MySQL5.6.12中innodb层的3点跟性能相关的改进
1.在文件操作部分,移除了许多sleep操作,而是改用condition wait
对应的bug  http://bugs.mysql.com/bug.php?id=68588。 在Mark的测试中,有近一倍的性能提升
主要修改都几种在函数fil_flush中:
每个文件结构体node都增加了一个event:
fil_node_create:
     node->sync_event = os_event_create();
fil_node_free:      
  os_event_free(node->sync_event)

fil_flush:

当文件上已经有线程在做flush时:
5638                 if (node->n_pending_flushes > 0) {
5639                         /* We want to avoid calling os_file_flush() on
5640                         the file twice at the same time, because we do
5641                         not know what bugs OS's may contain in file
5642                         i/o */
5643
5644                         ib_int64_t sig_count =
5645                                 os_event_reset(node->sync_event);
5646
5647                         mutex_exit(&fil_system->mutex);
5648
5649                         os_event_wait_low(node->sync_event, sig_count);
5650
5651                         mutex_enter(&fil_system->mutex);
5652
5653                         if (node->flush_counter >= old_mod_counter) {
5654
5655                                 goto skip_flush;
5656                         }
5657
5658                         goto retry;
5659                 }

5661                 ut_a(node->open);
5662                 file = node->handle;
5663                 node->n_pending_flushes++;
5664
5665                 mutex_exit(&fil_system->mutex);
5666
5667                 os_file_flush(file);
5668
5669                 mutex_enter(&fil_system->mutex);
5670
5671                 os_event_set(node->sync_event);
5672
5673                 node->n_pending_flushes--;

 

好吧,我承认我摘录上述代码的目的,只是简单记录下innodb condition wait的用法…
2.用户线程在查找空闲block,会刷单个page,这可能导致sync所有的文件
用户线程做了single page flush后,加入一个IO异步请求队列后,会调用 buf_flush_sync_datafiles.随后会唤醒IO线程,并在之后fsync所有的数据文件。           
该bzr主要修改包括:
* 所有batch flush操作异步进行(和以前一样)
buf_dblwr_flush_buffered_writes:
914         for (ulint i = 0; i < first_free; i++) {
915                 buf_dblwr_write_block_to_datafile(

916                         buf_dblwr->buf_block_arr[i], false);              // false表示异步写

917         }

* single page flush以寻找一个空闲块,这是同步操作
buf_flush_single_page_from_LRU->buf_flush_page->buf_flush_write_block_low->buf_dblwr_write_single_page
1130         /* We know that the write has been flushed to disk now

1131         and during recovery we will find it in the doublewrite buffer

1132         blocks. Next do the write to the intended position. */

1133         buf_dblwr_write_block_to_datafile(bpage, sync);      //该backtrace的sync为TRUE

buf_flush_page在多处被调用到,其增加了一个sync参数,用于表示该page flush操作需要同步还是异步进行。而在5.6.11中会在调用buf_flush_page后调用buf_flush_sync_datafiles()来sync所有的数据文件。
另外,有一种情况,在做SINGLE PAGE FLUSH时,采用异步的方式,backtrace如下:

row_import_for_mysql->buf_LRU_remove_pages->buf_flush_dirty_pages->buf_flush_or_remove_pages->buf_flush_page
这其实就是5.6的新特性ibd import功能的backtrace,后面单独开篇介绍

* 将single page flush的dblwr slot处理转移到IO线程

函数:buf_dblwr_update
当IO操作完成写一个page后,这个函数会被调用到,在5.6.12中会做两件事儿:
对于batch flush操作(BUF_FLUSH_LIST 或者BUF_FLUSH_LRU),当预留给batch flush的slot都全部完成刷新后(buf_dblwr->b_reserved = 0),会去sync数据文件(fil_flush_file_spaces(FIL_TABLESPACE)),将batch_running设为false,并发送完成信号
对于single page flush操作,找到当前page的slot,然后将其设置为未使用(in_use[i] = false),随后发送condition 信号(buf_dblwr->s_event)
这里采用顺序遍历,来寻找当前page的slot,是否存在效率问题?
而在5.6.11版本中,只考虑了batch flush操作。

* 移除对dblwr buffer中的sleep,改用condition wait

 写double write buffer时,如果已经在刷dblwr,以前是sleep 10ms,现在改成condition wait了,这里包括batch flush 和single page flush,这两者都增加了条件变量
当脏页刷新非常频繁时,会看到很明显的性能提升
*其他
另外一个没提到的修改是函数buf_flush_LRU_tail
在5.6.11的版本中,并没有对buf_flush_LRU的返回值进行处理。而在5.6.12中,增加了如下逻辑:
2092                 for (ulint j = 0;
2093                      j < scan_depth;
2094                      j += PAGE_CLEANER_LRU_BATCH_CHUNK_SIZE) {
2095 
2096                         ulint   n_flushed = 0;
2097 
2098                         /* Currently page_cleaner is the only thread
2099                         that can trigger an LRU flush. It is possible
2100                         that a batch triggered during last iteration is
2101                         still running, */
2102                         if (buf_flush_LRU(buf_pool,
2103                                           PAGE_CLEANER_LRU_BATCH_CHUNK_SIZE,
2104                                           &n_flushed)) {
2105 
2106                                 /* Allowed only one batch per
2107                                 buffer pool instance. */
2108                                 buf_flush_wait_batch_end(
2109                                         buf_pool, BUF_FLUSH_LRU);
2110                         }
2111 
2112                         if (n_flushed) {
2113                                 total_flushed += n_flushed;
2114                         } else {
2115                                 /* Nothing to flush */
2116                                 break;
2117                         }
2118                 }
3.优化batch flush的效率,之前的时间复杂度为O(N*N)
每个buffer pool实例增加了一个新的变量:
        const buf_page_t*       flush_list_hp;/*!< “hazard pointer”

used during scan of flush_list

while doing flush list batch.

Protected by flush_list_mutex */

根据注释,其在批量刷新时使用,用 flush_list_mutex  来保护
buf_flush_set_hp : 设置 flush_list_hp指针,指向参数传递的page
buf_flush_update_hp:当flush_list上的block移除或者移动时,需要检查buf_flush_set_hp指针是否被其他正在扫描flush list的线程设置,如果flush_list_hp指向我们下一个将要扫描的page,则将其设置为NULL,表示需要重新扫描
有两个地方会调用到这个函数:
* 从flush list上移除一个page的时候(buf_flush_remove)
* 为flush list上的一个page重分配控制块,buf_flush_relocate_on_flush_list
buf_do_flush_list_batch:
在该函数的修改是核心部分,主要消除了在bug#69170中描述的o(n*n)的时间复杂度。所有作用于flush list的线程,都需要先检查flush_list_hp指针,
这里的方法很简单,从flush list的尾部开始扫描, 每次获取一个page后,将bp->flush_list_hp的指针指向该page的前一个, 然后释放bp->flush_list_mutex
然后执行该page的刷新
flushed = buf_flush_page_and_try_neighbors(

bpage, BUF_FLUSH_LIST, min_n, &count);

再次持有flush_list_mutex锁,查看bp->flush_list_hp是否发生变化,如果发生变化了,则表明该指针被其他线程设置了,也就是说,有其他线程对flush list做了操作,因此需要从flush list尾部重新开始扫描
在5.6.11的版本中,总是无条件的从尾部开始重新扫描。

 


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
存储 关系型数据库 MySQL
MySQL InnoDB数据存储结构
MySQL InnoDB数据存储结构
|
1月前
|
存储 缓存 关系型数据库
MySQL的varchar水真的太深了——InnoDB记录存储结构
varchar(M) 能存多少个字符,为什么提示最大16383?innodb怎么知道varchar真正有多长?记录为NULL,innodb如何处理?某个列数据占用的字节数非常多怎么办?影响每行实际可用空间的因素有哪些?本篇围绕innodb默认行格式dynamic来说说原理。
828 6
MySQL的varchar水真的太深了——InnoDB记录存储结构
|
2月前
|
安全 关系型数据库 MySQL
mysql安全性能
mysql安全性能
37 10
|
2月前
|
存储 缓存 关系型数据库
MySQL - 存储引擎MyISAM和Innodb
MySQL - 存储引擎MyISAM和Innodb
|
7天前
|
存储 关系型数据库 MySQL
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
23 0
|
15天前
|
存储 关系型数据库 MySQL
MySQL数据库性能大揭秘:表设计优化的高效策略(优化数据类型、增加冗余字段、拆分表以及使用非空约束)
MySQL数据库性能大揭秘:表设计优化的高效策略(优化数据类型、增加冗余字段、拆分表以及使用非空约束)
|
15天前
|
缓存 关系型数据库 MySQL
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
|
2月前
|
关系型数据库 MySQL 数据库
RDS数据库测评:性能超出预期,双11优惠还在继续
RDS数据库测评:性能超出预期,双11优惠还在继续
30 0
|
10天前
|
关系型数据库 MySQL 数据库
mysql卸载、下载、安装(window版本)
mysql卸载、下载、安装(window版本)
|
1月前
|
关系型数据库 MySQL 数据库连接
关于MySQL-ODBC的zip包安装方法
关于MySQL-ODBC的zip包安装方法