博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MySQL · 引擎特性 · InnoDB COUNT(*) 优化(?)
阅读量:5989 次
发布时间:2019-06-20

本文共 2932 字,大约阅读时间需要 9 分钟。

在5.7版本中,InnoDB实现了新的handler的records接口函数,当你需要表上的精确记录个数时,会直接调用该函数进行计算。

使用

实际上records接口函数是在优化阶段调用的,在满足一定条件时,直接去计算行级计数。其explain出来的结果相比老版本也有所不同,这里我们使用sysbench的sbtest表来进行测试,共200万行数据。

mysql> show create table sbtest1\G*************************** 1. row ***************************       Table: sbtest1Create Table: CREATE TABLE `sbtest1` (  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,  `k` int(10) unsigned NOT NULL DEFAULT '0',  `c` char(120) NOT NULL DEFAULT '',  `pad` char(60) NOT NULL DEFAULT '',  PRIMARY KEY (`id`),  KEY `k_1` (`k`)) ENGINE=InnoDB AUTO_INCREMENT=2000001 DEFAULT CHARSET=utf8 MAX_ROWS=10000001 row in set (0.00 sec)mysql> explain select count(*) from sbtest1\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: NULL   partitions: NULL         type: NULLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: NULL     filtered: NULL        Extra: Select tables optimized away1 row in set, 1 warning (0.00 sec)

注意这里Extra里为”Select tables optimized away”,表示在优化器阶段已经被优化掉了。如果给id列带上条件的话,则回退到之前的逻辑

mysql> explain select count(*) from sbtest1 where id > 0\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sbtest1   partitions: NULL         type: rangepossible_keys: PRIMARY          key: PRIMARY      key_len: 4          ref: NULL         rows: 960984     filtered: 100.00        Extra: Using where; Using index1 row in set, 1 warning (0.00 sec)

实现

在中,为InnoDB实现了handler的records函数接口

函数栈

opt_sum_query|--> get_exact_record_count	|--> ha_records		|--> ha_innobase::records			|-->row_scan_index_for_mysql
  • HA_HAS_RECORDS:引擎flag,表示是否可以把count(*)下推到引擎层
  • 总是使用聚集索引来进行计算行数
  • 只需要读取主键值,无需去读取外部存储列(row_prebuilt_t::read_just_key),如果行记录较大的话,就可以节省客观的诸如内存拷贝之类的操作开销
  • 计算过程可中断,每检索1000条记录,检查事务是否被中断
  • 由于只有一次引擎层的调用,减少了Server层和InnoDB的交互,避免了无谓的内存操作或格式转换
  • 对于分区表,在5.7版本已经下推到innodb层,因此分区表的计算方式(ha_innopart::records)是针对每个分区调用ha_innobase::records,再将结果累加起来

相关代码:

缺点

由于总是强制使用聚集索引,缺点很明显:当二级索引的大小远小于聚集索引,且数据不在内存中时,使用二级索引显然要快些,因此文件IO更少。如下例:

默认情况下检索所有行(以下测试都是在清空buffer pool时进行的):

mysql> select count(*) from sbtest1;+----------+| count(*) |+----------+|  2000000 |+----------+1 row in set (3.92 sec)

即时强制指定索引也没用 :(

mysql> select count(*) from sbtest1 force index(k_1);+----------+| count(*) |+----------+|  2000000 |+----------+1 row in set (3.86 sec)

但如果带上一个简单的条件,让select count(*)走索引k_1,耗费的时间立马下降了….

mysql> select count(*) from sbtest1 where k > 0;+----------+| count(*) |+----------+|  2000000 |+----------+1 row in set (1.05 sec)

个人认为这算是一个性能退化,退一步讲,如果用户知道force index能够走一个更好的索引来计算行数,优化器应该做出选择,而不是总是无条件选择聚集索引,提了个

其他

从还提到了一个尚未公布的WL#6605,从其只言片语中可以推断官方有意向实现即时获得行数:

The next worklog, WL#6605, is intended to return the COUNT(*) through this handler::records() interface almost immediately in all conditions just by keeping track if the base committed count along with transaction deltas

让我们继续对新版本保持期待吧 :)

转载地址:http://bnilx.baihongyu.com/

你可能感兴趣的文章
html5的 setCustomValidity
查看>>
JAVA打开指定网页
查看>>
maven手动添加sqlserver驱动包
查看>>
常用的JAVA正则表达式
查看>>
Maven pom 配置
查看>>
守护进程
查看>>
linux dhcp 获取ip地址能上网,设置静态ip地址则不能上网
查看>>
sha256 加密
查看>>
Javascript中的重载的实现以及方法apply和call
查看>>
简单遮罩层的实现
查看>>
从大数据谈起1:OLTP和OLAP的设计区别
查看>>
<分手合约>-观后感
查看>>
linux下touch命令修改文件时间戳
查看>>
golang进程安全退出
查看>>
我的友情链接
查看>>
ESXi 本地升级 ESXi 5.5 update
查看>>
安装ipython
查看>>
10.1 modules
查看>>
业务软件的本质,超越CRUD
查看>>
webpack 简单命令
查看>>