我不得不告诉大家的MySQL优化“套路”(六)

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: 说起 MySQL 的查询优化,相信大家收藏了一堆奇技淫巧:不能使用 SELECT *、不使用 NULL 字段、合理创建索引、为字段选择合适的数据类型.....
避免多个范围条件

实际开发中,我们会经常使用多个范围条件,比如想查询某个时间段内登录过的用户:58.png这个查询有一个问题:它有两个范围条件,login_time 列和 age 列,MySQL 可以使用 login_time 列的索引或者 age 列的索引,但无法同时使用它们。


覆盖索引

如果一个索引包含或者说覆盖所有需要查询的字段的值,那么就没有必要再回表查询,这就称为覆盖索引。


覆盖索引是非常有用的工具,可以极大的提高性能,因为查询只需要扫描索引会带来许多好处:

  • 索引条目远小于数据行大小,如果只读取索引,极大减少数据访问量。
  • 索引是有按照列值顺序存储的,对于 I/O 密集型的范围查询要比随机从磁盘读取每一行数据的 IO 要少的多。

使用索引扫描来排序

MySQL 有两种方式可以生产有序的结果集:

  • 对结果集进行排序的操作。
  • 按照索引顺序扫描得出的结果自然是有序的,如果 explain 的结果中 type 列的值为 index 表示使用了索引扫描来做排序。


扫描索引本身很快,因为只需要从一条索引记录移动到相邻的下一条记录。但如果索引本身不能覆盖所有需要查询的列,那么就不得不每扫描一条索引记录就回表查询一次对应的行。


这个读取操作基本上是随机 I/O,因此按照索引顺序读取数据的速度通常要比顺序地全表扫描要慢。


在设计索引时,如果一个索引既能够满足排序,又满足查询,是最好的。只有当索引的列顺序和 ORDER BY 子句的顺序完全一致,并且所有列的排序方向也一样时,才能够使用索引来对结果做排序。


如果查询需要关联多张表,则只有 ORDER BY 子句引用的字段全部为第一张表时,才能使用索引做排序。


ORDER BY 子句和查询的限制是一样的,都要满足最左前缀的要求(有一种情况例外,就是最左的列被指定为常数,下面是一个简单的示例),其他情况下都需要执行排序操作,而无法利用索引排序。


59.png

冗余和重复索引

冗余索引是指在相同的列上按照相同的顺序创建的相同类型的索引,应当尽量避免这种索引,发现后立即删除。


比如有一个索引(A,B),再创建索引(A)就是冗余索引。冗余索引经常发生在为表添加新索引时,比如有人新建了索引(A,B),但这个索引不是扩展已有的索引(A)。


大多数情况下都应该尽量扩展已有的索引而不是创建新索引。但有极少情况下出现性能方面的考虑需要冗余索引,比如扩展已有索引而导致其变得过大,从而影响到其他使用该索引的查询。


删除长期未使用的索引

定期删除一些长时间未使用过的索引是一个非常好的习惯。


关于索引这个话题打算就此打住,最后要说一句,索引并不总是最好的工具,只有当索引帮助提高查询速度带来的好处大于其带来的额外工作时,索引才是有效的。


对于非常小的表,简单的全表扫描更高效。对于中到大型的表,索引就非常有效。


对于超大型的表,建立和维护索引的代价随之增长,这时候其他技术也许更有效,比如分区表。最后的最后,explain 后再提测是一种美德。


特定类型查询优化


优化 COUNT() 查询


COUNT() 可能是被大家误解最多的函数了,它有两种不同的作用,其一是统计某个列值的数量,其二是统计行数。


统计列值时,要求列值是非空的,它不会统计 NULL。如果确认括号中的表达式不可能为空时,实际上就是在统计行数。


最简单的就是当使用 COUNT(*) 时,并不是我们所想象的那样扩展成所有的列,实际上,它会忽略所有的列而直接统计行数。


我们最常见的误解也就在这儿,在括号内指定了一列却希望统计结果是行数,而且还常常误以为前者的性能会更好。


但实际并非这样,如果要统计行数,直接使用 COUNT(*),意义清晰,且性能更好。


有时候某些业务场景并不需要完全精确的 COUNT 值,可以用近似值来代替,EXPLAIN 出来的行数就是一个不错的近似值,而且执行 EXPLAIN 并不需要真正地去执行查询,所以成本非常低。


通常来说,执行 COUNT() 都需要扫描大量的行才能获取到精确的数据,因此很难优化,MySQL 层面还能做得也就只有覆盖索引了。


如果还不能解决问题,只有从架构层面解决了,比如添加汇总表,或者使用 Redis 这样的外部缓存系统。


优化关联查询


在大数据场景下,表与表之间通过一个冗余字段来关联,要比直接使用 JOIN 有更好的性能。


如果确实需要使用关联查询的情况下,需要特别注意的是:

  • 确保 ON 和 USING 字句中的列上有索引。在创建索引的时候就要考虑到关联的顺序。
    当表 A 和表 B 用列 c 关联的时候,如果优化器关联的顺序是 A、B,那么就不需要在 A 表的对应列上创建索引。
    没有用到的索引会带来额外的负担,一般来说,除非有其他理由,只需要在关联顺序中的第二张表的相应列上创建索引(具体原因下文分析)。
  • 确保任何的 GROUP BY 和 ORDER BY 中的表达式只涉及到一个表中的列,这样 MySQL 才有可能使用索引来优化。


要理解优化关联查询的第一个技巧,就需要理解 MySQL 是如何执行关联查询的。


当前 MySQL 关联执行的策略非常简单,它对任何的关联都执行嵌套循环关联操作,即先在一个表中循环取出单条数据,然后在嵌套循环到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行为为止。然后根据各个表匹配的行,返回查询中需要的各个列。


