mysql select in 不存在返回0_MySQL索引优化看这篇文章就够了!

1bfb9c8686e032759238168815cddfba.png

来源:cnblogs.com/songwenjie/p/9410009.html

本文主要讨论MySQL索引的部分知识。将会从MySQL索引基础、索引优化实战和数据库索引背后的数据结构三部分相关内容,下面一一展开(本文图片可点开放大)。

一、MySQL索引基础

首先,我们将从索引基础开始介绍一下什么是索引,分析索引的几种类型,并探讨一下如何创建索引以及索引设计的基本原则。

此部分用于测试索引创建的user表的结构如下:

0a9b720927129790cc5125547da34a3d.png

1. 什么是索引?

“索引(在MySQL中也叫“键key”)是存储引擎快速找到记录的一种数据结构。”——《高性能MySQL》

我们需要知道索引其实是一种数据结构,其功能是帮助我们快速匹配查找到需要的数据行,是数据库性能优化最常用的工具之一。其作用相当于超市里的导购员、书本里的目录。

2. 索引类型

可以使用SHOW INDEX FROM table_name;查看索引详情:

750648e74900230e27b663953c9aeb38.png

主键索引 PRIMARY KEY:它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引。注意:一个表只能有一个主键。

750648e74900230e27b663953c9aeb38.png

唯一索引 UNIQUE:唯一索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。可以通过ALTER TABLE table_name ADD UNIQUE (column);创建唯一索引:

750648e74900230e27b663953c9aeb38.png
750648e74900230e27b663953c9aeb38.png

可以通过ALTER TABLE table_name ADD UNIQUE (column1,column2);创建唯一组合索引:

750648e74900230e27b663953c9aeb38.png
750648e74900230e27b663953c9aeb38.png

普通索引 INDEX:这是最基本的索引,它没有任何限制。可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引:

750648e74900230e27b663953c9aeb38.png
750648e74900230e27b663953c9aeb38.png

组合索引 INDEX:即一个索引包含多个列,多用于避免回表查询。可以通过ALTER TABLE table_name ADD INDEX index_name(column1,column2, column3);创建组合索引:

750648e74900230e27b663953c9aeb38.png
750648e74900230e27b663953c9aeb38.png

全文索引 FULLTEXT:也称全文检索,是目前搜索引擎使用的一种关键技术。可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引:

750648e74900230e27b663953c9aeb38.png
750648e74900230e27b663953c9aeb38.png

索引一经创建不能修改,如果要修改索引,只能删除重建。可以使用

DROP INDEX index_name ON table_name;删除索引。

3、索引设计的原则

1)适合索引的列是出现在where子句中的列,或者连接子句中指定的列;

2)基数较小的类,索引效果较差,没有必要在此列建立索引;

3)使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间;

4)不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。

二、MySQL索引优化实战

上面我们介绍了索引的基本内容,这部分我们介绍索引优化实战。在介绍索引优化实战之前,首先要介绍两个与索引相关的重要概念,这两个概念对于索引优化至关重要。

此部分用于测试的user表结构:

750648e74900230e27b663953c9aeb38.png

1、索引相关的重要概念

基数:单个列唯一键(distict_keys)的数量叫做基数。

SELECT COUNT(DISTINCT name),COUNT(DISTINCT gender) FROM user;

750648e74900230e27b663953c9aeb38.png

user表的总行数是5,gender列的基数是2,说明gender列里面有大量重复值,name列的基数等于总行数,说明name列没有重复值,相当于主键。

返回数据的比例:user表中共有5条数据:

SELECT * FROM user;

750648e74900230e27b663953c9aeb38.png

查询满足性别为0(男)的记录数:

750648e74900230e27b663953c9aeb38.png

那么返回记录的比例数是:

750648e74900230e27b663953c9aeb38.png

同理,查询name为'swj'的记录数:

750648e74900230e27b663953c9aeb38.png

返回记录的比例数是:

750648e74900230e27b663953c9aeb38.png

现在问题来了,假设name、gender列都有索引,那么SELECT * FROM user WHERE gender = 0; SELECT * FROM user WHERE name = 'swj';都能命中索引吗?

user表的索引详情:

750648e74900230e27b663953c9aeb38.png

SELECT * FROM user WHERE gender = 0;没有命中索引,注意filtered的值就是上面我们计算的返回记录的比例数。

750648e74900230e27b663953c9aeb38.png

