http://mysql.taobao.org/monthly/2016/06/10/

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

使用

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

mysql> show create table sbtest1\G
*************************** 1. row ***************************
Table: sbtest1
Create 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=1000000
1 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: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Select tables optimized away
1 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: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 960984
filtered: 100.00
Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

实现

WL#6742中,为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,再将结果累加起来

相关代码:
commit1
commit2

缺点

由于总是强制使用聚集索引,缺点很明显:当二级索引的大小远小于聚集索引,且数据不在内存中时,使用二级索引显然要快些,因此文件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能够走一个更好的索引来计算行数,优化器应该做出选择,而不是总是无条件选择聚集索引,提了个Bug到官方

其他

WL#6742还提到了一个尚未公布的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

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

MySQL · 引擎特性 · InnoDB COUNT(*) 优化(?)的更多相关文章

  1. MySQL · 引擎特性 · InnoDB 崩溃恢复过程

    MySQL · 引擎特性 · InnoDB 崩溃恢复过程 在前面两期月报中,我们详细介绍了 InnoDB redo log 和 undo log 的相关知识,本文将介绍 InnoDB 在崩溃恢复时的主 ...

  2. MySQL · 引擎特性 · InnoDB 事务子系统介绍

    http://mysql.taobao.org/monthly/2015/12/01/ 前言 在前面几期关于 InnoDB Redo 和 Undo 实现的铺垫后,本节我们从上层的角度来阐述 InnoD ...

  3. MySQL · 引擎特性 · InnoDB index lock前世今生

    http://mysql.taobao.org/monthly/2015/07/05/ MySQL · 引擎特性 · InnoDB index lock前世今生 前言 InnoDB并发过程中使用两类锁 ...

  4. MySQL · 引擎特性 · InnoDB奔溃恢复

    前言 数据库系统与文件系统最大的区别在于数据库能保证操作的原子性,一个操作要么不做要么都做,即使在数据库宕机的情况下,也不会出现操作一半的情况,这个就需要数据库的日志和一套完善的奔溃恢复机制来保证.本 ...

  5. MySQL · 引擎特性 · InnoDB崩溃恢复

    前言 数据库系统与文件系统最大的区别在于数据库能保证操作的原子性,一个操作要么不做要么都做,即使在数据库宕机的情况下,也不会出现操作一半的情况,这个就需要数据库的日志和一套完善的崩溃恢复机制来保证.本 ...

  6. MySQL · 引擎特性 · InnoDB Buffer Pool

    前言 用户对数据库的最基本要求就是能高效的读取和存储数据,但是读写数据都涉及到与低速的设备交互,为了弥补两者之间的速度差异,所有数据库都有缓存池,用来管理相应的数据页,提高数据库的效率,当然也因为引入 ...

  7. MySQL · 引擎特性 · InnoDB 事务系统

    前言 关系型数据库的事务机制因其有原子性,一致性等优秀特性深受开发者喜爱,类似的思想已经被应用到很多其他系统上,例如文件系统等.本文主要介绍InnoDB事务子系统,主要包括,事务的启动,事务的提交,事 ...

  8. MySQL · 引擎特性 · InnoDB 事务锁简介

    https://yq.aliyun.com/articles/4270# zhaiwx_yinfeng 2016-02-02 19:00:43 浏览2194 评论0 mysql innodb lock ...

  9. MySQL · 引擎特性 · InnoDB IO子系统

    前言 InnoDB做为一款成熟的跨平台数据库引擎,其实现了一套高效易用的IO接口,包括同步异步IO,IO合并等.本文简单介绍一下其内部实现,主要的代码集中在os0file.cc这个文件中.本文的分析默 ...

随机推荐

  1. 【LeetCode 160】Intersection of Two Linked Lists

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  2. STL六大组件之——仿函数偷窥

    仿函数(functor),就是使一个类或类模板的使用看上去象一个函数.其实现就是类或类模板中对operator()进行重载,这个类或类模板就有了类似函数的行为.仿函数是智能型函数就好比智能指针的行为像 ...

  3. C语言char[]和char*比较

    先看看一个例子: #include <iostream> using namespace std; main() { char *c1 = "abc"; char c2 ...

  4. mybatis系列-11-一对多查询

    11.1     需求 查询订单及订单明细的信息. 11.2     sql语句 确定主查询表:订单表 确定关联查询表:订单明细表 在一对一查询基础上添加订单明细表关联即可. SELECT order ...

  5. Tkinter教程之Scale篇

    本文转载自:http://blog.csdn.net/jcodeer/article/details/1811313 '''Tkinter教程之Scale篇'''#Scale为输出限定范围的数字区间, ...

  6. [算法] 快速排序 Quick Sort

    快速排序(Quick Sort)使用分治法策略. 它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分:其中一部分的所有数据都比另外一部分的所有数据都要小.然后,再按此方法对这 ...

  7. IIS中使用PUT方法錯誤記錄

    在IIS7.5中使用PUT,DELETE方法時會遇到404,405錯誤,特記錄解決辦法:404: 405: 在web.config的system.webServer節點中加入 <modules ...

  8. 2016 CocosPods安装教程

    CocoaPods简介 CocoaPods是一个管理Swift和Objective-C的Cocoa项目的依赖工具.它现在有超过一万八千多个库,可以优雅地帮助你扩展你的项目.简单的说,就是替你管理Swi ...

  9. (转)UML实践----用例图、类图、对象图、顺序图、协作图、状态图、活动图、组件图、配置图

    面向对象的问题的处理的关键是建模问题.建模可以把在复杂世界的许多重要的细节给抽象出.许多建模工具封装了UML(也就是Unified Modeling Language™),这篇课程的目的是展示出UML ...

  10. javascript实现栈功能