一个例子与InnoDB索引的几个概念

1、一个简单的sql语句问题

    假设当前我们有一个表记录用户信息,结构如下:

    a)      表结构

CREATE TABLE `u` (

  `id` int(11) NOT NULL DEFAULT ‘0′,

  `regdate` int(1) unsigned,

  …..

  PRIMARY KEY (`id`),

  KEY `regdate` (`regdate`)

) ENGINE=InnoDB DEFAULT CHARSET=gbk

说明:1) 由于需要按照注册时间单独查询,建了一个regdate的索引

            2) 其他信息未列出, 一行长度100字节左右,表行数百万级。 

b)      需求:需要一个语句查出表中id为10000整数倍的记录总数。

 

 

2、常规答案

    一个正常想到的语句是 select sum(id % 10000 = 0) from u; —— (SQL1)

    我们来看这个语句的执行流程:

a)      遍历所有数据,取出id字段

b)      计算id%10000=0的值并通过sum累计。

           在构造的环境中这个语句的执行时间为2.6s.

 

 

3、查的多,查得快

    假设我们同时要查出注册时间在2007年之前的用户总数,我们自然得到这个语句

     select sum(id % 10000 = 0), sum(regdate<1167667200) from sbtest;—-(SQL2)

    执行结果发现这个语句执行时间约0.5s 。 这个语句查的数据结果比SQL1多,但执行时间却降为1/5.

 

 

4、分析 

    可以直接从执行期间的磁盘参数,或者在os/os0file.c中将程序读取的数据量输出结果查看,直观结果是SQL1读取了更多的磁盘数据。

 

问题1:在SQL1执行过程中,遍历所有数据,InnoDB只从磁盘读取了id这个字段,还是全部读入?

    实际上由于id是聚簇索引,并没有一个单独的索引树存id,因此在磁盘上,id索引树的叶节点上就是数据。 InnoDB以page为单位读取,在取id的过程中,必须将所有的数据读入。

    于是我们发现,在SQL1中,我们只需要id字段,而每行额外读入了几百字节的数据。

 

问题2:SQL2避免了读全数据?

    确实如此。

    我们对比两个语句的explain结果, 发现仅有的不同是选用的key结果不同。

SQL1 SQL2
key: PRIMARY key: regdate

    由于regdate是非聚簇(secondary index)索引,单独存于另一棵树。 我们知道使用非聚簇索引时,需要读行数据的时候,需要再到聚簇索引中取得。显然SQL2不会再读一遍全数据(否则性能必然低于SQL1)。

    而其原因是覆盖索引(covering index)。 非聚簇索引的叶节点上是聚簇索引的字段值,需要取数据时,根据这个值再去聚簇索引上取。而这时InnoDB变“聪明”了, 需要取的值只是id,而id作为聚簇索引的key信息,已经得到,不需要再到聚簇索引中读取数据。

    由于regdate索引树上只有regdate和主键(id)的信息,因此数据量远小于全表数据,因此SQL2的读盘量小于SQL1,执行速度快。

 

5、其他 

    这个例子涉及到几个概念, 聚簇索引(cluster index)、非聚簇索引(secondary index), 覆盖索引(covering index),还有磁盘的数据存放。都算是一些基本的内容,却是平时见到的一些优化的理论基础。举几个例子如下:

1)      我们经常被告诫select之后只填最必须的字段

    其中的一个原因是减少网络传输。但不一定能够提升服务器执行性能。比如例子中的表,select  * from u where id = n; 与select user_name from u where id =n一样。

    当然有些时候效果会很理想,比如 select id from u where regdate=xxx 就比select * from u where regdate=xxx快很多,原因已说明。

2)      查询符合条件的第10w个记录开始的10个记录。

    这个例子在其他博文上被多次提及,

select * from t order by a limit 100000, 10; 可以改进为

select * from t where a>=(select a from t order by a limit 100000,1) limit 10;

    在笔者环境中性能提升约1000倍。

    原因即在于, 改进语句中,子查询中的排序只在非聚餐索引a上执行,由于覆盖索引,排序过程不需要访问聚簇索引。实际读读取全数据的只有10条记录,而原语句则需要读所有记录的全数据。

    当然执行排序的过程消耗是一样的。 

 

6、结束

    回到开头,如果只需要查id满足特定条件的记录总数,可以使用select sum(id % 10000 = 0) from u force index (`regdate`);  

    把sum(id %10000=0)换成其他操作对执行效率均没有影响。 

    但若查询内容中出现除id和regdate外的其他字段,则force index优化无效,可自行分析。

时间: 2024-08-22 14:44:47

一个例子与InnoDB索引的几个概念的相关文章

MySQL数据库InnoDB索引原理详解实践教程【图】