SELECT * FROM user WHERE name = 'swj';命中了索引index_name,因为走索引直接就能找到要查询的记录,所以filtered的值为100。

750648e74900230e27b663953c9aeb38.png

因此,返回表中30%内的数据会走索引,返回超过30%数据就使用全表扫描。当然这个结论太绝对了,也并不是绝对的30%,只是一个大概的范围。

回表:当对一个列创建索引之后,索引会包含该列的键值及键值对应行所在的rowid。通过索引中记录的rowid访问表中的数据就叫回表。回表次数太多会严重影响SQL性能,如果回表次数太多,就不应该走索引扫描,应该直接走全表扫描。

EXPLAIN命令结果中的Using Index意味着不会回表,通过索引就可以获得主要的数据。Using Where则意味着需要回表取数据。

2. 索引优化实战

有些时候虽然数据库有索引,但是并不被优化器选择使用。我们可以通过SHOW STATUS LIKE 'Handler_read%';查看索引的使用情况:

750648e74900230e27b663953c9aeb38.png

Handler_read_key:如果索引正在工作,Handler_read_key的值将很高。

Handler_read_rnd_next:数据文件中读取下一行的请求数,如果正在进行大量的表扫描,值将较高,则说明索引利用不理想。

索引优化规则:

1)如果MySQL估计使用索引比全表扫描还慢,则不会使用索引。

返回数据的比例是重要的指标,比例越低越容易命中索引。记住这个范围值——30%,后面所讲的内容都是建立在返回数据的比例在30%以内的基础上。

2)前导模糊查询不能命中索引。

name列创建普通索引:

750648e74900230e27b663953c9aeb38.png

前导模糊查询不能命中索引:

EXPLAIN SELECT * FROM user WHERE name LIKE '%s%';

750648e74900230e27b663953c9aeb38.png

非前导模糊查询则可以使用索引,可优化为使用非前导模糊查询:

EXPLAIN SELECT * FROM user WHERE name LIKE 's%';

750648e74900230e27b663953c9aeb38.png

3)数据类型出现隐式转换的时候不会命中索引,特别是当列类型是字符串,一定要将字符常量值用引号引起来。

EXPLAIN SELECT * FROM user WHERE name=1;

750648e74900230e27b663953c9aeb38.png

EXPLAIN SELECT * FROM user WHERE name='1';

750648e74900230e27b663953c9aeb38.png

4)复合索引的情况下,查询条件不包含索引列最左边部分(不满足最左原则),不会命中符合索引。

name,age,status列创建复合索引:

ALTER TABLE user ADD INDEX index_name (name,age,status);

750648e74900230e27b663953c9aeb38.png

user表索引详情:

SHOW INDEX FROM user;

750648e74900230e27b663953c9aeb38.png

根据最左原则,可以命中复合索引index_name:

EXPLAIN SELECT * FROM user WHERE name='swj' AND status=1;

750648e74900230e27b663953c9aeb38.png

注意,最左原则并不是说是查询条件的顺序:

EXPLAIN SELECT * FROM user WHERE status=1 AND name='swj';

750648e74900230e27b663953c9aeb38.png

而是查询条件中是否包含索引最左列字段:

EXPLAIN SELECT * FROM user WHERE status=2 ;

750648e74900230e27b663953c9aeb38.png

5)union、in、or都能够命中索引,建议使用in。

union:

EXPLAIN SELECT*FROM user WHERE status=1

UNION ALL

SELECT*FROM user WHERE status = 2;

750648e74900230e27b663953c9aeb38.png

in:

EXPLAIN SELECT * FROM user WHERE status IN (1,2);

750648e74900230e27b663953c9aeb38.png

or:

EXPLAIN SELECT*FROM user WHERE status=1OR status=2;

750648e74900230e27b663953c9aeb38.png

查询的CPU消耗:or>in>union。

6)用or分割开的条件,如果or前的条件中列有索引,而后面的列中没有索引,那么涉及到的索引都不会被用到。

EXPLAIN SELECT * FROM payment WHERE customer_id = 203 OR amount = 3.96;

750648e74900230e27b663953c9aeb38.png

因为or后面的条件列中没有索引,那么后面的查询肯定要走全表扫描,在存在全表扫描的情况下,就没有必要多一次索引扫描增加IO访问。

7)负向条件查询不能使用索引,可以优化为in查询。

负向条件有:!=、<>、not in、not exists、not like等。

status列创建索引:

