MySQL索引优化与查询优化

1. 索引失效案例

MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了访问高效数据的方法,并且加快查询的速度,因此索引对查询的速度有着至关重要的影响。

使用索引可以快速地定位表中的某条记录,从而提高数据库查询的速度,提高数据库的性能。

如果查询时没有使用索引,查询语句就会扫描表中的所有记录。在数据量大的情况下,这样查询的速度会很慢。

大多数情况下都(默认)采用B+树来构建索引。只是空间列类型的索引使用R-树,并且MEMORY表还支持hash索引。

其实,用不用索引,最终都是优化器说了算。优化器是基于什么的优化器?基于cost开销(CostBaseOptimizer),它不是基于规则(Rule-BasedOptimizer),也不是基于语义。怎么样开销小就怎么来。另外,SQL语句是否使用索引,跟数据库版本、数据量、数据选择度都有关系。

1.1 全值匹配我最爱

1.2 最佳左前缀法则

在MySQL建立联合索引时会遵守最佳左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

结论:MySQL可以为多个字段创建索引,一个索引可以包括16个字段。对于多列索引,**过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用。**如果查询条件中没有使用这些字段中第1个字段时,多列(或联合)索引不会被使用。

1.3 主键插入顺序

对于一个使用InnoDB存储引擎的表来说,在我们没有显示的创建索引时,表中的数据实际上都是存储在聚簇索引的叶子节点的。而记录又存储在数据页中的,数据页和记录又是按照记录主键值从小到大的顺序进行排序,所以如果我们插入的记录的主键值是依次增大的话,那我们每插满一个数据页就换到下一个数据页继续插,而如果我们插入的主键值忽小忽大的话,则可能会造成页面分裂和记录移位。

1.4 计算、函数、类型转换(自动或手动)导致索引失效

1.5 类型转换导致索引失效

1.6 范围条件右边的列索引失效

应用开发中范围查询,例如:金额查询,日期查询往往都是范围查询。应将查询条件放置where语句最后。(创建的联合索引中,务必把范围涉及到的字段写在最后)

1.7 不等于(!= 或者<>)索引失效

1.8 is null可以使用索引,is not null无法使用索引

结论:最好在设计数据表的时候就将字段设置为 NOT NULL 约束,比如你可以将INT类型的字段,默认值设置为0。将字符类型的默认值设置为空字符串(‘’)

拓展:同理,在查询中使用not like也无法使用索引,导致全表扫描

1.9 like以通配符%开头索引失效

拓展:Alibaba《Java开发手册》

【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

1.10 OR 前后存在非索引的列,索引失效

在WHERE子句中,如果在OR前的条件列进行了索引,而在OR后的条件列没有进行索引,那么索引会失效。也就是说,OR前后的两个条件中的列都是索引时,查询中才使用索引。

1.11 数据库和表的字符集统一使用utf8mb4

统一使用utf8mb4( 5.5.3版本以上支持)兼容性更好,统一字符集可以避免由于字符集转换产生的乱码。不同的字符集进行比较前需要进行转换会造成索引失效。

2. 关联查询优化

结论1:对于内连接来说,查询优化器可以决定谁来作为驱动表,谁作为被驱动表出现

结论2:对于内连接来讲,如果表的连接条件中只能有一个字段有索引,则有索引的字段所在的表会被作为被驱动表

结论3:对于内连接来说,在两个表的连接条件都存在索引的情况下,会选择小表作为驱动表。小表驱动大表

2.1 Index Nested-Loop Join(索引嵌套循环连接)

Index Nested-Loop Join其优化的思路主要是为了减少内层表数据的匹配次数,所以要求被驱动表上必须有索引才行。

2.2 Block Nested-Loop Join(块嵌套循环连接)

如果存在索引,那么会使用index的方式进行join,如果join的列没有索引,被驱动表要扫描的次数太多了。每次访问被驱动表,其表中的记录都会被加载到内存中,然后再从驱动表中取一条与其匹配,匹配结束后清除内存,然后再从驱动表中加载一条记录,然后把被驱动表的记录再加载到内存匹配,这样周而复始,大大增加了IO的次数。为了减少被驱动表的IO次数,就出现了Block Nested-Loop Join的方式。

不再是逐条获取驱动表的数据,而是一块一块的获取,引入了join buffer缓冲区,将驱动表join相关的部分数据列(大小受join buffer的限制)缓存到join buffer中,然后全表扫描被驱动表,被驱动表的每一条记录一次性和join buffer中的所有驱动表记录进行匹配(内存中操作),将简单嵌套循环中的多次比较合并成一次,降低了被驱动表的访问频率。

2.3 Hash Join

从MySQL的8.0.20版本开始将废弃BNLJ,因为从MySQL8.0.18版本开始就加入了hash join默认都会使用hash join

