在MySQL的InnoDB存储引擎中count(*)函数的优化
写这篇文章之前已经看过了很多数据库方面的优化内容,大部分都是加索引、使用事务、要什么select什么等等。然而,只是停留在阅读的层面上,很少有实践,因为没有遇到真实的项目,一切都是纸上谈兵。实践是检验真理的唯一标准,于是就想在数据库上测试一些性能优化的方案,比如索引之类的,但是不想使用假的数据,于是就想着能不能抓取网上的一些数据来作分析,后来自己通过PHP抓取了一些数据(查看抓取数据博文),抓了大约110W的用户数据之后,当然需要统计一下具体的数量,于是我使用了以下的SQL语句(我使用的存储引擎是InnoDB):
SELECT COUNT(*) FROM zh_user;
然而,发现需要运行14-20s的时间才能看到结果。
这样的时间开销在真实的环境的用户体验是十分差的,试想一下,打开一个页面还要等接近20s才能看到数据,别说20s,就算是3s也是十分差的,于是便想在这方面做优化。
存储引擎
在MySQL中,日常开发中比较常用的有MyISAM和InnoDB两种存储引擎。两者之间的其中一个区别是使用count(*)函数计算表的具体行数。
因为MyISAM会保存表的具体行数,因此这段代码在MyISAM存储引擎中执行,MyISAM只要简单地读出保存好的行数即可。因此,如果表中没有使用事务之类的操作,这是最好的优化方案。然而,InnoDB存储引擎不会保存表的具体行数,因此,在InnoDB存储引擎中执行这段代码,InnoDB要扫描一遍整个表来计算有多少行。
查询优化命令--Explain
要弄懂查询性能在哪,首先,需要知道导致查询缓慢的瓶颈在哪。explain命令显示的rows是核心的性能指标,rows大,说明mysql需要扫描的行数就多,绝大部分rows大的语句执行一定很快。所以优化语句基本上都是在优化rows。
首先,当前表的结构:
表的当前索引:
再看看Explain的结果:
可以看到,mysql扫描了整个表来执行本次查询。
奇怪的地方
在数据表的设计中,我是添加了唯一索引的,但是后来有一个语句是根据其中一个字段统计数量,当时添加了一个普通的索引,当我再执行了一遍上面的SQL语句,发现只需要0.2-0.3s的时间就能统计出表中的行数。
不禁吓了一跳,误打误撞就发现了优化的方法:在InnoDB中,除了唯一索引之外,在其他字段添加一个普通索引(称为辅助索引)就能够提升count(*)函数的性能。但是这是为什么呢?
加了索引之后的表结构:
当前的索引:
Explain一下:
同样是扫描一样的行数,为什么添加一个普通索引就可以提高这么多的性能?于是便开始查找资料和阅读文档弄懂这个问题。
count(*)函数执行原理
正如在不同的存储引擎中,count(*)函数的执行是不同的。在MyISAM存储引擎中,count(*)函数是直接读取数据表保存的行记录数并返回,而在InnoDB存储引擎中,count(*)函数是先从内存中读取表中的数据到内存缓冲区,然后扫描全表获得行记录数的。在使用count函数中加上where条件时,在两个存储引擎中的效果是一样的,都会扫描全表计算某字段有值项的次数。
索引原理
因为是添加了索引之后才得到性能上的提升,于是便想到从索引的角度来探索。
根据官方文档上的定义:索引是帮助MySQL高效获取数据的数据结构。可以得知,索引的本质就是数据结构,添加索引的目的就是为了提高查询的效率。
使用索引的查询可以类比到字典,如果要查”mysql“这个单词,我们首先会定位到m字母,然后在m字母下面的单词中找y字母,以此类推,直到找到mysql这个单词,就能看到它在第几页,然后就去该页获取该单词更多的信息。想象一下,如果没有索引,那你就要在字典里一页一页的翻阅,效率十分低下。使用索引就是通过这样不断地缩小查询的范围来筛选出最终的结果。
那么在数据库也是一样的,但显然在数据库里使用索引要复杂许多。
磁盘存取与预读
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。那么数据库在构建索引的时候就需要先从磁盘读取数据了,此时就要产生磁盘I/O消耗。而每次的数据读取,都要经历寻道时间、旋转延迟、传输时间三个部分。寻道时间是指磁臂移动到指定磁道所需要的时间,一般在5ms以内;旋转延迟就是磁盘转速;传输时间指的是将数据从磁盘读出并写入到内存的时间,这个时间较短,可以忽略不计。相对于内存存取,I/O存取的消耗要高几个数量级。因此,评价一个数据结构作为索引的优劣最重要的指标就是查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。
从上面的描述可以得知磁盘I/O是非常高昂的操作,根据操作系统的局部性原理:
当一个数据被用到时,其附近的数据也通常会马上被使用。
计算机操作系统在这方面做了一些优化,当一次I/O时,不光把当前磁盘地址的数据读取到内存缓冲区内,而且把相邻的数据也都读取到内存缓冲区内。这样一来,在读取数据时产生的I/O就少了很多了。因为在数据库中,每一次I/O读取的数据我们称之为一页(page),一般为4k或8k,也就是说,我们读取一页内的数据时,实际上才发生了一次I/O。
根据以上的描述,我们可以初步得出结论,增加索引前后的性能差距体现在磁盘读取过程。但是在添加新的索引之前,我是添加了一个唯一索引的,后来发现在mysql中,我添加的唯一索引被称为聚簇索引,而后面添加的索引称为辅助索引,因此,让我们再来看看聚簇索引和辅助索引的区别。
聚簇索引(clustered index)和辅助索引(secondary index)
聚簇索引(clustered index)
每一个InnoDB存储引擎下的表都有一个特殊的索引用来保存每一行的数据,称为聚簇索引。通常情况下,聚簇索引是主键的同义词。在InnoDB中,mysql是这样选择聚簇索引的:
如果表中定义了PRIMARY KEY,那么InnoDB就会使用它作为聚簇索引;
否则,如果没有定义PRIMARY KEY,InnoDB会选择第一个有NOT NULL约束的唯一索引作为PRIMARY KEY,然后InnoDB会使用它作为聚簇索引;
如果表中没有定义PRIMARY KEY或者合适的唯一索引。InnoDB会在一个合成的列中自动生成一个包含行ID的隐含的聚簇索引。这些行使用InnoDB赋予这些表的ID进行排序。行ID是6个字节的字段,且作为新行单一地自增。因此,根据行ID排序的行数据在物理上是根据插入的顺序进行排序。
聚簇索引如何加速查询
因为所有的行数据都跟聚簇索引存放在同一个地方,因此,通过聚簇索引访问数据行会更快。如果表十分大,跟使用不同地方保存数据和索引的存储组织来说,聚簇索引的结构会节省很多的I/O操作。(比如说,MyISAM使用了一个文件来保存数据以及另一个文件保存索引记录)。
辅助索引(secondary index)
除了聚簇索引之外的所有索引都被称为辅助索引。在InnoDB里,辅助索引的每一行记录都包含每一行的主键列,辅助索引指向主键。InnoDB使用这个主键来查找在聚簇索引中的行。如果主键很长,辅助索引会使用更多的空间,因此辅助索引有利于存储引擎拥有长度更短的主键。
结论
在第一次使用了唯一索引(u_id)的时候,InnoDB使用了唯一索引作为表的聚簇索引。而在InnoDB存储引擎中,count(*)函数是先从内存中读取表中的数据到内存缓冲区,然后扫描全表获得行记录数的。因此,使用唯一索引作为聚簇索引的时候,InnoDB需要先读取110W条的数据到数据缓冲区中,这里发生了很多次I/O,因此造成了主要的时间消耗。而添加了辅助索引后,mysql在执行查询时会使用内部的优化机制:即使用辅助索引来统计数量。辅助索引保存的是index的值,此时只需要读取一个字段,I/O减少了,性能就提高了。因此在InnoDB中,如果有统计整张表的数量的需求,可以考虑增加一个辅助索引。
在MySQL的InnoDB存储引擎中count(*)函数的优化的更多相关文章
- MySQL数据库InnoDB存储引擎中的锁机制
MySQL数据库InnoDB存储引擎中的锁机制 http://www.uml.org.cn/sjjm/201205302.asp 00 – 基本概念 当并发事务同时访问一个资源的时候,有可能 ...
- MySQL 温故而知新--Innodb存储引擎中的锁
近期碰到非常多锁问题.所以攻克了后,细致再去阅读了关于锁的书籍,整理例如以下:1,锁的种类 Innodb存储引擎实现了例如以下2种标准的行级锁: ? 共享锁(S lock),同意事务读取一行数据. ? ...
- MySQL数据库InnoDB存储引擎中的锁机制(转载)
http://www.uml.org.cn/sjjm/201205302.asp 00 – 基本概念 当并发事务同时访问一个资源的时候,有可能导致数据不一致.因此需要一种致机制来将访问顺序化. 锁就是 ...
- MySQL数据库InnoDB存储引擎多版本控制(MVCC)实现原理分析
文/何登成 导读: 来自网易研究院的MySQL内核技术研究人何登成,把MySQL数据库InnoDB存储引擎的多版本控制(简称:MVCC)实现原理,做了深入的研究与详细的文字图表分析,方便大家理解I ...
- 一文带你读懂 Mysql 和 InnoDB存储引擎
作为一名开发人员,在日常的工作中会难以避免地接触到数据库,无论是基于文件的 sqlite 还是工程上使用非常广泛的 MySQL.PostgreSQL,但是一直以来也没有对数据库有一个非常清晰并且成体系 ...
- mysql之innodb存储引擎
mysql之innodb存储引擎 innodb和myisam区别 1>.InnoDB支持事物,而MyISAM不支持事物 2>.InnoDB支持行级锁,而MyISAM支持表级锁 3>. ...
- MySQL数据库InnoDB存储引擎
MySQL数据库InnoDB存储引擎Log漫游 http://blog.163.com/zihuan_xuan/blog/static/1287942432012366293667/
- MySQL InnoDB存储引擎中的锁机制
1.隔离级别 Read Uncommited(RU):这种隔离级别下,事务间完全不隔离,会产生脏读,可以读取未提交的记录,实际情况下不会使用. Read Committed (RC):仅能读取到已提交 ...
- MySQL中InnoDB存储引擎中的哈希算法
InnoDB存储引擎使用哈希算法来对字典进行查找,其冲突机制采用链表方式,哈希函数采用除法散列方式.对于缓冲池页的哈希表来说,在缓冲池中的Page页都有一个chain指针.它指向相同哈希函数值的页的. ...
随机推荐
- Enterprise Achitect使用与类的关系的简单介绍
本文作为Enterprise Achitect初步使用,另外也是类图基本介绍,是设计模式的基础. 类的关系有泛化(Generalization).实现(Realization).依赖(Depende ...
- sql server 锁
锁模式 锁模式 说明 共享 (S) 用于不更改或不更新数据的读取操作,如 SELECT 语句. 更新 (U) 用于可更新的资源中. 防止当多个会话在读取.锁定以及随后可能进行的资源更新时发生常见形式 ...
- 给numpy矩阵添加一列
问题的定义: 首先我们有一个数据是一个mn的numpy矩阵现在我们希望能够进行给他加上一列变成一个m(n+1)的矩阵 import numpy as np a = np.array([[1,2,3], ...
- DTMF三种模式(SIPINFO,RFC2833,INBAND)
转自:http://www.tuicool.com/articles/n6Vb2iJ 1.DTMF(双音多频)定义:由高频音和低频音的两个正弦波合成表示数字按键(0~9 * # A B C D). 2 ...
- Stack Overflow: The Architecture - 2016 Edition(Translation)
原文: https://nickcraver.com/blog/2016/02/17/stack-overflow-the-architecture-2016-edition/ 作者:Nick Cra ...
- D3中selection之使用
1. 极为重要的reference: [1] How selections works. http://bost.ocks.org/mike/selection/ [2] Nested selecti ...
- React虚拟DOM浅析
在Web开发中,需要将数据的变化实时反映到UI上,这时就需要对DOM进行操作,但是复杂或频繁的DOM操作通常是性能瓶颈产生的原因,为此,React引入了虚拟DOM(Virtual DOM)的机制. 什 ...
- js学习笔记之标准库
在全局函数中,this等于window 在函数被作为某个对象的方法调用时,this等于那个对象. 数组的函数: 检测:Array.isArray() 转换:toString(),toLocalStr ...
- bootstrap之移动支持
为了让 Bootstrap 开发的网站对移动设备友好,确保适当的绘制和触屏缩放,需要在网页的 head 之中添加 viewport meta 标签,如下所示: <meta name=" ...
- NotificationCenter接收不到通知的原因
今天在项目中遇到一个奇葩的事情,我在一个类中明明写了: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #00af ...