ALTER TABLE user ADD INDEX index_status (status);

65ae694cf62ab51c3131903d62f24657.png

user表索引详情:

SHOW INDEX FROM user;

cbf35cb82c023ec721bc3e5da17945a0.png

负向条件不能命中缓存:

EXPLAIN SELECT * FROM user WHERE status !=1 AND status != 2;

c5eed8e2f3c34dbb33800c0f1c56f6f9.png

可以优化为in查询,但是前提是区分度要高,返回数据的比例在30%以内:

EXPLAIN SELECT * FROM user WHERE status IN (0,3,4);

9e2a5c04eed00962e11a2d0de486e680.png

8)范围条件查询可以命中索引。范围条件有:、>=、between等。

status,age列分别创建索引:

ALTER TABLE user ADD INDEX index_status (status);

65ae694cf62ab51c3131903d62f24657.png

ALTER TABLE user ADD INDEX index_age (age);

98efcf6e471dda67566ca404395d8b31.png

user表索引详情:

SHOW INDEX FROM user;

bb6263f080c9138998a1d7d0e47ba525.png

范围条件查询可以命中索引:

EXPLAIN SELECT * FROM user WHERE status>5;

159bb0bc0c42f411c8907d977368df9a.png

范围列可以用到索引(联合索引必须是最左前缀),但是范围列后面的列无法用到索引,索引最多用于一个范围列,如果查询条件中有两个范围列则无法全用到索引:

EXPLAIN SELECT * FROM user WHERE status>5 AND age<24;

87df96368599193307da1822dcdb01af.png

如果是范围查询和等值查询同时存在,优先匹配等值查询列的索引:

EXPLAIN SELECT * FROM user WHERE status>5 AND age=24;

750648e74900230e27b663953c9aeb38.png

8)数据库执行计算不会命中索引。

EXPLAIN SELECT * FROM user WHERE age>24;

750648e74900230e27b663953c9aeb38.png

EXPLAIN SELECT * FROM user WHERE age+1>24;

750648e74900230e27b663953c9aeb38.png

计算逻辑应该尽量放到业务层处理,节省数据库的CPU的同时最大限度的命中索引。

9)利用覆盖索引进行查询,避免回表。

被查询的列,数据能从索引中取得,而不用通过行定位符row-locator再到row上获取,即“被查询列要被所建的索引覆盖”,这能够加速查询速度。

user表的索引详情:

750648e74900230e27b663953c9aeb38.png

因为status字段是索引列,所以直接从索引中就可以获取值,不必回表查询:

Using Index代表从索引中查询:

EXPLAIN SELECT status FROM user where status=1;

750648e74900230e27b663953c9aeb38.png

当查询其他列时,就需要回表查询,这也是为什么要避免SELECT*的原因之一:

EXPLAIN SELECT * FROM user where status=1;

750648e74900230e27b663953c9aeb38.png

10)建立索引的列,不允许为null。

单列索引不存null值,复合索引不存全为null的值,如果列允许为null,可能会得到“不符合预期”的结果集,所以,请使用not null约束以及默认值。

remark列建立索引:

ALTER TABLE user ADD INDEX index_remark (remark);

750648e74900230e27b663953c9aeb38.png

IS NULL可以命中索引:

EXPLAIN SELECT * FROM user WHERE remark IS NULL;

750648e74900230e27b663953c9aeb38.png

IS NOT NULL不能命中索引:

EXPLAIN SELECT * FROM user WHERE remark IS NOT NULL;

57b605ff706d38d5b3b0eff33b2e7417.png

虽然IS NULL可以命中索引,但是NULL本身就不是一种好的数据库设计,应该使用NOT NULL约束以及默认值。

a. 更新十分频繁的字段上不宜建立索引:因为更新操作会变更B+树,重建索引。这个过程是十分消耗数据库性能的。

b. 区分度不大的字段上不宜建立索引:类似于性别这种区分度不大的字段,建立索引的意义不大。因为不能有效过滤数据,性能和全表扫描相当。另外返回数据的比例在30%以外的情况下,优化器不会选择使用索引。

c. 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。虽然唯一索引会影响insert速度,但是对于查询的速度提升是非常明显的。另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,在并发的情况下,依然有脏数据产生。

d. 多表关联时,要保证关联字段上一定有索引。