Nested Loop:对于被连接的数据子集较小的情况下,Nested Loop是个较好的选择。

Hash Join是做大数据集连接时的常用方式,优化器使用两个表中较小(相对较小)的表利用Join Key在内存中建立散列值,然后扫描较大的表并探测散列值,找出与Hash表匹配的行。

这种方式适用于较小的表完全可以放入内存中的情况,这样总成本就是访问两个表的成本之和。

在表很大的情况下并不能完全放入内存,这时优化器会将它分割成若干不同的分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要求有较大的临时段从而尽量提高I/O的性能。

它能够很好的工作于没有索引的大表和并行查询的环境中,并提供最好的性能。Hash Join只能应用于等值连接,这是由Hash的特点决定的。

3. 子查询优化

子查询是 MySQL 的一项重要的功能,可以帮助我们通过一个 SQL语句实现比较复杂的查询。但是,子查询的执行效率不高。原因如下:

① 执行子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表,然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。这样会消耗过多的CPU和IO资源,产生大量的慢查询。

② 子查询的结果集存储的临时表,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响。

③ 对于返回结果集比较大的子查询,其对查询性能的影响也就越大。

在MySQL中,可以使用连接(JOIN)查询来替代子查询。**连接查询不需要建立临时表,其速度比子查询要快,如果查询中使用索引的话,性能就会更好。

结论:尽量不要使用NOT IN 或者 NOT EXISTS,用LEFT JOIN xxx ON xx WHERE xx IS NULL替代

4. 排序优化

SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中 避免全表扫描,在 ORDER BY 子句避免使用 FileSort 排序。当然,某些情况下全表扫描,或者 FileSort 排序不一定比索引慢。但总的来说,我们还是要避免,以提高查询效率。

尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列;如果不同就使用联合索引。

无法使用 Index 时,需要对 FileSort 方式进行调优。

5. GROUP BY优化

group by 使用索引的原则几乎跟order by一致 ,group by 即使没有过滤条件用到索引,也可以直接使用索引。

group by 先排序再分组,遵照索引建的最佳左前缀法则

当无法使用索引列,可以增大max_length_for_sort_data和sort_buffer_size参数的设置

where效率高于having,能写在where限定的条件就不要写在having中了

减少使用order by,和业务沟通能不排序就不排序,或将排序放到程序端去做。Order by、group by、distinct这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。

包含了order by、group by、distinct这些查询的语句,where条件过滤出来的结果集请保持在1000行以内,否则SQL会很慢。

6. 优化分页查询

优化思路一

在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。

EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a
WHERE t.id = a.id;

优化思路二

该方案适用于主键自增的表,可以把Limit 查询转换成某个位置的查询。

EXPLAIN SELECT * FROM student WHERE id > 2000000 LIMIT 10;

7. 优先考虑覆盖索引

7.1 什么是覆盖索引?

理解方式一:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了满足查询结果的数据就叫做覆盖索引。

理解方式二:非聚簇复合索引的一种形式,它包括在查询里的SELECT、JOIN和WHERE子句用到的所有列(即建索引的字段正好是覆盖查询条件中所涉及的字段)。

简单说就是,索引列+主键包含SELECT 到 FROM之间查询的列。

7.2 覆盖索引的利弊

好处:

1. 避免Innodb表进行索引的二次查询(回表)

2. 可以把随机IO变成顺序IO加快查询效率

弊端:

索引字段的维护总是有代价的。因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑了。这是业务DBA,或者称为业务数据架构师的工作。

8. 索引条件下推

8.1 使用前后的扫描过程

在不使用ICP索引扫描的过程:

storage层:只将满足index key条件的索引记录对应的整行记录取出,返回给server层

server 层:对返回的数据,使用后面的where条件过滤,直至返回最后一行。

使用ICP扫描的过程:

storage层:首先将index key条件满足的索引记录区间确定,然后在索引上使用index filter进行过滤。将满足的index filter条件的索引记录才去回表取出整行记录返回server层。不满足index filter条件的索引记录丢弃,不回表、也不会返回server层。

server 层:对返回的数据,使用table filter条件做最后的过滤。

9. 其它查询优化策略

9.1 EXISTS 和 IN 的区分

索引是个前提,其实选择与否还会要看表的大小。你可以将选择的标准理解为小表驱动大表。

9.2 COUNT(*)与COUNT(具体字段)效率

环节1:COUNT(*)和COUNT(1)都是对所有结果进行COUNT,COUNT(*)和COUNT(1)本质上并没有区别(二者执行时间可能略有差别,不过你还是可以把它俩的执行效率看成是相等的)。如果有WHERE子句,则是对所有符合筛选条件的数据行进行统计;如果没有WHERE子句,则是对数据表的数据行数进行统计。

