MySQL 索引优化(一)

索引创建原则

  1. 先写代码,再建索引
    不应该在创建完表之后立马就创建索引,等主体业务开发完毕以后,再把涉及到该表的 sql 分析过后再建立索引。
  2. 联合索引尽量覆盖查询条件
    在设计一个联合索引的时候,让联合索引尽可能的包含 sql 语句中的 whereorder bygroup by 的字段,还要确保这些字段尽量满足 sql 最左前缀原则。
  3. 选取索引字段选值比较多、区分度高的字段
    比如我有一张用户表,它有一个性别字段,只包含男女两种值,这就没有办法进行快速的二分查找,不如全表扫描,索引也就失去意义了。建立索引,尽量选取值比较多的字段,才能更好的发挥 B+ 树二分查找的优势。
  4. 长字符串可以采用前缀索引
    选取索引尽量对索引字段类型较小的列设计索引,字段小,占用磁盘空间也小,搜索的时候性能也会好一些。如果需要给长字符串建立索引,比如 varchar(255),比如这时就可以针对这个字段的前 20 歌字符建立索引,也就是说,把这个字段每个值的前面 20 位放到索引树里,比如 KEY index(name(20),age,position)。
  5. where 和 order by 冲突时优先 where
    在 where 和 order by 出现索引设计冲突时,优先让 where 条件去使用索引快速筛选出一部分数据,再进行排序。因为大多数情况下,基于索引进行 where 筛选往往可以最快速筛选出需要的少部分数据,再进行排序的成本可能就会降低很多。
  6. 数据量足够大的情况下再建立索引
    一般单表超过十万条数据以后,为了改善用户体验,再建立索引。

索引优化案例演示

示例表结构和数据

