MySQL索引及查询优化
mysql 索引
1.索引介绍
索引按数据结构分可分为哈希表,有序数组,搜索树,跳表:
哈希表适用于只有等值查询的场景
有序数组适用于有等值查询和范围查询的场景,但有序数组索引的更新代价很大,所以最好用于静态数据表
搜索树的搜索效率稳定,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高
跳表可以理解为优化的哈希索引
innodb 使用了 B+ 树索引模型,而且是多叉树。虽然二叉树是索引效率最高的,但是索引需要写入磁盘,如果使用二叉树磁盘 io 会变得很频繁。在 innodb 索引中分为主键索引(聚簇索引也叫聚集索引)和非主键索引(二级索引也叫辅助索引)。主键索引保存了该行数据的全部信息,二级索引保存了该行数据的主键;所以使用二级索引的时候会先查出主键值,然后回表查询出数据,而使用主键索引则不需要回表。
对二级索引而言可使用覆盖索引来优化 sql,看下面两条 sql
select * from table where key=1;
select id from table where key=1;
key 是一个二级索引,第一条 sql 是先查询出 id ,然后根据 id 回表查询出真正的数据。而第二条查询索引后直接返回数据不需要回表。第二条 sql 索引 key 覆盖了我们的查询需求,称作覆盖索引
2. 普通索引和唯一索引
innoDB 是按数据页来读写数据的,当要读取一条数据的时候是先将本页数据全部读入内存,然后找到对应数据,而不是直接读取,每页数据的默认大小为 16KB。
当一个数据页需要更新的时候,如果内存中有该数据页就直接更新,如果没有该数据页则在不影响数据一致性的前提下将;更新操作先缓存到 change buffer
中,在下次查询需要访问这个数据页的时候再写入更新操作除了查询会将 change buffer
写入磁盘,后台线程线程也会定期将 change buffer
写入到磁盘中。对于唯一索引来说所有的更新操作都要先判断这个操作是否会违反唯一性约束,因此唯一索引的更新无法使用 change buffer
而普通索引可以,唯一索引更新比普通索引更新多一个唯一性校验的过程。
3.联合索引
两个或更多个列上的索引被称作联合索引(复合索引)。联合索引可减少索引开销,以联合索引 (a,b,c) 为例,建立这样的索引相当于建立了索引 a、ab、abc 三个索引—— Mysql 从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分,而且当最左侧字段是常量引用时,索引就十分有效,这就是最左前缀原则。由最左前缀原则可知,组合索引是有顺序的,那么哪个索引放在前面就比较有讲究了。对于组合索引还有一个知识点——索引下推,假设有组合索引(a,b,c)有如下 sql:
selet * from table where a=xxx and b=xxx
这个 sql 会进行两次筛选第一次查出 a=xxx
数据 再从 a=xxx
中查出 b=xxx
的数据。使用索引下推和不使用索引下推的区别在于不使用索引下推会先查出 a=xxx
数据的主键然后根据查询出的主键回表查询出全行数据,再在全行数据上查出 b=xxx
的数据;而索引下推的执行过程是先查出 a=xxx
数据的主键,然后在这些主键上二次查询 b=xxx
的主键,然后回表。
索引下推的特点:
innodb 引擎的表,索引下推只能用于二级索引
索引下推一般可用于所查询字段不全是联合索引的字段,查询条件为多条件查询且查询条件子句字段全是联合索引。
4 优化器与索引
在索引建立之后,一条语句可能会命中多个索引,这时,就会交由优化器来选择合适的索引。优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句。那么优化器是怎么去确定索引的呢?优化器会优先选择扫描行数最少的索引,同时还会结合是否使用临时表、是否排序等因素进行综合判断。MySQL 在开始执行 sql 之前,并不知道满足这个条件的记录有多少条,而只能根据 mysql 的统计信息来估计,而统计信息是通过数据采样得出来的。
5 其他索引知识点
有时候需要索引很长的字符列,这会让索引变得很大很慢还占内存。通常可以以开始的部分字符作为索引,这就是前缀索引。这样可以大大节约索引空间,从而提高索引效率,但这样也会降低索引的选择性。
脏页对数据库的影响:
当内存数据页和磁盘的数据不一致的时候我们称这个内存页为脏页,内存数据写入磁盘后数据一致,称为干净页。当要读入数据而数据库没有内存的时候,这个时候需要淘汰内存中的数据页——干净页可以直接淘汰掉,而脏页需要先刷入磁盘再淘汰。如果一个查询要淘汰的脏页太多会导致查询的时间变长。为了减少脏页对数据库性能影响,innodb 会控制脏页的比例和脏页刷新时机。
索引
MySQL索引使用的数据结构主要有BTree索引 和 哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
MySQL的BTree索引使用的是B数中的B+Tree,但对于主要的两种存储引擎的实现方式是不同的。
MyISAM: B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。
InnoDB: 其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。
mysql 语法分析及优化
1 count(*)
count(*)
对 innodb 而言,它需要把数据从磁盘中读取出来然后累计计数;而 MyISAM 引擎把一个表的总行数存在了磁盘上,所以执行 count(*)
会直接返回这个数,如果有 where 条件则和 innodb一样。那么如何优化 count(*)
?一个思路是使用缓存,但是需要注意双写一致的问题(双写一致性后文缓存章节会做介绍)。还可以专门设计一张表用以存储 count(*)
。
对于 count(主键 id )来说,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。对于 count(1) 来说,InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1” 进去,判断是不可能为空的,按行累加。单看这两个用法的差别的话,你能对比出来,count(1) 执行得要比 count(主键 id)快。因为从引擎 返回 id 会涉及到解析数据行,以及拷贝字段值的操作。对于 count(字段)来说:如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再 判断一下,不是 null 才累加。而对于 count(*) 来说,并不会把全部字段取出来,而是专门做了优化,不取值,按行累加。所以排序效率:
count(*)=count(1)>count(id)>count(字段)
2 order by
Mysql 会给每个线程分配一块内存用于做排序处理,称为 sort_buffer
,一个包含排序的 sql 执行过程为:申请排序内存 sort_buffer
,然后一条条查询出整行数据,然后将需要的字段数据放入到排序内存中,染回对排序内存中的数据做一个快速排序,然后返回到客户端。当数据量过大,排序内存盛不下的时候就会利用磁盘临时文件来辅助排序。当我们排序内存盛不下数据的时候,mysql 会使用 rowid
排序来优化。rowid 排序相对于全字段排序,不会把所有字段都放入 sort_buffer,所以在 sort buffer 中进行排序之后还得回表查询。在少数情况下,可以使用联合索引+索引覆盖的方式来优化 order by。
3 join
在了解 join
之前我们应该先了解驱动表这个概念——当两表发生关联的时候就会有驱动表和被驱动表之分,驱动表也叫外表(R 表),被驱动表也叫做内表(S 表)。一般我们将小表当做驱动表(指定了联接条件时,满足查询条件的记录行数少的表为「驱动表」,未指定联接条件时,行数少的表为「驱动表」;MySQL 内部优化器也是这么做的)。
假设有这样一句 sql(xxx 为索引):
select * from table1 left join tablet2 on table1.xxx=table2.xxx
这条语句执行过程是先遍历表 table1 ,然后根据从表 table1 中取出的每行数据中的 xxx 值,去表 table2 中查找满足条件的 记录。这个过程就跟我们写程序时的嵌套查询类似,并且能够用上被驱动表的索引,这种查询方式叫 NLJ
。当 xxx 不是索引的时候,再使用 NLJ
的话就会对 table2 做多次的全表扫描(每从 table1 取一条数据就全表扫描一次 table2),扫描数暴涨。这个时候 mysql 会采用另外一个查询策略。Mysql 会先把 table1 的数据读入到一个 join_buffer
的内存空间里面去,然后依次取出 table2 的每一行数据,跟 join_buffer
中的数据做对比,满足 join 条件的作为结果集的一部分返回。
我们在使用 join
的时候,要遵循以下几点:
小表驱动大表。
被驱动表走索引的情况下(走
NLJ
查询方式)的时候才考虑用join
4 sql 的优化
1) 在 mysql 中,如果对字段做了函数计算,就用不上索引了。
如以下 sql(data 为索引):
select * from tradelog where month(data)=1;
优化器对这样的 sql 会放弃走搜索树,因为它无法知道 data 的区间。
2)隐式的类型转换会导致索引失效。
如以下 sql:
select * from table where xxx=110717;
其中 xxx 为 varchar
型,在 mysql 中,字符串和数字做比较的话,将字符串转换成数字再进行比较,这里相当于使用了 CAST(xxx ASsigned)
导致无法走索引。
3)索引列参与了计算不会走索引
4)like %xxx 不会走索引,like xxx% 会走索引
5)在 where 子句中使用 or ,在 innodb 中不会走索引,而 MyISAM 会。
MySQL索引及查询优化的更多相关文章
- 【转】MySQL索引和查询优化
原文链接:http://www.cnblogs.com/mailingfeng/archive/2012/09/26/2704344.html 对于任何DBMS,索引都是进行优化的最主要的因素.对于少 ...
- MySQL索引及查询优化总结
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:谢庆玲 文章<MySQL查询分析>讲述了使用MySQL慢查询和explain命令来定位mys ...
- MySQL索引和查询优化
对于任何DBMS,索引都是进行优化的最主要的因素.对于少量的数据,没有合适的索引影响不是很大,但是,当随着数据量的增加,性能会急剧下降. 如果对多列进行索引(组合索引),列的顺序非常重要,MySQL仅 ...
- MySQL索引及查询优化总结 专题
小结:db名与应用名相同,表名:业务名_此表的作用 ,表名表示内容,不体现数量,如果表示boolean概念,表名需要使用is_业务含义来表示,但POJO中不应该出现isXXX,因为不方便序列化,中间的 ...
- MySQL 索引及查询优化总结
本文由云+社区发表 文章<MySQL查询分析>讲述了使用MySQL慢查询和explain命令来定位mysql性能瓶颈的方法,定位出性能瓶颈的sql语句后,则需要对低效的sql语句进行优化. ...
- mysql 索引和查询优化
对于任何DBMS,索引都是进行优化的最主要的因素.对于少量的数据,没有合适的索引影响不是很大,但是,当随着数据量的增加,性能会急剧下降.如果对多列进行索引(组合索引),列的顺序非常重要,MySQL仅能 ...
- MySQL 索引与查询优化
本文介绍一些优化 MySQL 索引设计和查询的建议.在进行优化工作前,请务必了解MySQL EXPLAIN命令: 查看执行计划 索引 索引在逻辑上是指从索引列(关键字)到数据的映射,通过索引可以快速的 ...
- mysql 索引、查询优化
查询计划Explain mysql查询过程中,如若想了解当前sql的执行计划,可以通过explain your_sql的方式查看,具体可以参考mysql官方解释:https://dev.mysql.c ...
- 开发人员不得不知的MySQL索引和查询优化
转载:https://blog.csdn.net/enmotech/article/details/88809822 本文主要总结了慢查询优化的过程中常用的以及不合理的操作,适合有 MySQL 基础的 ...
随机推荐
- 前端开发--ajax
使用ajax,他是有两个模块的,一个是客户端,一个是服务端. 客户端负责发送数据,发送数据的方式有两种,一种是GET,另一种是POST. 服务端是用来接收,处理数据和发送请求的数据. 要想使用ajax ...
- sql语句-------重复时插入更新
ON DUPLICATE KEY UPDATE重复时插入更新 insert into user(id,username) value('231',"二人") on duplicat ...
- 使用nodejs + wecharty打造你的个人微信机器人
开源地址:https://github.com/isnl/wechat-robot 注: 从2017年6月下旬开始,使用基于web版微信接入方案存在大概率的被限制登陆的可能性. 主要表现为:无法登陆W ...
- 基于规则的分类——RIPPER算法
在<分类:基于规则的分类技术>中已经比较详细的介绍了基于规则的分类方法,RIPPER算法则是其中一种具体构造基于规则的分类器的方法.在RIPPER算法中,有几个点是算法的重要构成部分,需要 ...
- 一文揭秘测试平台中是如何将测试用例一键转化Jmeter压测脚本
接上篇,一键转化将接口测试平台测试用例转化成Jmeter压测脚本思路,这里我首先在java 上面做了一个简单的实验,看看 转化的中间遇到的问题,这里呢,我只是给了一个简单的demo 版本, ...
- 数学--数论--HDU 5019 revenge of GCD
Revenge of GCD Problem Description In mathematics, the greatest common divisor (gcd), also known as ...
- Java——多线程锁的那些事
引入 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率. 下面先带大家来总体预览一下锁的分类图 1.乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了 ...
- G - Island Transport 网络流
题目: In the vast waters far far away, there are many islands. People are living on the islands, and a ...
- Re模块的 三个方法
import re s1 = '绿茶白茶黄茶青茶红茶黑茶' s2 = '中国绿茶白茶黄茶青茶红茶黑茶' ret = re.findall(".茶", s1) print(ret) ...
- spring学习笔记(七)HttpMessageConverter
spring学习笔记(七)HttpMessageConverter 1. HttpMessageConverter的加载 2. 从StringMessageConverter探究消息转换器的原理 1. ...