环节2:如果是MyISAM存储引擎,统计数据表的行数只需要O(1)的复杂度,这是因为每张MyISAM的数据表都有一个meta信息存储了row_count值,而一致性则是由表级锁来保证的。

如果是InnoDB存储引擎,因为InnoDB支持事务,采用行级锁和MVCC机制,所以无法像MyISAM一样,维护一个row_count变量,因此需要采用扫描全表,是O(n)的复杂度,进行循环+计数的方式来完成统计。

环节3:在InnoDB引擎中,如果采用COUNT(具体字段)来统计数据行数,要尽量采用二级索引。因为主键采用的索引是聚簇索引,聚簇索引包含的信息多,明显会大于二级索引(非聚簇索引)。对于COUNT(*)和COUNT(1)来说,它们不需要查找具体的行,只是统计行数,系统会自动采用占用空间更小的二级索引来进行统计。

如果有多个二级索引,会使用key_len小的二级索引进行扫描。当没有二级索引的时候,才会采用主键索引来进行统计。

9.3 关于SELECT(*)

在表查询中,建议明确字段,不要使用 * 作为查询的字段列表,推荐使用SELECT <字段列表> 查询。原因:

① MySQL 在解析的过程中,会通过查询数据字典将"*"按序转换成所有列名,这会大大的耗费资源和时间。

② 无法使用覆盖索引

9.4 LIMIT 1 对优化的影响

针对的是会扫描全表的 SQL 语句,如果你可以确定结果集只有一条,那么加上LIMIT 1的时候,当找到一条结果的时候就不会继续扫描了,这样会加快查询速度。

如果数据表已经对字段建立了唯一索引,那么可以通过索引进行查询,不会全表扫描的话,就不需要加上LIMIT 1了。

9.5 多使用COMMIT

只要有可能,在程序中尽量多使用 COMMIT,这样程序的性能得到提高,需求也会因为 COMMIT 所释放的资源而减少。

COMMIT 所释放的资源:

回滚段上用于恢复数据的信息

被程序语句获得的锁

redo / undo log buffer 中的空间

管理上述 3 种资源中的内部花费

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】 shutdown 命令使用

shutdown 命令可以用来进行关机程序&#xff0c;并且在关机以前传送讯息给所有使用者正在执行的程序&#xff0c;shutdown 也可以用来重开机。使用权限&#xff1a;系统管理者。 语法 shutdown [选项] 时间 [警告信息] 命令选项及作用 执行令 man shutdown 执行命令结果 参…

字符串函数的模拟实现

今天我们来了解以下一些字符串函数的模拟实现&#xff1a; strlen strcpy strcat strcmp strlen函数的模拟实现 首先我们转到cplusplus中查找strlen的官方解释&#xff1a; 通过查找我们了解到&#xff0c;strlen是用来测量一个字符串长度的函数&#xff0c;函数的返回值就是…

【六、http】go的http的客户端重定向

一、http的重定向 重定向过程&#xff1a;客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应&#xff0c;则自动再发送一个新的http请求&#xff0c;请求url是新的location地址----》服务器根据此请求寻…

【嵌入式开发工具】STM32+Keil实现软件工程搭建与开发调试

本篇文章介绍了使用Keil来对STM32F103C8芯片进行初始工程搭建&#xff0c;以及开发与工程调试的完整过程&#xff0c;帮助读者能够在实战中体会到Keil这个开发环境的使用方法&#xff0c;了解一个嵌入式工程从无到有的过程&#xff0c;并且具备快速搭建一个全新芯片对应最小软件…

软件测试工作流程

流程体系介绍 在以往的项目工作中&#xff0c;我参与过&#xff0c;需求评审、测试计划制定、测试用例编写、测试用例执行、测试脚本编写、测试脚本的执行&#xff0c;进行回归测试、验收测试、编写阶段性测试报告等工作 需求分析&#xff0c;需求评审&#xff08;RPD、产品原…

【ArcGIS Pro二次开发】(74):Python、C#实现Excel截图导出图片

以村庄规划制图为例&#xff0c;通过对现状和规划用地的统计&#xff0c;生成Excel格式的【空间功能结构调整表】后&#xff0c;需要进一步将表格导出成图片&#xff0c;并嵌入到图集中&#xff0c;这样可以实现全流程不用手动参与&#xff0c;让制图的流程完全自动化。 关于E…

深度学习服务器(Linux)开发环境搭建教程

当你拿到一台服务器的使用权时&#xff0c;最头疼的莫过于登陆服务区并配置开发环境。本文将从0开始&#xff0c;讲述一台刚申请的服务器远程登陆并配置开发环境的全过程。希望对你有所帮助 1.登陆服务器 打开MobaXterm软件&#xff0c;创建一个新的Session&#xff0c;选择S…

Java操作redis常见类型数据存储

