高性能Mysql------------查询性能优化

查询优化,索引优化,库表结构优化需要齐头并进。

查询性能低下最基本的原因是访问的数据太多了。可以通过下面两个步骤来分析:
1.是否检索大量超过需要的数据
2.是否在分析大量超过需要的数据行

请求了不需要的数据

1)查询不需要的记录
最简单的解决方法是在查询后面加limit
2)多表关联时返回全部列
正确的方式是select u.* from user u 
inner join score s where u.scoreid = s.id
inner join course c where s.courseid = c.id
where u.name = 'zhangsan'

3)总是取出全部列
select * 的写法会带来额外的i/o,内存和CPU消耗;好处是可以避免列修改引起的问题,而且可以提高相同代码的复用性。
4)重复查询相同的数据
初次查询时将数据缓存起来,再次查询时从缓存中取

扫描了额外的记录

确定查询只返回需要的数据后,检查查询中是否扫描过多的数据。简单衡量查询开销的三个指标:
1)响应时间
2)扫描行数
3)返回行数
响应时间是服务时间和排队时间之和。
在explain语句中type列翻译了访问类型。访问 类型有多种:全表扫描,索引扫描,范围扫描,唯一索引查询,常数引用等,速度从慢到快,扫描的行数也是从小到大。
如果发现查询需要扫描大量的数据但只返回少数的行,可以尝试下面的技巧:
1)使用索引覆盖扫描,把所有需要用到的列都放到索引中
2)改变库表结构
3)重写查询,让mysql优化器能以更优化的方式执行查询

重构查询方式
优化有问题的查询时,目标是找到一个更优的方法获得实际需要的结果而不是一定要从查询中获得一个一模一样的结果集。
1)是否需要将一个复杂的查询分成多个简单的查询
Mysql响应数据给客户端要比扫描内存中数据慢很多,在其他条件都相同的情况下,使用尽可能少的查询更好。但有时候将一个大查询分解成多个小查询是很有必要的。
2)分解关联查询
select * from tag join tag_post on tag_post.tag_id = tag.id join post on tag_post.post_id = post.id where tag.tag = ‘json’;
分解成下面的查询:
select * from tag where tag.tag = ‘json’;
select * from tag_post where tag_id = 121;
select * from post where post_id in (111, 222, 333);

分解关联查询的优势:
1.让缓存的效率更高。
2.将查询分解,执行单个查询可以减少锁的竞争。
3.更容易对数据库做拆分
4.减少冗余数据的查询

四、查询执行的基础
查询优化工作实际上是遵循一些原则让优化器能够按照预想的合理方式运行。
1.客户端发送一条查询给服务器;
2.服务器先检查查询缓存,如果命中缓存,则立刻返回存储在缓存中的结果,否则进入下一个阶段;
3.服务器进行SQL解析、预处理、再由优化器生成对应的执行计划;
4.根据优化器生成的执行计划,调用存储引擎的API来执行查询;
5.将结果返回给客户端。

Mysql通信协议:在任何一个时刻,服务器向客户端发送数据或者客户端向服务器发送数据,两者不能同时进行。
当服务器开始响应客户端请求时,客户端必须完整地接收整个返回结果。和客户端交互的整个过程中,服务器的资源都被查询所占用。

查询优化器: Mysql使用基于成本的优化器,优化器的作用是找到最好的执行计划。很多原因导致优化器选择错误的执行计划:
1)统计信息不准确。mysql依赖存储引擎提供的统计信息来评估成本
2)执行计划中的成本估算不等同于实际执行的成本。
3)最优不一定是最快的执行方式
提前终止查询:在发现已经满足查询需求时候,能够立即终止查询。例如使用limit字句的时候;exists 、not exists的查询
列表IN()比较:
先将IN列表中的数据排序,然后通过二分查找的方式来确定列表中的值是否满足条件,这是一个O(logn)复杂度的操作。

关联查询执行策略:
先从一个表中循环取出单条数据,然后再嵌套循环到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行。然后返回查询结果

union:如果希望将2个字查询的结果联合起来,然后再取前20条数据,可以在2个子查询中分别加上limit 20来减少临时表中的数据。
Mysql不允许对同一张表同时进行查询和更新。例如:
update student as stu
set total total = (
select sum(sc.score) from sc where sc.SId = stu.SId group by sc.SId
)
会报错,下面的查询会正常执行:
UPDATE student INNER JOIN (
SELECT SUM(sc.score) AS total, sid FROM sc GROUP BY sc.SId
) AS s ON student.SId = s.sid
SET student.total = s.total

count():可以统计某个列值得数量,也可以统计行数。在统计列值的时候要求列值是非空的(不统计NULL)

优化关联查询:1)确保on或where字句中的列上有索引;2)确保group by和order by中的表达式只涉及到一个表中的列,这样才有可能用索引来优化;

优化limit分页:
SELECT * FROM product a inner join (select id from product limit 866613, 10) b
on a.id = b.id
这里“延迟关联“将大大提升查询效率。

猜你喜欢

转载自blog.csdn.net/zhanglinlove/article/details/83473686