最近感觉自己数据库技术知识储备有点薄弱,很多东西只知其然,不知其所以然,对一些比较复杂的SQL查询也没好的查询方案。一咬牙,一跺脚,决定拿起在书架上落灰已久的《高性能Mysql》一书,为了避免走马观花式的阅读,给自己定一个读书任务,每看完一章,写一篇总结博客,用来记录当前章节的重点难点知识,供日后翻阅查看,权当读书笔记。
在一个查询中,最重要的是响应时间,如果要优化查询,实际上要优化其子任务,要么消除其中一些子任务,要么减少子任务的执行次数,要么让子任务运行的更快,要对一个查询进行优化,首先要理解它为什么会慢。
对于低效的查询,可以从以下两个方面优化分析:
1.响应时间
响应时间是服务时间和排队时间之和。
服务时间:数据库处理这个查询真正花费的时间。
排队时间:服务器因为等待某些资源而没有真正执行查询的时间,可能等I/O操作完成,也可能等待行锁等。
2.扫描的行数和返回行数
3. 扫描行数和访问类型
扫描表、扫描索引、范围访问和单值访问的概念。
一般情况下mysql的where条件从好到坏依次是:
如果查询需要扫描大量的数据但只返回少数的行,可以采用以下方法进行优化:
优化有问题的查询时,目标应该是找到一个更优的方法获得实际需要的结果,而不一定总是需要从MySQL获取一模一样的结果集。
设计查询的时候有一个需要考虑的重要问题是,是否将一个复杂的查询分成多个简单的查询?
在其他条件都相同的时候,使用尽可能少的查询当然是更好的。但是有时候,将一个大查询分解为更多的小查询是很有必要的。
将大查询切分成小查询,每个查询功能完全一样,只完成一小部分,每次只返回一小部分查询结果。
比如说删除旧数据,定期清除大量数据时,如果用一个大的语句一次性完成的话,则可能需要一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。将一个大的delete语句切分成多个较小的查询可以尽可能小地影响MySQL性能,同时还可以减少MySQL复制的延迟。
# 优化前 mysql>delete from messages where created < date_sum(now(), interval 3 month); # 优化后 rows_affected = 0 do{ rows_affected = do_query( "mysql>delete from messages where created < date_sum(now(), interval 3 month) limit 10000") } while rows_afffected > 0
用分解关联查询的方式重构查询有如下的优势:
更容易对数据库进行拆分, 更容易做到高性能和可扩展
减少冗余记录的查询
当向MySQL发送一个请求时,MySQL执行如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GD5W93wA-1640933725518)(file://C:/Users/Lenovo/AppData/Roaming/Typora/typora-user-images/image-20211031133439960.png?lastModify=1637802602)]
MySQL客户端和服务器之间的通信协议是"半双工",在任何一个时刻,要么服务器向客户端发送数据,要么客户端向服务器发送数据,这两个动作不可能同时发生。
服务器-> 客户端 或客户端->服务器
查询状态
对于一个MySQL链接,任何时刻都有一个状态,该状态表示了MySQL当前正在做什么。可以使用命令SHOW FULL PROCESSLIST
命令查看(Command列表示当前的状态),
sleep: 线程正在等待客户端发送新的请求
query: 线程正在执行查询或者正在将结果发送给客户端
locked: 等待锁
ayalyzing and statistics : 线程正在收集存储引擎的统计信息, 并生成查询的执行计划
copying to tmp table [on disk]: 线程正在执行查询, 并将结果都赋值到一张临时表,一般是在做group by操作、文件排序操作或union操作。
sorting result: 线程正在对结果集进行排序
Sending data: 线程在多个状态之间传送数据或生成结果集,或在向客户端返回数据
若查询缓存是打开的,则先查询缓存,没有就进入下一阶段。如果查询缓存命中,就检查用户权限,权限验证成功直接从缓存中拿取结果返回客户端。
注:缓存是大小写敏感的哈希查找,查询和缓存中的查询即时只有一个字节不同,也不会匹配缓存结果。
MySQL通过关键字将SQL语句进行解析,并生成一颗对应的“解析树”。MySQL解析器将使用MySQL语法规则验证和解析查询。
一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。
MySQL使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。
静态优化: 直接对解析树进行分析,并完成优化(编译时优化) 。比如,优化器可以通过一些简单的代数变换将WHERE条件转化为另一种等价形式。
动态优化:(运行时优化)
MySQL服务器层没有任何统计信息,所以查询优化器在生成查询的执行计划时,需要向存储引擎获取相应的统计信息。
任何一次查询都是一次关联
MySQL对任何关联都执行嵌套循环关联操作, 即mysql先在一个表中循环取出单挑数据, 然后在嵌套循环到下一个表中寻找匹配的行
在解析和优化阶段,MySQL将生成查询对应的执行计划,MySQL的查询执行引擎则根据这个执行计划来完成整个查询。
查询执行的最后一个阶段是将结果返回给客户端。即使查询不需要返回结果集给客户端,MySQL仍然会返回这个查询的一些信息,如该查询影响到的行数。
如果查询可以被缓存,那么MySQL在这个阶段也会将结果放到查询缓存中。
WHERE条件中包含IN()的子查询语句非常影响查询效率。