目录 一、Java连接Redis 1.1 导入pom依赖 1.2 建立连接 二、Java使用Redis 2.1 字符串 String 2.2 哈希 Hash 2.3 列表 List 2.4 集合 Set 2.5 有序集合 Sorted Set 三、Redis的实际应用场景 一、Java连接Redis redis与mysq都是数据库&#xff0c;java操作redis其实跟…

S32K324 UDS Bootloader开发-下位机篇-Bootload软件(1)

文章目录 前言启动过程Bootloader开发链接文件编译文件跳转函数CAN收发相关发送接收初始化及使能CAN周期函数总结前言 上一篇文章介绍了S32K324 -UDS Bootlodaer开发中的需求,本文根据需求开发Bootloader软件。 本文参考NXP官网的S32K324 UBL,其中有一些Bug,也有一些和上位机…

【算法训练营】最近公共祖先+求最大连续bit数

算法 1.最近公共祖先求最大连续bit数 1.最近公共祖先 题目链接 【题目解析】&#xff1a; 最近公共祖先表示距离两个节点最近的公共父节点&#xff0c;这道题考察二叉树。【解题思路】&#xff1a; 题目所描述的满二叉树如下&#xff1a; 1 / \ 2 3 / \ / \ 4 5 6 7 上述树中…

英语——分享篇——每日200词——1-200

1——ball——[bɔːl]——n.球——ball——ba爸(拼音)ll筷子(象形)——爸爸用筷子夹球——The kid is playing the ball. ——孩子在玩皮球。 2——boat——[bəʊt]——n.船——boat——bo60(象形)at在(熟词)——60个人在船上——I have 60 boats.——我有60艘船。 3——bag—…

flutter项目引入本地静态图片资源并展示

想要在flutter中引入静态资源&#xff0c;需要配置pubspec.yaml&#xff0c;将本地的静态资源添加到assets下面&#xff1a; 然后在flutter引入这些静态资源&#xff1a; Image.asset("images/squick.png") 就可以在app中看到这个图片了&#xff1a; 也可以使用网…

第六章:进制转换与数据存储

系列文章目录 文章目录 系列文章目录前言一、进制二、进制的转换三、原码、反码、补码总结 前言 进制转换是程序员的基本功。 一、进制 进制组成二进制0-1 &#xff0c;满2进1以0b或0B开头十进制0-9 &#xff0c;满10进1八进制0-7&#xff0c;满8进1以数字0开头表示十六进制0…

045_第三代软件开发-U盘监测

第三代软件开发-U盘监测 文章目录 第三代软件开发-U盘监测项目介绍U盘监测原理解释源代码 关键字&#xff1a; Qt、 Qml、 USB、 Disk、 文件 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&#xff09;和…

leetcode刷题 - SQL - 简单

目录 1. 175组合两个表 左外连接 2. 181. 超过经理收入的员工 3. 182. 查找重复的电子邮箱 4. 196. 删除重复的电子邮箱 5. 197. 上升的温度 日期作差 6. 511. 游戏玩法分析 I 7. 577. 员工奖金 null条件运算 8. 584. 寻找用户推荐人 9. 586. 订单最多的客户 10. 595. 大的国家…

Linux--线程-条件控制实现线程的同步

1.条件变量 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时&#xff0c;允许线程以无竞争的方式等待特定的条件发生。 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量&#xff0c;其他线程在获…

大航海时代Ⅳ 威力加强版套装 HD Version (WinMac)中文免安装版

《大航海时代》系列的人气SRPG《大航海时代IV》以HD的新面貌再次登场&#xff01;本作品以16世纪的欧洲“大航海时代”为舞台&#xff0c;玩家将以探险家、商人、军人等不同身份与全世界形形色色的人们一起上演出跌宕起伏的海洋冒险。游戏中玩家的目的是在不同的海域中掌握霸权…

新技术前沿-2023-应用GPT提问模板写技术文章

参考一份万能的GPT提问模版&#xff01;直接套用&#xff01; 参考用GPT写技术文章是真爽&#xff01; 参考码住这篇 8200 字 ChatGPT 实战指南&#xff01;&#xff01; 1 GPT提问模板 想让GPT回答的内容符合我们所希望的&#xff0c;最最重要的一点就在于我们如何提问。提问…

【C++】一篇文章搞懂auto关键字及其相关用法!

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

【Mybatis小白从0到90%精讲】11:Mybatis批量插入 batchInsert

文章目录 前言foreach批量插入前言 在实际开发中,我们经常需要批量插入大量数据到数据库中,而MyBatis也提供了批量插入的支持,可以大大提高插入效率。 今天要分享的是 工作中常用的在Mapper中使用foreach标签批量插入数据的方式,比在Java代码中循环调用Mapper的单条插入性…