MySQL如何创建一个好索引?创建索引的5条建议【宇哥带你玩转MySQL 索引篇(三)】
MySQL如何创建一个好索引?创建索引的5条建议
过滤效率高的放前面
对于一个多列索引,它的存储顺序是先按第一列进行比较,然后是第二列,第三列...这样。查询时,如果第一列能够排除的越多,那么后面列需要判断的行数就越少,效率越高。
关于如何判断哪个列的过滤效率更高,可以通过选择性计算来决定。例如我们要在books表创建一个name列和author列的索引,可以计算这两列各自的选择性:
select count(distinct name) / count(*) as name, count(distinct author) / count(*) as author from books;
最后得出结果如下:
|
Name |
author |
|
0.95 |
0.9 |
显然name字段的选择性更高,那么如果把name放第一列,在name条件过滤时就可以排除更多的列,减少接下来 author的过滤。
使用频率高的放前面
其实该建议比上一个建议优先级更高
例如一个商品管理页面,一般都是基于该店家的上架或已下架的商品,再添加其他的查询条件等等。由于所有的查询都需要带有shopid和status条件,此时应该优先将这两个条件作为基本前缀,这样就可以方便复用索引。
例如一个(shopid, status, createdat)的索引,当查询条件只有shopid和status时,也可以使用该索引。如果完全根据字段的过滤效率来决定索引,就需要创建很多不同的索引。
避免排序
索引的值都是有序排列的,在创建索引时还可以显式指定每个列的排序方式,例如
create index idx_books_author_created_at on books (author, created_at DESC);
此时,如果执行下面的的查询
select * from books where author = 'author' order by created_at DESC;
由于满足auhtor的索引的created_at列都是有序排列的,所以不需要再进行额外的排序操作。