太抽象了?以上面的示例来说明,比如有这样的一个查询:


60.png


假设 MySQL 按照查询中的关联顺序 A、B 来进行关联操作,那么可以用下面的伪代码表示 MySQL 如何完成这个查询:

61.png

可以看到,最外层的查询是根据 A.xx 列来查询的,A.c 上如果有索引的话,整个关联查询也不会使用。


再看内层的查询,很明显 B.c 上如果有索引的话,能够加速查询,因此只需要在关联顺序中的第二张表的相应列上创建索引即可。


优化 LIMIT 分页


当需要分页操作时,通常会使用 LIMIT 加上偏移量的办法实现,同时加上合适的 ORDER BY 字句。


如果有对应的索引,通常效率会不错,否则,MySQL 需要做大量的文件排序操作。


一个常见的问题是当偏移量非常大的时候,比如:LIMIT 10000 20 这样的查询,MySQL 需要查询 10020 条记录然后只返回 20 条记录,前面的 10000 条都将被抛弃,这样的代价非常高。


优化这种查询一个最简单的办法就是尽可能的使用覆盖索引扫描,而不是查询所有的列。


然后根据需要做一次关联查询再返回所有的列。对于偏移量很大时,这样做的效率会提升非常大。考虑下面的查询:


62.png


如果这张表非常大,那么这个查询最好改成下面的样子:

63.png

这里的延迟关联将大大提升查询效率,让 MySQL 扫描尽可能少的页面,获取需要访问的记录后在根据关联列回原表查询所需要的列。


有时候如果可以使用书签记录上次取数据的位置,那么下次就可以直接从该书签记录的位置开始扫描,这样就可以避免使用 OFFSET,比如下面的查询:

64.png

其他优化的办法还包括使用预先计算的汇总表,或者关联到一个冗余表,冗余表中只包含主键列和需要做排序的列。


优化 UNION


MySQL 处理 UNION 的策略是先创建临时表,然后再把各个查询结果插入到临时表中,最后再来做查询。


因此很多优化策略在 UNION 查询中都没有办法很好的时候,经常需要手动将 WHERE、LIMIT、ORDER BY 等字句“下推”到各个子查询中,以便优化器可以充分利用这些条件先优化。


除非确实需要服务器去重,否则就一定要使用 UNION ALL,如果没有 ALL 关键字,MySQL 会给临时表加上 DISTINCT 选项,这会导致整个临时表的数据做唯一性检查,这样做的代价非常高。


当然即使使用 ALL 关键字,MySQL 总是将结果放入临时表,然后再读出,再返回给客户端。


虽然很多时候没有这个必要,比如有时候可以直接把每个子查询的结果返回给客户端。


结语


理解查询是如何执行以及时间都消耗在哪些地方,再加上一些优化过程的知识,可以帮助大家更好的理解 MySQL,理解常见优化技巧背后的原理。


希望本文中的原理、示例能够帮助大家更好的将理论和实践联系起来,更多的将理论知识运用到实践中。


最后给大家留两个思考题吧,可以在脑袋里想想答案,这也是大家经常挂在嘴边的,但很少有人会思考为什么?

  • 有非常多的程序员在分享时都会抛出这样一个观点:尽可能不要使用存储过程,存储过程非常不容易维护,也会增加使用成本,应该把业务逻辑放到客户端。既然客户端都能干这些事,那为什么还要存储过程?
  • JOIN 本身也挺方便的,直接查询就好了,为什么还需要视图呢?



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
16天前
|
关系型数据库 MySQL 数据库
数据迁移脚本优化过程:从 MySQL 到 Django 模型表
在大规模的数据迁移过程中,性能问题往往是开发者面临的主要挑战之一。本文将分析一个数据迁移脚本的优化过程,展示如何从 MySQL 数据库迁移数据到 Django 模型表,并探讨优化前后的性能差异。
|
16天前
|
存储 关系型数据库 MySQL
mysql optimizer_switch : 查询优化器优化策略深入解析
mysql optimizer_switch : 查询优化器优化策略深入解析
|
16天前
|
关系型数据库 MySQL 数据库
MySQL索引优化:深入理解索引合并
MySQL索引优化:深入理解索引合并
|
17天前
|
关系型数据库 MySQL 分布式数据库
PolarDB产品使用问题之 MySQL数据库中,执行delete命令删除数据后,存储空间通常不会立即释放,该如何优化
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
9天前
|
SQL Oracle 关系型数据库
MySQL单表千万级数据查询优化大家怎么说(评论有亮点)
单表千万级数据是MySQL查询的一个坎,可能还不是天花板。“一个人走的慢,一群人走的快”,通过讨论可以发现MySQL千万数据的全貌大概是怎样的。
23 0
|
12天前
|
关系型数据库 MySQL 索引
MySQL之优化SELECT语句
以上只是一些基本的优化策略,具体的优化方案还需要根据实际的业务需求和数据情况来定制。
11 0
|
16天前
|
存储 关系型数据库 MySQL
技术笔记:MySQL数据库优化详解(收藏)
技术笔记:MySQL数据库优化详解(收藏)
20 0
|
16天前
|
缓存 关系型数据库 MySQL
欢迎来到MySQL优化之旅
欢迎来到MySQL优化之旅
14 0
|
17天前
|
SQL 存储 关系型数据库
Mysql-事务-锁-索引-sql优化-隔离级别
Mysql-事务-锁-索引-sql优化-隔离级别
|
18天前
|
SQL 关系型数据库 MySQL
Mysql优化之索引相关介绍(笔记)
这段内容涵盖了创建MySQL用户表的SQL语句,创建一个包含`username`、`age`和`dept`字段的联合索引,以及关于联合索引查询时遵循的最左前缀原则的解释。
25 0