InnoDB是Mysql的默认存储引擎(Mysql5.5.5之前是MyISAM,文档).本着高效学习的目的,本篇以介绍InnoDB为主,少量涉及MyISAM作为对比. 这篇文章是我在学习过程中总结完成的,内容主要来自书本和博客(参考文献会给出),过程中加入了一些自己的理解,描述不准确的地方烦请指出. 1 各种树形结构 本来不打算从二叉搜索树开始,因为网上已经有太多相关文章,但是考虑到清晰的图示对理解问题有很大帮助,也为了保证文章完整性,最后还是加上了这部分. 先看看几种树形结构: 1 搜索二叉树

《深入理解Elasticsearch(原书第2版)》一2.1.4 一个例子

2.1.4 一个例子 现在,我们已经了解评分的工作原理.接下来我们看一个在现实生活中应用评分的简单例子.首先我们需要创建一个名为scoring的新索引.使用如下命令创建这个索引: 简单起见,我们使用了只有一个物理分片和0个副本的索引(我们不需要在这个例子中关心分布式文档频率).我们需要索引一个简单的文档,代码如下: 接着我们执行一个简单的匹配(match)查询,查询的词项是"document". Elasticsearch返回的结果如下: 显然,刚才索引的这个文档被匹配上了,并且被赋予

怎样用一个例子讲解StarUML中的用例图、类图、时序图 ?

问题描述 怎样用一个例子讲解StarUML中的用例图.类图.时序图 ? 老师让我讲解StarUTML中的用例图.类图.时序图 , 我不想让老师失望 , 求解啊 请大家能给我一个简单例子 谢谢了

给你一个例子:FileFilter接口的使用。

原问题:怎么检查服务器的c:\是否有test.jpg这个文件(不知道扩展名)===================首先,定义一个FileFilter的实例    private static FileFilter fileFilter=new FileFilter(){        public boolean accept(File pathname) {            String tmp=pathname.getName().toLowerCase();            i

一个例子

一个例子这一章,我们要把我们已学的知识集合起来.具体来讲,我们来写一个使用ODBC APIs的程序.为简单起见,这个程序中我使用Microsoft的Access数据库(Microsoft Access 97) . 下载例子源程序. 注意:如果你使用的windows.inc 是1.18及其以下版本,在开始编译之前要修改其中的一个小bug.在windows.inc中查找 "SQL_NULL_HANDLE",将得到下面这行: SQL_NULL_HANDLE equ 0L 将0后面的"

Spring中基于aop命名空间的AOP 一(一点准备工作和一个例子)

在某些时候,我们工程中使用的JDK 不一定就是1.5 以上,也就是说可能不支持Annotation 注解,这时自然也就不能使用@AspectJ 注解驱动的AOP 了,那么如果我们仍然想使用AspectJ 灵活的切入点表达式,那么该如何呢?Spring 为我们提供了基于xml schematic 的aop 命名空间,它的使用方式和@AspectJ 注解类似,不同的是配置信息从注解中转移到了Spring 配置文件中.在这里,我们将详细介绍如何使用Spring 提供的<aop:config/> 标签

android应用开发详解里的一个例子

问题描述 android应用开发详解里的一个例子 我导入的书上例子可以运行,照着书打进去的代码就不能运行,同样从例子里面复制过来的代码也不能运行,MainActivity可以显示,但是点击register后跳转的ResultActivity就不能显示,模拟器弹出has stopped字样,求解答 以下是代码 package com.amaker.test; import android.app.Activity; import android.content.Intent; import and

求一个例子:java socket 采用ObjectInputStream序列化收发文件例子

问题描述 求一个例子:java socket 采用ObjectInputStream序列化收发文件例子 求一个:java socket 采用ObjectInputStream序列化收发文件例子求一个:java socket 采用ObjectInputStream序列化收发文件例子 解决方案 我参考 这个 例子 解决我的问题 http://bbs.csdn.net/topics/200033850 解决方案二: http://www.cnblogs.com/feiyun126/p/3921466.

编程c语言-C语言大型程序一个例子为什么不能运行

问题描述 C语言大型程序一个例子为什么不能运行 我编写了两张图里四个文件,一个.h和三个.c,然后按照给出的那个命令运行,却出现一堆错误和warning,怎么回事?一般大点的程序按这种编写方法怎么链接和用什么命令运行? 解决方案 warning不影响运行,没有error就行 解决方案二: 有时候编译器的版本不一样也会出错的,也有课能本上给出的代码是伪代码 解决方案三: C语言实现的一个程序只能运行一次,不能重复运行 解决方案四: 这是用gcc运行是的错误 解决方案五: 要用gcc -o命令先编译