黑马程序员 MySQL数据库入门到精通——进阶篇(1)
- 1. 存储引擎
- 1.1 MySQL体系结构
- 1.2 存储引擎简介
- 1.3 存储引擎特点
- 1.3.1 InnoDB
- 1.3.2 MyISAM
- 1.3.3 Memory
- 1.3.4 三种存储引擎对比
- 1.4 存储引擎选择
- 2. 索引
- 2.1 索引概述(Index Overview)
- 2.2 索引结构(index structure)
- 2.2.1 二叉树
- 2.2.2 红黑树:我tm来了!
- 2.2.3 B树:我tm来了!
- 2.2.4 B树构造过程
- 2.2.5 B+树
- 2.2.6 Hash树
- 2.3 索引分类(Index classification)
- 2.4 索引语法(Index syntax)
- 2.4.1 创建索引
- 2.4.2 查看索引
- 2.4.3 删除索引
- 2.5 性能分析(SQL Performance Analyzer)
- 2.5.1 SQL执行频率
- 2.5.2 慢查询日志(当select权重比较高)
- 2.5.3 profile详情
- 2.5.4 explain详情
- 2.6 索引使用规则(index usage)
- 2.6.1 验证索引效率
- 2.6.2 最左前缀法则
- 2.6.3 索引失效情况一
- 2.6.4 索引失效情况二
- 2.6.5 SQL 提示
- 2.6.6 覆盖索引&回表查询
- 2.6.7 前缀索引
- 2.6.8 单列&联合索引
- 2.7 索引设计原则(Index design principles)
1. 存储引擎
1.1 MySQL体系结构
MySQL 最重要、最与众不同的特性是它的存储引擎架构,这种架构的设计将查询处理(Query Processing)及其他系统任务(Server Task)和数据的存储/提取相分离。这种处理和存储分离的设计可以在使用时根据性能、特性,以及其他需求来选择数据存储的方式。
MySQL的体系结构图
由图,可以看出MySQL最上层是连接组件。上图中,服务器是由连接池、管理工具和服务、SQL接口、解析器、优化器、缓存、存储引擎、文件系统组成。
-
用户:进行数据库连接的人。
-
支持接口:是第三方语言提供和数据库连接的接口,常见的有jdbc,odbc,c的标准api函数等等。
-
管理工具和服务:系统管理和控制工具,例如备份恢复、Mysql复制、集群等(见图)
-
连接层:提供与用户的连接服务,用于验证登录服务。
—> 连接池:由于每次建立连接需要消耗很多时间,连接池的作用就是将这些连接缓存下来,下次可以直接用已经建立好的连接,提升服务器性能。
—>服务层:完成大多数的核心服务功能。有SQL接口,解析器parser,优化器optimizer,查询缓存 cache/buffer 。
—>SQL接口:接受用户的SQL命令,并且返回用户需要查询的结果。
比如select * from就是调用SQL Interface
—>解析器: SQL命令传递到解析器的时候会被解析器验证和解析。解析器是由Lex和YACC实现的,是一个很长的脚本。
解析器功能是:
-
将SQL语句分解成数据结构,并将这个结构传递到后续步骤,以后SQL语句的传递和处理就是基于这个结构的。
-
如果在分解构成中遇到错误,那么就说明这个SQL语句是不合理的。
—>优化器:查询优化器,SQL语句在查询之前会使用查询优化器对查询进行优化。他使用的是“选取-投影-联接”策略进行查询。举一个例子:
select * from users where uname='admin';
- 这个select查询先根据where语句进行选取,而不是先将表全部查询出来以后再进行uname过滤。(选取)
- 这个select查询先根据*进行属性投影,而不是将属性全部取出以后再进行过滤。(投影)
- 将这两个查询条件联接起来生成最终查询结果。(联接)
—>缓存器: 查询缓存,如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。
- 通过LRU算法将数据的冷端溢出,未来得及时刷新到磁盘的数据页,叫脏页。
- 这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等
简而言之,服务层执行过程:
SQL语句通过SQL接口,服务器如果缓存cache有命中查询结果,直接读取数据。如果没有命中查询结果,由解析器进行sql语句的解析,预处理,经过优化器进行优化后提交给引擎层。通俗地说—>服务层告诉引擎层要做什么。
引擎层: 提供各种存储引擎,真正的负责MySQL中数据的存储和提取。常见有Innodb MyISAM。
Innodb支持全文索引,事务(高并发),行锁,myisam 性能优先。 mysql中查询引擎语句:show engines.
通俗地说—>引擎层会转发服务层解析出来的SQL语句告诉存储层要做什么(增删改查)并且告诉存储层要以何种方式做(innodb myisam等等)。
存储层: 数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
SQL的执行流程:数据库通常不会被单独使用,而是由其它编程语言通过SQL支持接口调用MySQL。由MySQL处理并返回执行结果。
①首先,其它编程语言通过SQL支持接口调用MySQL,MySQL收到请求后,会将该请求暂时放在连接池,并由管理服务与工具进行管理。
②当该请求从等待队列进入到处理队列时,管理器会将该请求传给SQL接口,SQL接口接收到请求后,它会将请求进行hash处理并与缓存中的数据进行对比。
③如果匹配,则通过缓存直接返回处理结果;否则,去文件系统查询:由SQL接口传给后面的解析器,解析器会判断SQL语句是否正确,若正确则将其转化为数据结构。解析器处理完毕后,便将处理后的请求传给优化器控制器,它会产生多种执行计划,最终数据库会选择最优的方案去执行。
④确定最优执行计划后,SQL语句交由存储引擎处理,存储引擎将会到文件系统中取得相应的数据,并原路返回。
客户端连接器:各个语言的都可以链接服务器
看到的服务端是分层的。
首先,连接层是主要是接收客户端的连接,完成一些连接的处理,以及认证授权的操作(校验用户名密码等),相关的安全方案,检查是否超过最大连接数等等。
-
连接层
- 最上层是一些客户端和链接服务,主要完成一些类似于连接处理、 授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
- 最上层是一些客户端和链接服务,主要完成一些类似于连接处理、 授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
-
服务层
- 第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询, SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现, 如过程、函数等。
- 第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询, SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现, 如过程、函数等。
-
引擎层
- 存储引擎真正的负责了MySQL 中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。可插拔,不用的时候 可以拔掉。
- 存储引擎真正的负责了MySQL 中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。可插拔,不用的时候 可以拔掉。
-
存储层
- 主要是将数据存储在文件系统之上,并完成与存储引擎的交互。.
- 主要是将数据存储在文件系统之上,并完成与存储引擎的交互。.
1.2 存储引擎简介
引擎是核心!!
不同的引擎还有会不同的应用场景!!
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。(一个数据库下不同表,是可以使用不同的存储引擎的)
-- 查询建表语句
show create tabLe account;
默认InnoDB
-- 创建表my_myisam,并指定MyISAM存储引擎
create table my_myisam(
id int,
name varchar(10)
)engine = MyISAM;
-- 创建表my_ memory,指定Memory存储引擎
create table my_memory(
id int,
name varchar(10)
) engine = Memory;
1.3 存储引擎特点
1.3.1 InnoDB
-
定义:
InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL 5.5之后, InnoDB是默认的MySQL存储引擎。 -
特点
(1)DML操作遵循ACID模型,支持事务;
(2)行级锁,提高并发访问性能;
(3)支持外键FOREIGN KEY约束,保证数据的完整性和正确性; -
文件
xxx.ibd: xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi) 、数据和索引。
参数: innodb_ file_ per_ table
如果我们想去看结构,我们可以使用CMD指令:
idb2sdi account.idb
输入之后我们会看到 返回JSON格式
InnoDB存储引擎逻辑存储结构
Trx 最后一次操作事务的 ID
- InnoDB中,Page页是磁盘操作的最小单元,大小是固定的16KB
- InnoDB中,一个Extent区的大小是固定的 1MB,共包含64个Page页
1.3.2 MyISAM
-
介绍
MyISAM是MySQL早期的默认存储引擎。 -
特点
不支持事务,不支持外键
支持表锁,不支持行锁
访问速度快 -
这种存储引擎的表涉及到的文件包括:
- sdi 表结构信息
- MYD 数据
- MYI 索引
1.3.3 Memory
-
介绍
Memory引擎的表数据时存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用。 -
特点
(1)内存存放 (访问速度很快)
(2)hash索引(默认) -
文件
xxx.sdi:存储表结构信息
数据都在内存中的,所以只有表结构信息,其他的信息 这里没有
1.3.4 三种存储引擎对比
特点 | InnoDB | MyISAM | Memory |
---|---|---|---|
存储限制 | 64TB | 有 | 有 |
事务安全 | 支持 | - | - |
锁机制 | 行锁 | 表锁 | 表锁 |
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | - | - | 支持 |
全文索引 | 支持(5.6版本之后) | 支持 | - |
空间使用 | 高 | 低 | N/A |
内存使用 | 高 | 低 | 中等 |
批量插入速度 | 低 | 高 | 高 |
支持外键 | 支持 | - | - |
1.4 存储引擎选择
存储引擎没有好坏之分!我们需要的选择合适的♥
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。
- InnoDB :
- 是Mysq|的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。
- MyISAM:
- 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
- MEMORY:
- 将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。
2. 索引
大家大多是都是win操作系统,但是对于视频教学,已经到Linux操作系统中,大家可查看其他的博客,我看的是这个。
注意下载与安装,收费情况,大家可以去找找资源什么的,本人不负责嗷!!!!!!!
注意想复制下面的代码:Ubuntu的复制粘贴是Ctrl+Shift+C/V
安装你的MySQL:
- 从 Ubuntu 仓库安装 MySQL
首先,输入下列命令确保你的仓库已经被更新:
sudo apt update
- 现在,安装 MySQL 5.7,简单输入下列命令:
sudo apt install mysql-server -y
验证你的 MySQL 安装
要验证 MySQL 已经正确安装,使用下列命令:
sudo systemctl status mysql.service
注意上面运行时出现了:
正在等待缓存锁:无法获得锁 /var/lib/dpkg/lock-frontend。锁正由进程 13535(unattended-upgr)持有...
执行下面的代码
sudo rm /var/lib/dpkg/lock-frontend
sudo rm /var/lib/dpkg/lock
sudo apt install mysql-server -y
这将展示一些关于 MySQL 服务的信息:
你应该在那里看到 “Active: active (running)”
。如果你没有看到,使用下列命令去开始这个服务:
sudo systemctl start mysql.service
配置/保护 MySQL
对于刚安装的 MySQL,你应该运行它提供的安全相关的更新命令。就是:
sudo mysql_secure_installation
这样做首先会询问你是否想使用 “ 密码有效强度(validate password component)”。如果你想使用它,你将必须选择一个最小密码强度(0 – 低,1 – 中,2 – 高)。你将无法输入任何不遵守所选规则的密码。如果你没有使用强密码的习惯(本应该使用),这可能会配上用场。如果你认为它可能有帮助,那你就键入 y或者 Y,按下回车键,然后为你的密码选择一个强度等级和输入一个你想使用的密码。如果成功,你将继续强化过程;否则你将重新输入一个密码。
但是,如果你不想要此功能(我不会),只需按回车或任何其他键即可跳过使用它。
对于其他选项,我建议开启它们(对于每一步输入 y 或者 Y 和按下回车)。它们(依序)是:
“ 移除匿名用户(remove anonymous user)”,
“ 禁止 root 远程登录(disallow root login remotely)”,
“ 移除测试数据库及其访问(remove test database and access to it)”。
“ 重新载入权限表(reload privilege tables now)”。
链接与断开 MySQL Server
为了运行 SQL 查询,你首先必须使用 MySQL 连到服务器并在 MySQL 提示符使用。
执行此操作的命令是:mysql -h host_name -u user -p
-h 用来指定一个主机名(如果这个服务被安装到其他机器上,那么会有用;如果没有,忽略它)
-u 指定登录的用户
-p 指定你想输入的密码.
虽然出于安全原因不建议,但是你可以在命令行最右边的 -p 后直接输入密码。例如,如果用户test_user 的密码是 1234,那么你可以在你使用的机器上尝试去连接,你可以这样使用:
mysql -u test_user -p1234
如果你成功输入了必要的参数,你将会收到由 MySQL shell 提示符提供的欢迎(mysql >):
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'test_user'@'localhost' (using password: YES)
Ubuntu MySQL解决ERROR 1698 (28000): Access denied for user ‘root‘@‘localhost‘的问题
要从服务端断开连接和离开 MySQL 提示符,输入:QUIT
输入 quit (MySQL 不区分大小写)或者 \q 也能工作。按下回车退出。
然后就是关于本教程了:
完成的任务是指:在win11上安装了Ubuntu的虚拟机,并且我使用win11里的DataGrip 连接我虚拟机上的数据库。
- 首先我们要在Ubuntu 上一个用户:默认的root用户只能当前节点localhost访问,是无法远程访问的,我们还需要一个root账户,用户远程访问
create user 'root'@'%' identified with mysql_native_password by '1234';
- 并给root用户分配权限
grant all on *.* to 'root'@'%';
(现在是虚拟机的数据库是登录的状态 显示mysql>
) - 现在我们进入DataGrip
- 需要修改的地方又这些
- Host修改成虚拟机的ip,怎么查看呢?
Ubuntu虚拟机如何查看IP地址
https://blog.csdn.net/u011318077/article/details/104627076
-
端口一定是开放的,即检验虚拟机的防火墙,推荐关闭
-
User 就是 你建立的新用户 root
-
Password 就是 你设置的密码 跟着上面做的 密码就是1234
- 然后点击Test Connection
2.1 索引概述(Index Overview)
索引(index) 是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
备注:上述二叉树索引结构的只是一个示意图,并不是真实的索引结构。
- 优缺点
优势 | 劣势 |
---|---|
提高数据检索的效率,降低数据库的IO成本 | 索引列也是要占用空间的。 |
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗 | 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT 、UPDATE 、DELETE 时,效率降低。 |
- 但是对于现在来讲,磁盘很便宜,而且对于现在的业务逻辑,增删改的比例远远小于查!
2.2 索引结构(index structure)
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:
索引结构 | 描述 |
---|---|
B+Tree索引 | 最常见的索引类型,大部分引擎都支持B+树索引(InnoDB,Memory,MyISAM) |
Hash索引 | 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 |
R-tree(空间索引) | 空间索引是MyISAM引擎的一一个特殊索引类型,主要用于地理空间数据类型,使用较少 |
Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES |
Hash索引性能很高,不支持范围查询
索引 | InnoDB | MyISAM | Memory |
---|---|---|---|
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | 不支持 | 不支持 | 支持 |
R-tree索引 | 不支持 | 支持 | 不支持 |
Full-text | 5.6版本之后支持 | 支持 | 不支持 |
2.2.1 二叉树
一个节点最多包含两个子节点。
但是二叉树有弊端!
二叉树缺点:顺序插入时,会形成-一个链表,查询性能大大降低。大数据量情况下,层级较深,检索速度慢。
2.2.2 红黑树:我tm来了!
老子是自平衡二叉树!
红黑树:大数据量情况下,层级较深,检索速度慢。
2.2.3 B树:我tm来了!
老子是多路平衡查找树!
以一颗最大度数(max-degree)为5(5阶)的b-tree为例(每个节点最多存储4个key,5个指针):
树的度数指的是一个节点的子节点个数
n
n
n 个key,
n
+
1
n+1
n+1 个指针
2.2.4 B树构造过程
中间元素向上分裂!动态界面是这个!
2.2.5 B+树
以一颗最大度数(max-degree)为4 (4阶)的b+tree为例:
特点:
- 所有的节点都会出现在叶子节点
- 叶子节点才是存储数据的,分叶子节点是索引的
- 叶子节点行程单向链表
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
2.2.6 Hash树
Hash
哈希索引就说采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞) ,可以通过链表来解决。
Hash索引特点
- Hash索引只能用于对等比较(=,in),不支持范围查询(between, >, <,…)
- 无法利用索引完成排序操作
- 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引
存储引擎支持
在MySQL中,支持hash索引的是Memory引擎, 而InnoDB中 具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的。
为什么InnoDB存储引擎选择使用B+tree索引结构?
- 相对于二叉树,层级更少,搜索效率高;
- 对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
- 相对Hash索引,B+tree支持范围匹配及排序操作;
2.3 索引分类(Index classification)
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对于表中主键创建的索引 | 默认自动创建,只能有一个 | PRIMARY |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个 | FULLTEXT |
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
分类 | 含义 | 特点 |
---|---|---|
聚集索引(Clustered Index) | 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 | 必须有,而且只有一个 |
二级索引(Secondary Index) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则: .
➢如果存在主键, 主键索引就是聚集索引。
➢如果不存在主键,将使用第一个唯一(UNIQUE) 索引作为聚集索引。
➢如果表没有主键, 或没有合适的唯一索引, 则nnoDB会自动生成一个rowid作为隐藏的聚集索引。
现在描述一下这个索引分类:
select * from user where name='Arm';
学习一个名词:回表查询
先走二级索引找到对应的主键值,再用聚集索引找到行数据。
2.4 索引语法(Index syntax)
2.4.1 创建索引
CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name (index_col_name,...);
2.4.2 查看索引
SHOW INDEX FROM table_name;
2.4.3 删除索引
DROP INDEX index_ name ON table_ name;
- name字段为姓名字段,该字段的值可能会重复,为该字段创建索引。
- phone手机号字段的值, 是非空,且唯一的,为该字段创建唯一索引。
- 为profession、 age、status创建联合索引。
- 为email建立合适的索引来提升查询效率。
create table tb_user(
id int primary key auto_increment comment '主键',
name varchar(50) not null comment '用户名',
phone varchar(11) not null comment '手机号',
email varchar(100) comment '邮箱',
profession varchar(11) comment '专业',
age tinyint unsigned comment '年龄',
gender char(1) comment '性别 , 1: 男, 2: 女',
status char(1) comment '状态',
createtime datetime comment '创建时间'
) comment '系统用户表';
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('吕布', '17799990000', 'lvbu666@163.com', '软件工程', 23, '1', '6', '2001-02-02 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('曹操', '17799990001', 'caocao666@qq.com', '通讯工程', 33, '1', '0', '2001-03-05 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('赵云', '17799990002', '17799990@139.com', '英语', 34, '1', '2', '2002-03-02 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('孙悟空', '17799990003', '17799990@sina.com', '工程造价', 54, '1', '0', '2001-07-02 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('花木兰', '17799990004', '19980729@sina.com', '软件工程', 23, '2', '1', '2001-04-22 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('大乔', '17799990005', 'daqiao666@sina.com', '舞蹈', 22, '2', '0', '2001-02-07 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('露娜', '17799990006', 'luna_love@sina.com', '应用数学', 24, '2', '0', '2001-02-08 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('程咬金', '17799990007', 'chengyaojin@163.com', '化工', 38, '1', '5', '2001-05-23 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('项羽', '17799990008', 'xiaoyu666@qq.com', '金属材料', 43, '1', '0', '2001-09-18 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('白起', '17799990009', 'baiqi666@sina.com', '机械工程及其自动化', 27, '1', '2', '2001-08-16 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('韩信', '17799990010', 'hanxin520@163.com', '无机非金属材料工程', 27, '1', '0', '2001-06-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('荆轲', '17799990011', 'jingke123@163.com', '会计', 29, '1', '0', '2001-05-11 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('兰陵王', '17799990012', 'lanlinwang666@126.com', '工程造价', 44, '1', '1', '2001-04-09 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狂铁', '17799990013', 'kuangtie@sina.com', '应用数学', 43, '1', '2', '2001-04-10 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('貂蝉', '17799990014', '84958948374@qq.com', '软件工程', 40, '2', '3', '2001-02-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('妲己', '17799990015', '2783238293@qq.com', '软件工程', 31, '2', '0', '2001-01-30 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('芈月', '17799990016', 'xiaomin2001@sina.com', '工业经济', 35, '2', '0', '2000-05-03 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('嬴政', '17799990017', '8839434342@qq.com', '化工', 38, '1', '1', '2001-08-08 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狄仁杰', '17799990018', 'jujiamlm8166@163.com', '国际贸易', 30, '1', '0', '2007-03-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('安琪拉', '17799990019', 'jdodm1h@126.com', '城市规划', 51, '2', '0', '2001-08-15 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('典韦', '17799990020', 'ycaunanjian@163.com', '城市规划', 52, '1', '2', '2000-04-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('廉颇', '17799990021', 'lianpo321@126.com', '土木工程', 19, '1', '3', '2002-07-18 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('后羿', '17799990022', 'altycj2000@139.com', '城市园林', 20, '1', '0', '2002-03-10 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('姜子牙', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');
mysql> create index idx_user_name on tb_user(name);
Query OK, 0 rows affected (1.37 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from tb_user;
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.13 sec)
mysql> show index from tb_user\G;
*************************** 1. row ***************************
Table: tb_user
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: id
Collation: A
Cardinality: 24
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
*************************** 2. row ***************************
Table: tb_user
Non_unique: 1
Key_name: idx_user_name
Seq_in_index: 1
Column_name: name
Collation: A
Cardinality: 24
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
2 rows in set (0.01 sec)
mysql> create unique index idx_user_phone on tb_user(phone);
Query OK, 0 rows affected (0.46 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from tb_user;
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
3 rows in set (0.08 sec)
mysql>
mysql> create index idx_user_pro_age_sta on tb_user(profession, age,status);
Query OK, 0 rows affected (0.32 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
6 rows in set (0.03 sec)
2.5 性能分析(SQL Performance Analyzer)
我们现在为什么学这个?我们要进行SQL优化!那我对哪一类的SQL优化呢??
- 我们主要优化的就是查询语句!!
2.5.1 SQL执行频率
如果一个数据库的表,只做插入用,那你还优化个der啊!所以要掌握SQL的执行频率,看看以什么为主。增删改为主的话,我的优化就是可以放弃一些。
MySQL客户端连接成功后,通过show [sessionlglobal] status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次:
SHOW GLOBAL STAYUS LIKE 'Com_______';
下划线的个数 表示 几个字符 (模糊匹配)7g个一般是
2.5.2 慢查询日志(当select权重比较高)
那些select的执行效率比较低,那就选择哪一类的
- 慢查询日志记录了所有执行时间超过指定参数(
long_query_time
,单位:秒,默认10秒)的所有SQL语句的日志。 - MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件
(/etc/my.cnf)
中配置如下信息:
通过下面的
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | OFF |
+----------------+-------+
1 row in set (1.24 sec)
#开启MySQL慢日志查询开关
slow_query_log=1
#设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2
配置完毕之后,通过以下指令重新启动MySQL服务器遊行测试,查看慢日志文件中记录的信息/var/lib/mysql/localhost-slow.log
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | OFF |
+----------------+-------+
1 row in set (1.24 sec)
mysql> set global slow_query_log=1
-> ;
Query OK, 0 rows affected (0.14 sec)
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | ON |
+----------------+-------+
1 row in set (0.00 sec)
mysql>
如果可以在mysql数据库里面输入命令 show variables like ‘slow_query_log_file’; 来查找慢查询日志存放地址
mysql> show variables like 'slow_query_log_file';
+---------------------+-------------------------------------------------+
| Variable_name | Value |
+---------------------+-------------------------------------------------+
| slow_query_log_file | /var/lib/mysql/matthew-virtual-machine-slow.log |
+---------------------+-------------------------------------------------+
1 row in set (0.00 sec)
mysql>
matthew@matthew-virtual-machine:~$ cd /var/lib/mysql/matthew-virtual-machine-slow.log
bash: cd: /var/lib/mysql/matthew-virtual-machine-slow.log: 权限不够
matthew@matthew-virtual-machine:~$ sudo passwd root
[sudo] matthew 的密码:
新的 密码:
重新输入新的 密码:
passwd:已成功更新密码
matthew@matthew-virtual-machine:~$ su
密码:
root@matthew-virtual-machine:/home/matthew# cd /var/lib/mysql/matthew-virtual-machine-slow.log
bash: cd: /var/lib/mysql/matthew-virtual-machine-slow.log: 不是目录
root@matthew-virtual-machine:/home/matthew# cd /var/lib/mysql
root@matthew-virtual-machine:/var/lib/mysql# ll
总用量 3424
drwx------ 8 mysql mysql 32 9月 21 16:47 ./
drwxr-xr-x 72 root root 72 9月 21 10:23 ../
-rw-r----- 1 mysql mysql 56 9月 20 15:05 auto.cnf
-rw-r----- 1 mysql mysql 180 9月 20 15:05 binlog.000001
-rw-r----- 1 mysql mysql 404 9月 20 15:05 binlog.000002
-rw-r----- 1 mysql mysql 3612 9月 21 10:57 binlog.000003
-rw-r----- 1 mysql mysql 48 9月 20 15:05 binlog.index
-rw------- 1 mysql mysql 1680 9月 20 15:05 ca-key.pem
-rw-r--r-- 1 mysql mysql 1112 9月 20 15:05 ca.pem
-rw-r--r-- 1 mysql mysql 1112 9月 20 15:05 client-cert.pem
-rw------- 1 mysql mysql 1676 9月 20 15:05 client-key.pem
-rw-r--r-- 1 root root 0 9月 20 15:05 debian-5.7.flag
-rw-r----- 1 mysql mysql 196608 9月 21 10:59 '#ib_16384_0.dblwr'
-rw-r----- 1 mysql mysql 8585216 9月 21 10:57 '#ib_16384_1.dblwr'
-rw-r----- 1 mysql mysql 3426 9月 20 15:05 ib_buffer_pool
-rw-r----- 1 mysql mysql 12582912 9月 21 10:57 ibdata1
-rw-r----- 1 mysql mysql 12582912 9月 20 15:05 ibtmp1
drwxr-x--- 2 mysql mysql 34 9月 21 10:21 '#innodb_redo'/
drwxr-x--- 2 mysql mysql 12 9月 20 15:05 '#innodb_temp'/
-rw-r----- 1 mysql mysql 7 9月 20 15:05 matthew-virtual-machine.pid
-rw-r----- 1 mysql mysql 178 9月 21 16:47 matthew-virtual-machine-slow.log
drwxr-x--- 2 mysql mysql 8 9月 20 15:05 mysql/
-rw-r----- 1 mysql mysql 28311552 9月 21 10:57 mysql.ibd
drwxr-x--- 2 mysql mysql 112 9月 20 15:05 performance_schema/
-rw------- 1 mysql mysql 1676 9月 20 15:05 private_key.pem
-rw-r--r-- 1 mysql mysql 452 9月 20 15:05 public_key.pem
-rw-r--r-- 1 mysql mysql 1112 9月 20 15:05 server-cert.pem
-rw------- 1 mysql mysql 1680 9月 20 15:05 server-key.pem
drwxr-x--- 2 mysql mysql 3 9月 20 15:05 sys/
drwxr-x--- 2 mysql mysql 3 9月 21 10:21 tb_user/
-rw-r----- 1 mysql mysql 16777216 9月 21 10:59 undo_001
-rw-r----- 1 mysql mysql 16777216 9月 21 10:59 undo_002
root@matthew-virtual-machine:/var/lib/mysql#
使用cat matthew-virtual-machine-slow.log
root@matthew-virtual-machine:/var/lib/mysql# cat matthew-virtual-machine-slow.log
/usr/sbin/mysqld, Version: 8.0.34-0ubuntu0.20.04.1 ((Ubuntu)). started with:
Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock
Time Id Command Argument
root@matthew-virtual-machine:/var/lib/mysql#
实时更新日志。
2.5.3 profile详情
刚刚所讲的慢查询日志是我们超过了规定的时间,才会记录下来,假如时间也比较长,但是没超过你设置的值,就显得比较鸡肋了,
profile 详情:show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持
profile操作:
SELECT @@have_profiling;
mysql> SELECT @@have_profiling;
+------------------+
| @@have_profiling |
+------------------+
| YES |
+------------------+
1 row in set, 1 warning (0.05 sec)
mysql>
默认profiling是关闭的,可以通过set语句在session/ global级别开启profiling:
SET profiling= 1;
执行一系列的业务SQL的操作,然后通过如下指令查看指令的执行耗时:
#查看每一条SQL的耗时基本情况
show profiles;
#查看指定query_id的SQL 语句各个阶段的耗时情况
show profile for query query_id;
#查看指定query_id的SQL 语句CPU的使用情况
show profile cpu for query query_id;
输入show profiles;
可以查看所有的查询耗时情况
mysql> show profiles;
+----------+------------+---------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+---------------------------------------------+
| 1 | 0.00061850 | SELECT DATABASE () |
| 2 | 0.00117050 | show databases |
| 3 | 0.00212525 | show tables |
| 4 | 0.00109650 | show tables |
| 5 | 0.00014225 | select * from tb_user |
| 6 | 0.05214250 | select count(*) from tb_user |
| 7 | 0.00017600 | select * from tb_user |
| 8 | 0.07077425 | select count(*) from tb_sku |
| 9 | 0.00008575 | select @@have_ profiling |
| 10 | 0.00014075 | select @@profiling |
| 11 | 0.00009050 | select @@profiling |
| 12 | 0.00020325 | select * from tb_user |
| 13 | 0.00032125 | select * from tb_user where id = 1 |
| 14 | 0.00032425 | select * from tb_user where name = '白起' |
| 15 | 0.00008175 | select count (*) from tb_user |
+----------+------------+---------------------------------------------+
15 rows in set, 1 warning (0.00 sec)
2.5.4 explain详情
我们不能通过单挑语句就评判他的耗时情况,只是粗略的。
explain,来救场!
EXPLAIN或者DESC命令获取MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序。
语法:
#直接在select语句之前加上关键字explain / desc
EXPLAIN SELECT 字段列表 FROM 表名WHERE 条件;
mysql> select * from tb_user where id = 1;
+----+--------+-------------+-----------------+--------------+------+--------+--------+---------------------+
| id | name | phone | email | profession | age | gender | status | createtime |
+----+--------+-------------+-----------------+--------------+------+--------+--------+---------------------+
| 1 | 吕布 | 17799990000 | lvbu666@163.com | 软件工程 | 23 | 1 | 6 | 2001-02-02 00:00:00 |
+----+--------+-------------+-----------------+--------------+------+--------+--------+---------------------+
1 row in set (0.07 sec)
mysql> explain select * from tb_user where id = 1;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.06 sec)
EXPLAIN执行计划各字段含义:
- Id
- select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上到下; id不同,值越大,越先执行)。
对于下面的id演示 需要用多表查询!!!!
select s.*,c.* from student s, course c, student_ course sc where s.id = sc.studentid and c.id = sc.courseid;
explian select s.*,c.* from student s, course c, student_ course sc where s.id = sc.studentid and c.id = sc.courseid;
explain select * from student s where s.id in (select studentid from student_course sc where sc.courseid=(select id from course c where c.name = 'MySQL'));
-
select_ type
- 表示SELECT的类型,常见的取值有SIMPLE ( 简单表,即不使用表连接或者子查询)、PRIMARY (主查询,即外层的查询)、UNION (UNION中的第二个或者后面的查询语句)、SUBQUERY (SELECT/WHERE之后包含了子查询)等
-
type
- 表示连接类型,性能由好到差的连接类型为NULL、system、 const、 eq_ref、 ref、range、 index、all。
NULL的性能最好,但是一般的业务查询 不会用到NULL NULL是再不会用到任何表的时候。
-
possible_key
- 显示可能应用在这张表上的索引,一个或多个。
-
key
- 实际使用的索引,如果为NULL,则没有使用索引。
-
key_len
- 表示索引中使用的字节数,该值为索引字段最大可能长度, 并非实际使用长度,在不损失精确性的前提下,长度越短越好。
-
rows
- MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。
-
filtered
- 表示返回结果的行数占需读取行数的百分比,filtered 的值越大越好。
2.6 索引使用规则(index usage)
2.6.1 验证索引效率
在未建立索引之前,执行如下SQL语句,查看SQL的耗时。
对比一下:
SELECT * FROM tb_sku WHERE id= '1';
SELECT * FROM tb_sku WHERE sn= '100000003145001';
针对字段创建索引
create index idx_sku_sn on tb_sku(sn) ;
然后再次执行相同的SQL语句,再次查看SQL的耗时。
SELECT * FROM tb_sku WHERE sn = '100000003145001';
2.6.2 最左前缀法则
- 如果索引了多列(联合索引), 要遵守最左前缀法则。
- 最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引|中的列。
- 如果跳跃某一列, 索弓|将部分失效(店面的字段索弓|失效)。
explain select * from tb_user where pratfession= '软件工程' and age = 31 and status= '0';
explain select * from tb_user where profession=软件工程' and age = 31;
explain select * from tb_user where profession= '软件工程' ;
explain select * from tb_user where age = 31 and status= '0';
explain select * from tb_user where status= '0';
现在表的结构上是这样的
select * from tb_user;
mysql> select * from tb_user;
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| id | name | phone | email | profession | age | gender | status | createtime |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| 1 | 吕布 | 17799990000 | lvbu666@163.com | 软件工程 | 23 | 1 | 6 | 2001-02-02 00:00:00 |
| 2 | 曹操 | 17799990001 | caocao666@qq.com | 通讯工程 | 33 | 1 | 0 | 2001-03-05 00:00:00 |
| 3 | 赵云 | 17799990002 | 17799990@139.com | 英语 | 34 | 1 | 2 | 2002-03-02 00:00:00 |
| 4 | 孙悟空 | 17799990003 | 17799990@sina.com | 工程造价 | 54 | 1 | 0 | 2001-07-02 00:00:00 |
| 5 | 花木兰 | 17799990004 | 19980729@sina.com | 软件工程 | 23 | 2 | 1 | 2001-04-22 00:00:00 |
| 6 | 大乔 | 17799990005 | daqiao666@sina.com | 舞蹈 | 22 | 2 | 0 | 2001-02-07 00:00:00 |
| 7 | 露娜 | 17799990006 | luna_love@sina.com | 应用数学 | 24 | 2 | 0 | 2001-02-08 00:00:00 |
| 8 | 程咬金 | 17799990007 | chengyaojin@163.com | 化工 | 38 | 1 | 5 | 2001-05-23 00:00:00 |
| 9 | 项羽 | 17799990008 | xiaoyu666@qq.com | 金属材料 | 43 | 1 | 0 | 2001-09-18 00:00:00 |
| 10 | 白起 | 17799990009 | baiqi666@sina.com | 机械工程及其自动化 | 27 | 1 | 2 | 2001-08-16 00:00:00 |
| 11 | 韩信 | 17799990010 | hanxin520@163.com | 无机非金属材料工程 | 27 | 1 | 0 | 2001-06-12 00:00:00 |
| 12 | 荆轲 | 17799990011 | jingke123@163.com | 会计 | 29 | 1 | 0 | 2001-05-11 00:00:00 |
| 13 | 兰陵王 | 17799990012 | lanlinwang666@126.com | 工程造价 | 44 | 1 | 1 | 2001-04-09 00:00:00 |
| 14 | 狂铁 | 17799990013 | kuangtie@sina.com | 应用数学 | 43 | 1 | 2 | 2001-04-10 00:00:00 |
| 15 | 貂蝉 | 17799990014 | 84958948374@qq.com | 软件工程 | 40 | 2 | 3 | 2001-02-12 00:00:00 |
| 16 | 妲己 | 17799990015 | 2783238293@qq.com | 软件工程 | 31 | 2 | 0 | 2001-01-30 00:00:00 |
| 17 | 芈月 | 17799990016 | xiaomin2001@sina.com | 工业经济 | 35 | 2 | 0 | 2000-05-03 00:00:00 |
| 18 | 嬴政 | 17799990017 | 8839434342@qq.com | 化工 | 38 | 1 | 1 | 2001-08-08 00:00:00 |
| 19 | 狄仁杰 | 17799990018 | jujiamlm8166@163.com | 国际贸易 | 30 | 1 | 0 | 2007-03-12 00:00:00 |
| 20 | 安琪拉 | 17799990019 | jdodm1h@126.com | 城市规划 | 51 | 2 | 0 | 2001-08-15 00:00:00 |
| 21 | 典韦 | 17799990020 | ycaunanjian@163.com | 城市规划 | 52 | 1 | 2 | 2000-04-12 00:00:00 |
| 22 | 廉颇 | 17799990021 | lianpo321@126.com | 土木工程 | 19 | 1 | 3 | 2002-07-18 00:00:00 |
| 23 | 后羿 | 17799990022 | altycj2000@139.com | 城市园林 | 20 | 1 | 0 | 2002-03-10 00:00:00 |
| 24 | 姜子牙 | 17799990023 | 37483844@qq.com | 工程造价 | 29 | 1 | 4 | 2003-05-26 00:00:00 |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
24 rows in set (0.00 sec)
show index from tb_user;
mysql> show index from tb_user;
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
3 rows in set (0.12 sec)
软件工程
mysql> explain select * from tb_user where profession= '软件工程' ;
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta | idx_user_pro_age_sta | 47 | const | 4 | 100.00 | NULL |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from tb_user where age = 31 and status= '0';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | NULL | NULL | NULL | NULL | 24 | 4.17 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql>
●范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效
explain select * from tb_user where profession= '软件工程' and age> 30 and status= '0';
explain select * from tb_user where profession= '软件工程' and age>= 30 and status= '0';
mysql> explain select * from tb_user where profession= '软件工程' and age> 30 and status= '0';
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | tb_user | NULL | range | idx_user_pro_age_sta | idx_user_pro_age_sta | 49 | NULL | 2 | 10.00 | Using index condition |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.02 sec)
mysql> explain select * from tb_user where profession= '软件工程' and age>= 30 and status= '0';
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | tb_user | NULL | range | idx_user_pro_age_sta | idx_user_pro_age_sta | 54 | NULL | 2 | 10.00 | Using index condition |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
2.6.3 索引失效情况一
- 索引列运算
不要在索引列上进行运算操作,索引将失效。
explain select * from tb_user where phone= '17799990015';
+----+-------------+---------+------------+-------+----------------+----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+----------------+----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | const | idx_user_phone | idx_user_phone | 46 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+-------+----------------+----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
explain select * from tb_user where substring(phone, 10, 2)='15';
mysql> explain select * from tb_user where substring(phone, 10, 2)='15';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | NULL | NULL | NULL | NULL | 24 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.10 sec)
全表扫描!!!!
- 字符串不加引号
字符串类型的字段使用时,不加引号,索引将失效。
explain select * from tb_user where phone ='17799990015' ;
explain select * from tb_user where phone= 17799990015 ;
mysql> explain select * from tb_user where phone ='17799990015' ;
+----+-------------+---------+------------+-------+----------------+----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+----------------+----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | const | idx_user_phone | idx_user_phone | 46 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+-------+----------------+----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from tb_user where phone= 17799990015 ;
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | idx_user_phone | NULL | NULL | NULL | 24 | 10.00 | Using where |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
1 row in set, 3 warnings (0.03 sec)
- 模糊查询
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。(百分号)
explain select * from tb_user where profession like '软件%';
走索引
explain select * from tb_user where profession like '%工程';
不走索引
2.6.4 索引失效情况二
- or连接条件
用or分割开的条件,如果or前的条件 中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
explain select* from tb_user where id= 10 or age = 23;
explain select * from tb_user where phone = '17799990017' or age = 23;
mysql> explain select* from tb_user where id= 10 or age = 23;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | PRIMARY | NULL | NULL | NULL | 24 | 13.75 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
mysql> explain select * from tb_user where phone = '17799990017' or age = 23;
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | idx_user_phone | NULL | NULL | NULL | 24 | 13.75 | Using where |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
由于age没有索引,所以即使id、phone有索引,索引也会失效。所以需要针对于age也要建立索引。
create index idx_user_age on tb_user (age);
mysql> explain select * from tb_user where id= 10 or age = 23;
+----+-------------+---------+------------+-------------+----------------------+----------------------+---------+------+------+----------+------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------------+----------------------+----------------------+---------+------+------+----------+------------------------------------------------+
| 1 | SIMPLE | tb_user | NULL | index_merge | PRIMARY,idx_user_age | PRIMARY,idx_user_age | 4,2 | NULL | 3 | 100.00 | Using union(PRIMARY,idx_user_age); Using where |
+----+-------------+---------+------------+-------------+----------------------+----------------------+---------+------+------+----------+------------------------------------------------+
1 row in set, 1 warning (0.02 sec)
索引使用
数据分布影响
如果MySQL评估使用索引比全表更慢,则不使用索引。
mysql> explain select * from tb_user where phone>='17799990020';
+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | tb_user | NULL | range | idx_user_phone | idx_user_phone | 46 | NULL | 4 | 100.00 | Using index condition |
+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
全表扫描
mysql> explain select * from tb_user where phone>='17799990000';
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | idx_user_phone | NULL | NULL | NULL | 24 | 100.00 | Using where |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.02 sec)
mysql> explain select * from tb_user where phone>='17799990010';
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | idx_user_phone | NULL | NULL | NULL | 24 | 58.33 | Using where |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
profession 是否是null or not null——走全表扫描还是索引?不一定
mysql> explain select * from tb_user where profession is null;
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta | idx_user_pro_age_sta | 47 | const | 1 | 100.00 | Using index condition |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from tb_user where profession is not null;
+----+-------------+---------+------------+------+----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | idx_user_pro_age_sta | NULL | NULL | NULL | 24 | 100.00 | Using where |
+----+-------------+---------+------------+------+----------------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
2.6.5 SQL 提示
mysql> select * from tb_user;
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| id | name | phone | email | profession | age | gender | status | createtime |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| 1 | 吕布 | 17799990000 | lvbu666@163.com | 软件工程 | 23 | 1 | 6 | 2001-02-02 00:00:00 |
| 2 | 曹操 | 17799990001 | caocao666@qq.com | 通讯工程 | 33 | 1 | 0 | 2001-03-05 00:00:00 |
| 3 | 赵云 | 17799990002 | 17799990@139.com | 英语 | 34 | 1 | 2 | 2002-03-02 00:00:00 |
| 4 | 孙悟空 | 17799990003 | 17799990@sina.com | 工程造价 | 54 | 1 | 0 | 2001-07-02 00:00:00 |
| 5 | 花木兰 | 17799990004 | 19980729@sina.com | 软件工程 | 23 | 2 | 1 | 2001-04-22 00:00:00 |
| 6 | 大乔 | 17799990005 | daqiao666@sina.com | 舞蹈 | 22 | 2 | 0 | 2001-02-07 00:00:00 |
| 7 | 露娜 | 17799990006 | luna_love@sina.com | 应用数学 | 24 | 2 | 0 | 2001-02-08 00:00:00 |
| 8 | 程咬金 | 17799990007 | chengyaojin@163.com | 化工 | 38 | 1 | 5 | 2001-05-23 00:00:00 |
| 9 | 项羽 | 17799990008 | xiaoyu666@qq.com | 金属材料 | 43 | 1 | 0 | 2001-09-18 00:00:00 |
| 10 | 白起 | 17799990009 | baiqi666@sina.com | 机械工程及其自动化 | 27 | 1 | 2 | 2001-08-16 00:00:00 |
| 11 | 韩信 | 17799990010 | hanxin520@163.com | 无机非金属材料工程 | 27 | 1 | 0 | 2001-06-12 00:00:00 |
| 12 | 荆轲 | 17799990011 | jingke123@163.com | 会计 | 29 | 1 | 0 | 2001-05-11 00:00:00 |
| 13 | 兰陵王 | 17799990012 | lanlinwang666@126.com | 工程造价 | 44 | 1 | 1 | 2001-04-09 00:00:00 |
| 14 | 狂铁 | 17799990013 | kuangtie@sina.com | 应用数学 | 43 | 1 | 2 | 2001-04-10 00:00:00 |
| 15 | 貂蝉 | 17799990014 | 84958948374@qq.com | 软件工程 | 40 | 2 | 3 | 2001-02-12 00:00:00 |
| 16 | 妲己 | 17799990015 | 2783238293@qq.com | 软件工程 | 31 | 2 | 0 | 2001-01-30 00:00:00 |
| 17 | 芈月 | 17799990016 | xiaomin2001@sina.com | 工业经济 | 35 | 2 | 0 | 2000-05-03 00:00:00 |
| 18 | 嬴政 | 17799990017 | 8839434342@qq.com | 化工 | 38 | 1 | 1 | 2001-08-08 00:00:00 |
| 19 | 狄仁杰 | 17799990018 | jujiamlm8166@163.com | 国际贸易 | 30 | 1 | 0 | 2007-03-12 00:00:00 |
| 20 | 安琪拉 | 17799990019 | jdodm1h@126.com | 城市规划 | 51 | 2 | 0 | 2001-08-15 00:00:00 |
| 21 | 典韦 | 17799990020 | ycaunanjian@163.com | 城市规划 | 52 | 1 | 2 | 2000-04-12 00:00:00 |
| 22 | 廉颇 | 17799990021 | lianpo321@126.com | 土木工程 | 19 | 1 | 3 | 2002-07-18 00:00:00 |
| 23 | 后羿 | 17799990022 | altycj2000@139.com | 城市园林 | 20 | 1 | 0 | 2002-03-10 00:00:00 |
| 24 | 姜子牙 | 17799990023 | 37483844@qq.com | 工程造价 | 29 | 1 | 4 | 2003-05-26 00:00:00 |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
24 rows in set (0.00 sec)
mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_age | 1 | age | A | 19 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
7 rows in set (0.04 sec)
建立单列索引
mysql> create index idx_user_pro on tb_user(profession);
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from tb_user where profession= '软件工程' ;
+----+-------------+---------+------------+------+-----------------------------------+----------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+-----------------------------------+----------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta,idx_user_pro | idx_user_pro_age_sta | 47 | const | 4 | 100.00 | NULL |
+----+-------------+---------+------------+------+-----------------------------------+----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
我还是选择联合索引,是MySQL优化器自动的选择的结果。——引出SQL提示!
SQL提示
SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
use index:
explain select * from tb_user use index(idx_user_pro) where profession= '软件工程';
ignore index: .
explain select * from tb_user ignore index(idx_user_pro) where profession='软件工程';
force index:
explain select * from tb_user force index(idx_user_pro) where profession= '软件工程';
mysql> explain select * from tb_user use index(idx_user_pro) where profession= '软件工程';
+----+-------------+---------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro | idx_user_pro | 47 | const | 4 | 100.00 | NULL |
+----+-------------+---------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from tb_user ignore index(idx_user_pro) where profession='软件工程';
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta | idx_user_pro_age_sta | 47 | const | 4 | 100.00 | NULL |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
mysql> explain select * from tb_user force index(idx_user_pro) where profession= '软件工程';
+----+-------------+---------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro | idx_user_pro | 47 | const | 4 | 100.00 | NULL |
+----+-------------+---------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
2.6.6 覆盖索引&回表查询
覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到) ,减少select *
。
清理一下:
mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_age | 1 | age | A | 19 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
8 rows in set (0.02 sec)
mysql> drop index idx_user_age on tb_user;
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> drop index idx_user_pro on tb_user;
Query OK, 0 rows affected (0.12 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
6 rows in set (0.00 sec)
观察下面执行计划的区别:
mysql> explain select id,profession from tb_user where profession = '软件工程' and age=31 and status='0';
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta | idx_user_pro_age_sta | 54 | const,const,const | 1 | 100.00 | Using where; Using index |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select id,profession,age from tb_user where profession = '软件工程' and age=31 and status='0';
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta | idx_user_pro_age_sta | 54 | const,const,const | 1 | 100.00 | Using where; Using index |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select id,profession,age,status from tb_user where profession = '软件工程' and age=31 and status='0';
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta | idx_user_pro_age_sta | 54 | const,const,const | 1 | 100.00 | Using where; Using index |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select id,profession,age,status,name from tb_user where profession = '软件工程' and age=31 and status='0';
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+-----------------------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_user_pro_age_sta | idx_user_pro_age_sta | 54 | const,const,const | 1 | 100.00 | Using index condition |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------------------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
观察Extra这一列:
Using where; Using index
的性能比Using index condition
高!
知识小贴士A
- using index condition:查找使用了索引,但是需要回表查询数据
- using where; using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据
覆盖索引:
回表查询:
2.6.7 前缀索引
前缀索引的定义
当字段类型为字符串(varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
语法:
create index idx_XXX on table_name (column(n));
前缀长度
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
select count(distinct email)/count(*) from tb_user ;
select count(distinct substring(email,1,5))/count(*) from tb_user ;
查询表中的数据个数
mysql> select count(*) from tb_user;
+----------+
| count(*) |
+----------+
| 24 |
+----------+
1 row in set (0.03 sec)
查询表中不为空的个数
mysql> select count(email) from tb_user;
+--------------+
| count(email) |
+--------------+
| 24 |
+--------------+
1 row in set (0.00 sec)
查询重复的个数
mysql> select count(distinct email) from tb_user;
+-----------------------+
| count(distinct email) |
+-----------------------+
| 24 |
+-----------------------+
1 row in set (0.00 sec)
mysql> select count(distinct email)/count(*) from tb_user;
+--------------------------------+
| count(distinct email)/count(*) |
+--------------------------------+
| 1.0000 |
+--------------------------------+
1 row in set (0.02 sec)
mysql> select count(distinct substring(email,1,10))/count(*) from tb_user;
+------------------------------------------------+
| count(distinct substring(email,1,10))/count(*) |
+------------------------------------------------+
| 1.0000 |
+------------------------------------------------+
1 row in set (0.00 sec)
mysql> select count(distinct substring(email,1,8))/count(*) from tb_user;
+-----------------------------------------------+
| count(distinct substring(email,1,8))/count(*) |
+-----------------------------------------------+
| 0.9583 |
+-----------------------------------------------+
1 row in set (0.00 sec)
mysql> select count(distinct substring(email,1,5))/count(*) from tb_user;
+-----------------------------------------------+
| count(distinct substring(email,1,5))/count(*) |
+-----------------------------------------------+
| 0.9583 |
+-----------------------------------------------+
1 row in set (0.00 sec)
建立前缀索引(前五个字符串)
mysql> create index idx_email_5 on tb_user(email(5));
Query OK, 0 rows affected (0.16 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_email_5 | 1 | email | A | 23 | 5 | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
7 rows in set (0.00 sec)
查询——使用了
mysql> explain select * from tb_user where email='daqiao666@sina.com';
+----+-------------+---------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ref | idx_email_5 | idx_email_5 | 23 | const | 1 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
不是直接返回,因为我们只查询了前五个字符串,要将返回的值进行与lvbu666@163.com 对比,如果是那就返回,如果不是!!!!二级索引按照链表往下找。
2.6.8 单列&联合索引
- 单列索引与联合索引
单列索引:即一个索引只包含单个列。
联合索引:即一个索引包含了多个列。
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。
多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询。
2.7 索引设计原则(Index design principles)
- 针对于数据量较大,且查询比较频繁的表建立索引。
- 针对于常作为查询条件(where) 、排序(order by)、分组(group by)操作的字段建立索引。
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
- 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。