当结果数据集很大时,应该尽可能的通过索引来避免查询的额外排序,因为当内存排序空间(sort_buffer_size)不够用时,就需要把一部分内容放到硬盘中,此时会很影响性能。
例如一个分页查询每页显示100条,按从大到小的顺序显示,当浏览到第100页时,如果查询是file sort的,数据库需要使用堆排序先计算出这个表里面前100 * 100 = 10000条最大的数据,然后取9900 - 10000之间的数据返回给客户端,在计算的过程中,这个最大堆如果放不下就需要保存到磁盘中,但是又需要频繁比较和替换。
减少随机IO
在之前对硬盘知识了解后可以知道,一次随机读会有10ms的寻址延迟,如果一次查询涉及达到多次的随机读,会很大程度的限制查询性能。常见的sql查询造成随机IO的包括回表和join
例如下面的查询
select * from books where author = 'author1';
如果author1有100本书,但是这100本书并不是连续录入的,也就是说这100本书在硬盘中的存储是分离的。那么在有二级索引(author, created_at)的情况下,MySQL先通过二级索引找到满足author1的所有books的id,然后再通过id在聚簇索引中找到具体数据。
在这一过程中,二级索引的存储可以认为是连续的,那么二级索引耗时就是10ms + 100 * 0.01 = 11ms,包含一次寻址以及接下来的顺序读。而主键索引回表造成的随机IO最差情况是10ms * 100 = 1000ms。那么一共就需要11ms + 1000ms = 1011ms
通常减少随机IO的一种方式就使用覆盖索引。例如上面的查询中,如果我们只是想要该作者的书名,可以将(author, createdat)扩展为(author, createdat,name),然后将sql修改如下
select name from books where author = 'author1';
由于索引中已经有name的信息,此时就不会再次回表,查询耗时就变成了10ms + 100 * 0.01 = 11ms
值得一提的是mysql5.6新增一个叫做索引条件下推的优化,例如在有索引(author, created_at,name)的情况下,进行下面的查询:
select name from books where author = 'author1' and name like '%name%' and created_at > '2020-01-01';
根据最左匹配原则,这个查询只能用到索引的author字段,如果没有索引条件下推优化,数据库需要在二级索引找到满足author条件的所有列id,然后回表找到剩余信息后,再过滤name和created_at条件。
有了索引条件下推,在找到满足author条件的所有索引后,会再用索引的name字段进行普通过滤,尽量减少回表的次数,减少随机IO
避免重复索引
以减少随机IO中的查询为例,我们最终是把(author, createdat)扩展为(author, createdat,name),而不是创建一个新的(author, name)的索引。
在实际应用场景中也有类似的情况,例如创建一个userid的外键索引,然后又创建(userid, xxx)的索引。由于索引存储的顺序性,其实可以将这两个索引进行合并,如果我们先创建(userid, xxx)的索引,然后再添加userid的外键,mysql会自动使用前面创建索引。
索引是否越多越好呢?
显然不是,因为索引是对原表的数据冗余,那么他就必须要保证数据的一致性。如果原表增加了一条数据,索引也需要增加。如果原表修改了一条数据,那么对应的索引可能也要修改内容以及排序的位置,这可能会造成页分裂或页合并。一个表如果索引过多,那么维护索引与表的数据一致性也是不小的压力。通常建议在满足需求前提下,索引越少越好。
MySQL如何创建一个好索引?创建索引的5条建议【宇哥带你玩转MySQL 索引篇(三)】的更多相关文章
- 【宇哥带你玩转MySQL】索引篇(一)索引揭秘,看他是如何让你的查询性能指数提升的
场景复现,一个索引提高600倍查询速度? 首先准备一张books表 create table books( id int not null primary key auto_increment, na ...
- 为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生【宇哥带你玩转MySQL 索引篇(二)】
为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生 在上一节,我们聊到数据库为了让我们的查询加速,通过索引方式对数据进行冗余并排序,这样我们在使用时就可以在排好序的数据里进行快速的二分查找,使得查 ...
- spool命令、创建一个表,创建而且copy表,查看别的用户下的表,rowid行地址 索引的时候使用,表的增删改查,删除表,oracle的回收站
1.spool命令 spool "D:\test.txt" spool off SQL> host cls 2.创建一个表 SQL> --条件(1):有创建 ...
- File 创建一个空目录,创建一个多级目录,删除一个目录
package seday03; import java.io.File; /** * 创建一个空目录,* @author xingsir*/public class MkDirDemo { publ ...
- 前端传递数据到后台的两种方式;创建一个map或者创建一个FormData对象
一.构建一个map getAllDeptAllUsers(){ const modleCode = {'auditMenuId': this.auditMenuId, 'enterpriseId': ...
- Angular2快速入门-4.创建一个服务(创建NewsService提供数据)
上篇我们使用的数据是通过mock-news.ts中的const News[] 数组直接赋给Component 组件的,这篇我们把提供数据的部分单独封装成服务 第一.创建news.service.ts ...
- 【2】按照Django官网,创建一个web app 创建app/创建相应的数据库表
1. Creating app $ python manage.py startapp polls That'll create a directory polls, which is laid ou ...
- 用shell脚本写出检测/tmp/size.log文件,如果存在显示它的内容,不存在则创建一个文件将创建时间写入
1 #!/bin/bash 2 if [ -d "/tmp" ]; then 3 echo "/tmp is exists" 4 else 5 mkdir /t ...
- [转]自己写PHP扩展之创建一个类
原文:http://www.imsiren.com/archives/572 比如我们要创建一个类..PHP代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...
随机推荐
- Spring Framework之IoC容器
Spring IoC 概述 问题 1.什么是依赖倒置? 2.什么是控制反转? 3.什么是依赖注入? 4.它们之间的关系是怎样的? 5.优点有哪些? 依赖倒置原则 (Dependency Inversi ...
- 四、【Docker笔记】Docker容器
容器是Docker的另一个核心概念,容器就是镜像的一个运行实例,只是它具有一个可写的文件层,而镜像是一个只读的文件. 一.创建容器 1.新建容器 我们可以使用 docker create 命令来创建一 ...
- 用全站 CDN 部署 Discourse 论坛
Discourse 介绍 Discourse 是一款由 Stack Overflow 的联合创始人--Jeff Atwood,基于 Ruby on Rails 开发的开源论坛.相较于传统论坛,Disc ...
- 记一次Windb死锁排查
正在开会,突然线上站点线程数破千.然后一群人现场dump分析. 先看一眼线程运行状态 !eeversion 发现CPU占用并不高,19%,937条线程正在运行. 看看他们都在干什么. ~* e !cl ...
- C语言:signed和unsigned的区别
参考博客:https://blog.csdn.net/wen381951203/article/details/79922220 signed和unsigned用于修饰整数类型(包括char,从ANS ...
- cls
class : python中cls代表的是类的本身,相对应的self则是类的一个实例对象. class Person(object): def __init__(self, name, age): ...
- CentOS8中安装maven
下载maven,具体目录可根据实际情况而定 # wget http://mirrors.cnnic.cn/apache/maven/maven-3/3.3.9/binaries/apache-mave ...
- fastfdfs搭配nginx
fastfdfs搭配nginx 下载fastdfs-nginx-module 模块 wget https://github.com/happyfish100/fastdfs-nginx-module/ ...
- problems
exceptionUnable to connect to userservice.shanmaohuwai.com:80 . Error #0: stream_socket_client(): un ...
- SpringBoot入门系列(十一)统一异常处理的实现
前面介绍了Spring Boot 如何整合定时任务已经Spring Boot 如何创建异步任务和定时任务.不清楚的朋友可以看看之前的文章:<Spring Boot 入门系列文章> 接下来主 ...