CREATE TABLE `student` (`id` INT(11) NOT NULL AUTO_INCREMENT,`name` VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名',`age` INT(11) NOT NULL DEFAULT '0' COMMENT '年龄',`school` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '学校名',`start_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入学时间',PRIMARY KEY (`id`),KEY `idx_name_age_school` (`name`,`age`,`school`) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='学生表';INSERT INTO student(NAME,age,school,start_time) VALUES('老大',11,'老大小学',NOW());
INSERT INTO student(NAME,age,school,start_time) VALUES('老二', 12,'老大小学',NOW());
INSERT INTO student(NAME,age,school,start_time) VALUES('老三',13,'老大小学',NOW());DROP PROCEDURE IF EXISTS insert_student;
DELIMITER ;;
CREATE PROCEDURE insert_student()
BEGINDECLARE i INT;SET i=1;WHILE(i<=100000)DOINSERT INTO student(NAME,age,school) VALUES(CONCAT('老',i),i,'老大小学');SET i=i+1;END WHILE;
END;;
DELIMITER ;
CALL insert_student();

覆盖查询优化

第一个例子,联合索引的首个字段用范围查询不会走索引

EXPLAIN SELECT * FROM student WHERE NAME > '老1' AND age = 11 AND school='老大小学';
    id  select_type  table    partitions  type    possible_keys          key     key_len  ref       rows  filtered  Extra        
------  -----------  -------  ----------  ------  ---------------------  ------  -------  ------  ------  --------  -------------1  SIMPLE       student  (NULL)      ALL     idx_name_age_position  (NULL)  (NULL)   (NULL)   97664      0.50  Using where  

从上面的输出结果可以看得到,没有使用索引,mysql 内部可能判断结果集很大,回表效率不高,直接使用全表扫描,我们再来看强制使用索引的情况:

EXPLAIN SELECT * FROM student FORCE INDEX(idx_name_age_school) WHERE NAME > '老1' AND age = 11 AND school='老大小学';
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra                  
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  -----------------------1  SIMPLE       student  (NULL)      range   idx_name_age_school  idx_name_age_school  74       (NULL)   48832      1.00  Using index condition  

看似结果集数量少了,但是回表查询效率不高,所以最重的查询效率不一定比全表扫描高,可以自己尝试一下这两条语句,看看执行时间的区别,我就不截图了。

使用覆盖索引查询优化那条查询语句:

EXPLAIN SELECT NAME, age, school FROM student WHERE NAME > '老1' AND age = 11 AND school='老大小学';
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra                     
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  --------------------------1  SIMPLE       student  (NULL)      range   idx_name_age_school  idx_name_age_school  74       (NULL)   48832      1.00  Using where; Using index  

可以看到我们使用了 idx_name_age_school 索引,查询的数目也少了,extra 列也显示出使用了索引,提高了查询速度。

like kk%一般会使用索引

EXPLAIN SELECT * FROM student_copy WHERE NAME LIKE '老%' AND age = 11 AND school='老大小学';
    id  select_type  table         partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra                  
------  -----------  ------------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  -----------------------1  SIMPLE       student_copy  (NULL)      range   idx_name_age_school  idx_name_age_school  140      (NULL)       3     33.33  Using index condition  

可以看到在 key 的字段里,使用了索引,这个主要是用到了索引下推优化。

正常联合索引(name, age, school)是按照最左原则,上面这个查询语句会查询 name 字段的索引,根据 name 字段过滤完,得到的索引行里的 age 和 school 是无序的,没有办法很好的利用。MySQL 5.6 之前的版本,这个查询语句只能查询名字是开头的索引,然后拿着这些索引的主键一个个回表,到主键索引上找出相应的记录,再对比 age 和 school 这两个字段是否符合。

MySQL 5.6 引入索引下推,可以在遍历索引过程中,对索引中包含的所有字段先判断,过滤掉不符合的记录后再进行回表,能够有效的减少回表次数。使用索引下推优化后,上面那个查询在联合索引里匹配开头的索引之后,同时也会在索引里过滤 age 和 school 两个字段,拿着过滤完剩下的索引对应的主键 id 再回表查整行数据。

Order by 的优化

EXPLAIN SELECT * FROM student WHERE NAME = '老1' AND school = '老大小学' ORDER BY age;
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra                  
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  -----------------------1  SIMPLE       student  (NULL)      ref     idx_name_age_school  idx_name_age_school  74       const        1     10.00  Using index condition  

利用最左前缀原则,查询匹配的条件中并不包含 age 字段,只有 name 和 school 两个条件,key_len=74,可以看到 Extra 字段的结果是 Using index condition 而不是 Using filesort。

EXPLAIN SELECT * FROM student WHERE NAME = '老1' ORDER BY school;
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra           
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  ----------------1  SIMPLE       student  (NULL)      ref     idx_name_age_school  idx_name_age_school  74       const        1    100.00  Using filesort  

看结果 key_len=74,查询使用了 name 索引,由于用了 school 进行排序,跳过了age,extra 结果 Using filesort。

对比下一条语句

EXPLAIN SELECT * FROM student WHERE NAME = '老1' ORDER BY age, school;
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra   
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  --------1  SIMPLE       student  (NULL)      ref     idx_name_age_school  idx_name_age_school  74       const        1    100.00  (NULL)                                                                                                                                        

查询只用了索引 name,age 和 school 用于排序,没有 Using filesort。

将 age 和 school 调换

EXPLAIN SELECT * FROM student WHERE NAME = '老1' ORDER BY school, age;
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra           
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  ----------------1  SIMPLE       student  (NULL)      ref     idx_name_age_school  idx_name_age_school  74       const        1    100.00  Using filesort  

又出现了 Using filesort,因为索引的创建顺序是 name,age,school,但是排序的顺序颠倒了,不符合最左前缀原则, 又使用 Using filesort。

EXPLAIN SELECT * FROM student WHERE NAME = '老1' AND age = 11 ORDER BY school, age;
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref            rows  filtered  Extra   
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  -----------  ------  --------  --------1  SIMPLE       student  (NULL)      ref     idx_name_age_school  idx_name_age_school  78       const,const       1    100.00  (NULL)                                                                                                                                          

虽然排序的字段与索引顺序还是不一样,但 age 是常量,在排序中被优化了,所以索引未颠倒,不会出现 Using filesort。

EXPLAIN SELECT * FROM student WHERE NAME = '老1' ORDER BY age ASC, school DESC;
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra           
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  ----------------1  SIMPLE       student  (NULL)      ref     idx_name_age_school  idx_name_age_school  74       const        1    100.00  Using filesort  

虽然排序的字段和索引顺序一样了,但在排序的时候 age 正序,school 倒序,这与索引中的排序方式不同,从而产生 Using file sort。

EXPLAIN SELECT * FROM student WHERE NAME > '老1' ORDER BY NAME;
    id  select_type  table    partitions  type    possible_keys        key     key_len  ref       rows  filtered  Extra                        
------  -----------  -------  ----------  ------  -------------------  ------  -------  ------  ------  --------  -----------------------------1  SIMPLE       student  (NULL)      ALL     idx_name_age_school  (NULL)  (NULL)   (NULL)   97664     50.00  Using where; Using filesort  

使用范围查找的时候,这种情况可以通过使用覆盖索引优化,避免 Using filesort

EXPLAIN SELECT NAME, age, school FROM student WHERE NAME > '老1' ORDER BY NAME;
    id  select_type  table    partitions  type    possible_keys        key                  key_len  ref       rows  filtered  Extra                     
------  -----------  -------  ----------  ------  -------------------  -------------------  -------  ------  ------  --------  --------------------------1  SIMPLE       student  (NULL)      range   idx_name_age_school  idx_name_age_school  74       (NULL)   48832    100.00  Using where; Using index  

Group by 和 Order by 类似的,以上的情况都差不多,实质是先排序后分组,遵照索引创建顺序的最左前缀原则。对于 Group by 的优化如果不需要排序的可以加上 Order by null 禁止排序。能写在 where 中的不要用 having。

order by 总结

MySQL 支持用两种方式排序,filesort 和 index。Using index 指的是只需要扫描索引本身就能完成排序。index效率高,filesort 效率低。

order by 满足两种情况会使用 Using index。

  1. order by 语句满足最左前缀原则。
  2. where 子句和 order by 子句组合使用最左前缀原则。
    尽量在索引列上完成排序,遵循索引建立时的最左前缀原则。
    能用覆盖索引尽量用覆盖索引。

总结

这篇文章包含了建立索引的原则,联合索引查询优化,like XX% 类型查询以及 order by、group by 查询的优化。最重要的部分还是最左前缀原则的理解,如果把最左前缀原则理解通透,这些优化都会融会贯通。下一篇继续索引优化。

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

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

相关文章

rtph264depay插件分析笔记

1、rtp协议头 2、rtp可以基于TCP或者UDP 其中基于TCP需要加4个字节的RTP标志 3、rtph264depay定义解析函数gst_rtp_h264_depay_process&#xff0c;通过RFC 3984文档实现。 static void gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass) {GObjectClass *gobject…

AI资讯2024-04-02 | 前微软副总裁姜大昕携「阶跃星辰」入场,出手即万亿参数大模型!

关注文章底部公众号获取每日AI新闻,以及各种好玩的黑科技,如AI换脸,AI数字人,AI生成视频等工具 阶跃星辰发布万亿参数大模型 终于!国内大模型创业公司最后一位强实力玩家入场——阶跃星辰。它是由微软前全球副总裁姜大昕所创办,公司名称也来源于,发了三个大模型:Step-…

当msvcp120.dll文件找不到了要怎么解决?教你靠谱的3种修复msvcp120.dll方法

当出现msvcp120.dll文件丢失的问题时&#xff0c;不用担心&#xff0c;这是一个常见的情况。在日常使用电脑时&#xff0c;误删或受到计算机病毒影响都可能导致这个问题。为了解决这个问题&#xff0c;今天我们将向大家介绍正确的msvcp120.dll修复方法。 一.msvcp120.dll文件是…

体验OceanBase 的binlog service

OceanBase对MySQL具备很好的兼容性。目前&#xff0c;已经发布了开源版的binlog service工具&#xff0c;该工具能够将OceanBase特有的clog模式转换成binlog模式&#xff0c;以便下游工具如canal、flink cdc等使用。今天&#xff0c;我们就来简单体验一下这个binlog service的功…

RA8889/RA8876显示自定义ASCII字符方法

本文介绍用户自己生成的ASCII字库如何通过RA8889/RA8876显示到液晶屏上。 先上一张实例效果图&#xff1a; 再上程序代码&#xff1a; int main(void) {unsigned short x,y;/* System Clocks Configuration */RCC_Configuration(); delay_init(72); GPIO_Configuration(); …

转圈游戏(acwing)

题目描述&#xff1a; n 个小伙伴&#xff08;编号从 0 到 n−1&#xff09;围坐一圈玩游戏。 按照顺时针方向给 n 个位置编号&#xff0c;从 0 到 n−1。 最初&#xff0c;第 0 号小伙伴在第 0 号位置&#xff0c;第 1 号小伙伴在第 1 号位置&#xff0c;…

Redis面试题10道

1、什么是 Redis&#xff1f;简述它的优缺点&#xff1f; Redis 的全称是&#xff1a;Remote Dictionary.Server&#xff0c;本质上是一个 Key-Value 类型的内存数据库&#xff0c;很像 memcached&#xff0c;整个数据库统统加载在内存当中进行操作&#xff0c;定期通过异步操作…

前端学习<二>CSS基础——17-CSS3的常见边框汇总

CSS3 常见边框汇总 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><title>CSS3 边框</title><style>body, ul, li, dl, dt, dd, h1, h2, h3, h4, h5 {margin: 0;padding: 0;}​body {background-c…

治愈风景视频素材在哪找?日落风景、伤感风景、江南风景这里都有

在这个视频内容为王的时代&#xff0c;做个爆款视频好比烹饪一道米其林三星级大餐&#xff0c;少了那么一点儿神秘的调料&#xff0c;总觉得差了点味道。我&#xff0c;一个在视频剪辑战场上摸爬滚打多年的老兵&#xff0c;今天就来跟大家分享几个找素材的秘密武器&#xff0c;…

C++_Vector的学习

1.概述 1. vector 是表示可变大小数组的序列容器。 2. vector 采用的连续存储空间来存储元素&#xff0c;可以采用下标对 vector 的元素进行访问&#xff0c;它的大小是可以动态改变的&#xff0c;而且它的大小会被容器自动处理。 3. vector使用动态分配数组来存储它的元素。…

STM32应用开发——使用PWM+DMA驱动WS2812

STM32应用开发——使用PWMDMA驱动WS2812 目录 STM32应用开发——使用PWMDMA驱动WS2812前言1 硬件介绍1.1 WS2812介绍1.1.1 芯片简介1.1.2 引脚描述1.1.3 工作原理1.1.4 时序1.1.5 传输协议 1.2 电路设计 2 软件编程2.1 软件原理2.2 测试代码2.2.1 底层驱动2.2.2 灯效应用 2.3 运…

leetcode二叉树相关题目

目录 二叉树的建立整数数组转二叉树Object数组转二叉树 二叉树的遍历leetcode94.二叉树的中序遍历leetcode144.二叉树的前序遍历 二叉树的建立 整数数组转二叉树 下面只是一个简单的示例&#xff0c;没考虑某个子树为空的情况。把{1, 2, 3, 21, 22, 31, 32} 转变为一个二叉树…

新兴AI技术及其创业机会

量子计算与AI 量子计算是未来计算技术的前沿&#xff0c;它通过量子比特进行信息处理&#xff0c;相较于传统计算机&#xff0c;量子计算在处理复杂问题上有着天然的优势。将量子计算与AI结合&#xff0c;可以极大提升AI模型训练的效率和处理数据的能力。 创业机会&#xff1a…

基于NXP的ISELED开发环境搭建

目录 前言一、获取S32K SDK ISELED driver二、安装S32K SDK ISELED driverISELED文件说明三、ISELED工程介绍方案框图:图形化界面配置注意事项前言 NXP S32K ISELED 32位Arm Cortex-M微控制器 (MCU) 包括一种全新的通信协议 (ISELED),用于控制汽车和工业照明应用中的静态和动…

简述前端开发全景:从基础到未来的语言与趋势解析

前端开发概述 前端开发指的是创建网站或应用程序中用户可以直接看到和与之交互的部分。前端开发的主要目标是通过编写代码来制作用户界面&#xff0c;确保信息以易于理解和互动的方式展示给用户。 HTML和CSS HTML&#xff1a;HTML是构建网页的基础&#xff0c;负责定义网页的…

系统IO函数接口

目录 前言 一. man手册 1.1 man手册如何查询 1.2 man手册基础 二.系统IO函数接口 三.open打开文件夹 3.1 例1 open打开文件 3.2 open打开文件代码 3.3 例2 创建文件 四.write写文件 4.1 write写文件 五. read读文件 5.1 read读文件与偏移 5.2 偏移细节 5.3 read读文件代码 六.复…

3.5网安学习第三阶段第五周回顾(个人学习记录使用)

本周重点 ①SSRF服务器端请求伪造 ②序列化和反序列化 ③Vaudit代码审计 本周主要内容 ①SSRF服务器端请求伪造 一、概述 SSRF: server site request forgery (服务器端请求伪造)。 SSR: 服务端请求&#xff0c;A服务器通过函数向B服务器发送请求。 SSRF发生的前提条件…

Flutter动画笔记---总结

三种情况&#xff1a;第一种情况&#xff1a;隐式动画&#xff0c;全自动动画&#xff08;属于内部封装好了&#xff0c;只需要几行代码&#xff0c;就可以实现非常强大的效果&#xff09;第二种情况&#xff1a;显示动画&#xff0c;手动控制的动画&#xff08;提供自定义选择…

【ARM 嵌入式 C 字符串系列 23 -- 返回从父字符串找到相同子字符串的个数】

请阅读【嵌入式开发学习必备专栏 】 文章目录 返回从父字符串找到相同子字符串的个数 返回从父字符串找到相同子字符串的个数 在 C 语言中&#xff0c;可以编写一个自定义函数来计算一个父字符串&#xff08;haystack&#xff09;中相同子字符串&#xff08;needle&#xff09…

蓝桥杯备考随手记: practise04

问题描述: 小明被不明势力劫持。后莫名其妙被扔到 X 星站再无问津。小明得知每天都有飞船飞往地球&#xff0c;但需要 1 元的船票&#xff0c;而他却身无分文。他决定在 X 星站打工。好心的老板答应包食宿&#xff0c;第 1 天给他 1 元钱。并且&#xff0c;以后的每一天都比前…