e. 创建索引时避免以下错误观念:索引越多越好,认为一个查询就需要建一个索引;宁缺勿滥,认为索引会消耗空间、严重拖慢更新和新增速度;抵制唯一索引,认为业务的唯一性一律需要在应用层通过“先查后插”方式解决;过早优化,在不了解系统的情况下就开始优化。

3. 小结

对于自己编写的SQL查询语句,要尽量使用EXPLAIN命令分析一下,做一个对SQL性能有追求的程序员。衡量一个程序员是否靠谱,SQL能力是一个重要的指标。作为后端程序员,深以为然。

(完)

ps:最近学习资料有更新,wx公众号:Java7749

#java讨论区# #编程语言JAVA# #MySQL#

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/550284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言文件实现通信录程序,学C三个月了,学了文件,用C语言写了个通讯录程序...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include #include #include #include struct QQ //结构体&#xff0c;有6个成员{char name[20];char age[5];char tel[15];char qq[15];char birth[12];char address[80];}user;void style()//窗口风格函数{system("mode con…

《软件测试实战:微软技术专家经验总结》

《软件测试实战&#xff1a;微软技术专家经验总结》 基本信息 作者&#xff1a; 史亮 丛书名&#xff1a; 图灵原创 出版社&#xff1a;人民邮电出版社 ISBN&#xff1a;9787115345844 上架时间&#xff1a;2014-3-3 出版日期&#xff1a;2014 年2月 开本&#xff1a;16开 页…

websocket一直无法链接_.NET Core 实现基于Websocket的在线聊天室

什么是Websocket我们在传统的客户端程序要实现实时双工通讯第一想到的技术就是socket通讯&#xff0c;但是在web体系是用不了socket通讯技术的&#xff0c;因为http被设计成无状态&#xff0c;每次跟服务器通讯完成后就会断开连接。在没有websocket之前web系统如果要做双工通讯…

c语言既能读字母也能读数字,2014计算机二级考试C语言考前预测选择题

无忧考网为大家收集整理了《2014计算机二级考试C语言考前预测选择题》供大家参考&#xff0c;希望对大家有所帮助&#xff01;&#xff01;&#xff01;1.结构化分析方法是面向( )的自顶向下、逐步求精进行需求分析的方法A.对象B.数据结构C.数据流D.目标2.对线性表进行二分法检…

生成 cone(锥体)

简介 生成 圆锥 代码 // CreateCone.cpp: 定义控制台应用程序的入口点。 //#include <iostream> #include <OpenMesh/Core/IO/MeshIO.hh> #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh> #include<cmath> #include "AddPolygon.h" …

使用函数统计指定c语言,浙大版《C语言程序设计(第3版)》题目集 习题5-5 使用函数统计指定数字的个数...

热门文章推荐以下50道算法编程题访问量较大&#xff0c;包含常用语法&#xff0c;数据结构&#xff0c;解题思路等等&#xff0c;作为C语言学习者必练题目推荐给各位。创作不易&#xff0c;记得一键三连哦&#xff01;&#xff01;&#xff01;习题5-5 使用函数统计指定数字的个…

半监督分类算法_基于同质区和迁移学习的高光谱图像半监督分类

作 者 信 息赵婵娟&#xff0c;周绍光&#xff0c;丁 倩&#xff0c;刘丽丽(河海大学 地球科学与工程学院&#xff0c;江苏 南京 211100)“【摘要】针对高光谱遥感图像分类中标记样本难获取的问题&#xff0c;提出了一种基于同质区和迁移学习的新型半监督分类方法。首先对高…

【题解】Luogu P4052[JSOI2007]文本生成器 AC自动机

AC自动机上DP f[i][j]表示节点j,串长为i 当一个串的尾节点被标红或其fail指针指向的被标红&#xff0c;都是可读的 用总的减去不可读的即为答案 #include<iostream> #include<cstring> #include<cstdio> #include<queue> #define MOD (10007) #define …

在SQL Server中使用命令调用SSIS包

在SQL Server中可以使用dtexec命令运行SSIS包&#xff08;2005以上版本&#xff09;,当然也可以通过系统过程:xp_cmdshell调用dtexec运行SSIS包。 具体操作步骤如下&#xff1a; 1.首先&#xff0c;当然是要在Business Intelligence中设计好包&#xff0c;并调试通过。 2.然后&…

c语言 结构体数组嵌套另一个结构体数组怎么初始化?,如何初始化结构体数组(内嵌结构体)...

