mysql用了索引为什么还是查询很慢?

前言

近几个月在华为实习的过程中,有个场景下取数,对于一个sn号的查询竟然长达几分钟的时间,批量查几十个sn时,则需要一二十分钟的sql查询时间,故专门对这个问题进行一些整理和思考。
首先,这个数据库是存在HIVE上,经过和一些前辈交流后,了解到企业中的数据库主要分为两大种:OLTP(on-line transaction processing)事务型、OLAP(On-Line Analytical Processing)分析型。

  • OLTP是传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,例如银行交易。
  • OLAP是数据仓库系统的主要应用,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。
  1. 那么,事务型数据库OLTP主要以小的事务以及小的查询为主,评估其系统的时候,一般看其每秒执行的Transaction以及Execute SQL的数量。在这样的系统中,单个数据库每秒处理的Transaction往往超过几百个,或者是几千个,Select 语句的执行量每秒几千甚至几万个。OLTP系统最容易出现瓶颈的地方就是CPU与磁盘子系统。
  2. OLAP,也叫联机分析处理(Online Analytical Processing)系统,有的时候也叫DSS决策支持系统,就是我们说的数据仓库。在这样的系统中,语句的执行量不是考核标准,因为一条语句的执行时间可能会非常长,读取的数据也非常多。所以,在这样的系统中,考核的标准往往是磁盘子系统的吞吐量(带宽),如能达到多少MB/s的流量。磁盘子系统的吞吐量则往往取决于磁盘的个数,这个时候,Cache基本是没有效果的,数据库的读写类型基本上是db file scattered read与direct path read/write。应尽量采用个数比较多的磁盘以及比较大的带宽,如4Gb的光纤接口。
    在OLAP系统中,常使用分区技术、并行技术。

查询速度影响因素

回到正题,为什么我用了索引,查询速度还是很慢?到底会出现什么问题,以及如何去解决?本文来探究一二。

先给出结论:MySQL用到了索引和执行时间的长短没有必然关系,确定查询执行效率的是 “扫描行数”与“回表次数”。

  • 扫描行数
  • 回表次数

在实际的sql优化过程中,也是尽量去优化这两块影响因素。

举例

举例说明:

CREATE TABLE t
id int(11)NOT NULL
a varchar(64)DEFAULT NULL,
b int(11)DEFAULT NULL,
PRIMARY KEY (id)
KEY a(a) ENGINE-InnoDB

Query OK, O rows affected(0.02 sec)

先建一个表,InnoDB必须有一个主键索引,这里给id为主键,其次有a、b两个字段,再给a字段(姓名字段)一个普通索引,目前就有了主键索引和a索引树结构,如下图所示:

mysql用了索引为什么还是查询很慢?_第1张图片

这里再回顾一下,InnoDB默认有主键索引,存储的是主键id1、2、3…,它是采用B+ tree的聚簇索引,所有的数据都存放在叶子结点中,而普通索引a的数据只包含对应的id,没有整行数据,所以如果通过普通索引找到了某个id,需要select别的字段,则还需要回表找主键索引中的整行数据取值!

慢查询:
sq中慢查询阈值为ong_ query time=10s
当sq执行后大于10s就会被记录到慢sq志
般建议缩小到1s,一般来说当并发系统中某一条sql查询时间大于1s了,对整体系统的性能影响就会比较大了。

情况1:

select * from t #全表扫描
在这里插入图片描述

其中,type=ALL表示扫描所有行,速度是最慢的,从头到尾扫描。

情况2:

select * from where id = 2 #主键索引快速过滤
在这里插入图片描述
根据主键id索引进行查询,这时采用了key也就是索引,速度是最快的,也不用回表

情况3:

select a from t #索引覆盖,无需回表
在这里插入图片描述
采用了索引覆盖,查询速度也是很快的,因为也不用回表,采用了索引为a,取的也是a字段,也是一步到位。

在MySQL大分页的过程中也用到了索引覆盖的方式进行了优化

情况4:

select * from t where id>0 #基于id扫描全表,且产生大量回表,速度慢
在这里插入图片描述

用*或者多个字段可能要全表扫描。
一般来说查询的数据量要是全表的25%以内,数据库才会判断有走索引的价值,这里不能光看key中有primary就认为走了索引就很快,而还要看row中扫描了多少行,这种情况下row中的行数就等于表的总行数,查询的速度并不快,是全表扫描!

情况5:

create index t on(a, b)
假设t表有1亿人,其中600万姓“张”

select* from t where a=张三 and b=23
索引选择性就很好

select from t where a like 5k%' and b =23
索引选择性较差,但仍会用到索引

作为联合索引,根据前面博客提到的最左前缀法则,我们必须在where中按照联合索引的顺序来查,在左索引基础上查右索引,所以第一句sql是比较快的,直接可以在联合索引树中找到张三这个人;而第二句由于使用了模糊查询,如果姓张的人超过了总数的10%、15%,MySQL就会认为这样的选择性太差,就不会给使用索引了!(没有使用索引的价值!)而转去走全表扫描了。这里的设计是1亿人中有600万人姓“张”,所以虽然索引选择性相对较差,但是MySQL认为还是可以用索引。
MySQL5.6之后做的优化:index condition pushdown,优化了回表数量级

  • mysq5.6以前,基于左侧列对600万张回表筛选23岁数据,速度比较慢
  • mysq5.6以后,基于联合索引左侧列a筛选“张"再去筛选“23岁”,再讲符
    合条件的id回表提取,回表数量大幅度减少
    这种新特性称为" index condition pushdown”

几种调优方式:

  1. 增加多种不同规格索引提高索引选择性(5~6组)
  2. 空间换时间,定时任务增加时报、日报、约报等中间结果
  3. 边缘运算数据压缩:物联网惯用伎俩
    4硬件调优:增大 innodb buffer pool多利用内存,减少硬盘回表

解释:
2.每一个小时 每一分钟去做计算,做汇总,而不是全量汇总,这样数据量就小很多了
3.边缘运算:比如几百万的基站给服务器去每秒上传数据,那么服务器的压力肯定是很大的,如果过往一个小时内数据没有任何异常,则可以用一个表达式把一个小时的数据状态记录压缩成一条记录,实现数据的压缩,再向上上报一次,压力大大降低
4.从硬件角度优化,增大内存!

你可能感兴趣的:(mysql,数据库,database)