Keil 结构体变量初始化tujidi1csd25142016-09-30一维数组结构体数组结构体数组&#xff0c;通过改变指针类型改变访问数组的方式toto129748850424332014-08-01mfc结构体指针、结构体数组指针u01399024812292014-12-14memset结构体初始化shagua_nan184092016-03-16【C -> 容器…

本特利3500_本特利技术控的自我修养之 轴位移探头安装

正在学习之-客户答疑国家疫情当前&#xff0c;除了保护好自己、不给国家添麻烦以外我们能做的实在有限。上学时因为种种原因没有按家里的期望学医&#xff0c;也没有能够考上最喜欢的生物专业(理想中希望去非洲做动物类研究工作-跟拍狮子)至今还是憾事&#xff0c;但是加入本特…

map标签的详细使用参数

map标签必须成对出现&#xff0c;即 <map> ....</map> 同时map必须和area配合使用。 img标签里的usermap属性值必须与map标签里的id和name值完全一致 area标签&#xff1a;定义图片的点击区域 area 是单标签&#xff0c;不成对。 属性&#xff1a; accesskey 快捷键…

快捷方式修复_Mac上的屏幕截图不起作用该如何修复?

屏幕截图是Mac提供的内置功能&#xff0c;很少有它不起作用。但是由于某些意外的设置或硬件问题&#xff0c;Mac上的屏幕截图有时无法正常工作&#xff0c;这里提供的是Mac上的屏幕截图不起作用该如何修复&#xff1f;1.在Mac上启用屏幕快照快捷方式如果您按CMD SHIFT 3或CMD…

计算机二级2019年9月c语言题库,(3)2019年9月计算机二级C语言试题

当你的计算机知识还撑不起你的学习、工作时&#xff0c;那你就应该静下心来刷计算机等级考试题库-二级C语言试题。1)下列叙述中正确的是( )。A.解决同一个问题的不同算法的时间复杂度一般是不同的B.解决同一个问题的不同算法的时间复杂度必定是相同的C.对同一批数据作同一种处理…

MySQL对于有大量重复数据表的处理方法

需要在MySQL的一张innodb引擎的表(tableA)上添加一个唯一索引(idx_col1_u)。但是对于每个key(col1)表中已经有大量重复数据。此时&#xff0c;做数据的手工清理&#xff0c;或者SQL处理是非常耗时的。 MySQL有一个独有的 alter ignore add unique index的语法。 ALTER [ONLINE …

python qt 按钮_PyQt(Python+Qt)学习随笔:toolButton的popupMode属性

属性介绍toolButton的popupMode属性为设有菜单集或Action列表的toolButton指定菜单弹出模式&#xff0c;类型为枚举类型ToolButtonPopupMode&#xff0c;有如下三种模式&#xff1a;1、DelayedPopup(值为0)&#xff1a;按住toolButton一定时间(超时取决于样式&#xff0c;请参见…

最短哈密顿路径 c语言算法,最短路径系列【最短路径、哈密顿路等】

最短路径问题&#xff0c;一个经典算法问题。本文粗略总结了一种常见的最短路径算法&#xff0c;以及几个最短路径变种问题的解法&#xff0c;其中包括哈密顿路。对于有向图或者无向图&#xff0c;假设有V个节点&#xff0c;E条边&#xff0c;G[Vi,Vj]表示图中点Vi到Vj边的权值…

Android应用开发学习笔记之多线程与Handler消息处理机制

作者&#xff1a;刘昊昱 博客&#xff1a;http://blog.csdn.net/liuhaoyutz 和JAVA一样&#xff0c;Android下我们可以通过创建一个Thread对象实现多线程。Thread类有多个构造函数&#xff0c;一般通过构造函数Thread(Runnable runnable)实现多线程&#xff0c;代码如下&#…

nsga2算法c++实现_Bellman-Ford算法

之前文章对于Dijkstra算法进行了讲解和实现&#xff0c;其实现的原理在于采用贪心算法&#xff0c;遍历N(结点数)次&#xff0c;每次找到局部最优的路径的结点u&#xff0c;判断该节点可达的顶点v的权重是否大于结点u权重u->v的权重&#xff0c;如果大于则替换顶点v的权重(也…

JSTL详解(一)

将jstl.jar包导入到工程中 jstldemo1.jsp <% taglib prefix"c" uri"http://java.sun.com/jsp/jstl/core" %> <% page contentType"text/html; charsetgb2312" language"java" %> <c:set var"userName&quo…