mysql 高性能查询

本文详细介绍了如何优化MySQL数据库的数据访问,包括避免全表扫描,合理使用索引,优化查询语句,如避免SELECT *,使用缓存,分解复杂查询,以及优化关联查询、子查询、LIMIT和UNION操作。通过这些方法可以显著提高查询效率,减少资源消耗。
摘要由CSDN通过智能技术生成

优化数据访问

  • 只查询需要的数据

  • 永远不要使用select * 除了count(*)

  • 用缓存等手段减少重复数据访问

  • 关注查询的响应时间扫描行数返回行数

如果发现某个查询扫描行数远大于返回行数,可以从以下角度优化

  1. 使用覆盖索引
  2. 改变表库结构,使用汇总表等
  3. 改进查询方式

应用where的三种方式,性能从高到低

  1. 在索引中使用where条件过滤不匹配的内容,存储引擎层完成;
  2. 使用索引覆盖扫描(Extra列中出现 Using Index),直接从索引中过滤并返回结果,服务层执行,但是无须回表操作;
  3. 从数据表中返回数据,然后过滤不满足的记录(Extra 中出现 Using Where) ,服务层完成。

重构查询

一个复杂查询还是多个简单查询

根据实际情况选择,需要权衡

切分查询
delete from log where created_at < DATE_SUB(NOW(), Interval 3 MONTH);
-- 替换成如下 降低因为锁表带来的并发问题,降低资源占用等
rows = 0;
do {
	rows  = do_query("delete from log where created_at < DATE_SUB(NOW(), Interval 3 MONTH) limit 10000");
} while rows > 0
分解关联查询
select * from tag 
join tag_post tp on tp.tag_id=tag.id
join post on post.id=tp.post_id
where tag.tag = 'mysql';
-- 替换为如下查询
select * from tag where tag = 'mysql';
select * from tag_post where tag_id=1234;
select * from post where id in (1, 2, 3, 4);

分解关联查询的优势:

  • 让缓存的效率更高,如果tag表已被应用程序缓存,则减少一次查询
  • 查询分解后,单个查询可以减少锁竞争
  • 在应用层做管理,可以更容易对数据库进行拆分,更容易做到高性能和可扩展
  • 查询本身的效率可能会有所提升
  • 可以减少冗余记录的查询,应用层做关联,所有的数据只会查询一次,数据库中做关联可能会重复扫描一部分数据,可能会减少内存占用

优化特定类型查询

优化count() 查询

作用: 统计行数,统计某列的数量(列值非NULL)

  1. 根据实际情况,可反向统计(仅限MyISAM引擎)

    select count(*) from article where id > 5;
    -- 改写为如下,利用MyISAM引擎的全表统计的特性,减少扫描的行数,前提是两种查询方式扫描行数有差距
    select (select count(*) from article) - count(*) from article where id <= 5;
    
  2. 同一查询返回同一列不同值的统计

    -- 查询文章表中分类分别为1和3的文章总数,以下两种方式皆可
    select sum(cid = 1), sum(cid = 3) from article;
    select count(if(cid = 1, 1, null)), count(if(cid = 3, 1, null)) from article;
    
  3. 使用近似值

    有些统计查询对精度要求不高,可近似的使用 explain 的扫描行数

优化关联查询
  1. 确保 ONUSING 子句中的列上有索引,一般来说除非有其他理由,否则只需要在关联顺序中的第二个表的相应列上创建索引,没有用到的索引只会带来负担。
  2. 确保 GROUP BYORDER BY 中的表达式只涉及一个表中的列,这样才有可能使用索引优化
优化子查询

尽量使用关联查询代替,>= 5.6 版本的则无所谓。

优化GROUP BY 和 DISTINCT

分组查询的时候,通常使用标识列(id)分组效果更好

如果开启了 ONLY_FULL_GROUP_BY,如果分组列不是标识列(id),那 select 后面跟随的列必须是分组列中的或者是聚合函数计算过的。

如果没有通过ORDER BY 显式的指定排序列,结果集会按照分组的字段进行排序,如果不关心结果集的顺序,而这种默认排序需要文件排序,可以用ORDER BY NULL 让 MySQL不再进行文件排序。

优化 LIMIT
  1. 延后关联,子查询使用覆盖索引

    select * from people where sex=1 limit 10000, 20;
    -- 改写为如下
    select * from people inner join (
    	select id from people where sex=1 limit 10000, 20
    ) using (id);
    
  2. 记录边界值,通过边界值做条件

  3. 汇总表、冗余表等手段

优化 UNION 查询

尽量将所有条件下推到每一个UNION子查询,临时表中不能很好的使用索引

除非确实需要服务器消除重复行,否则一定使用 UNION ALL ,不携带 ALL 关键字会执行去重操作,成本很高

使用自定义变量

使用自定义变量的限制

  • 使用自定义变量的查询,无法使用查询缓存
  • 不能在使用常量或者标识符的地方使用自定义变量,如表名,limit子句中
  • 自定义变量生命周期是一个连接内有效,无法夸连接通信
  • MySQL优化器可能会在某些情况下将这些变量优化掉,这可能导致代码不按预想的方式运行
  • 赋值的顺序和赋值的时间点不是固定的,这依赖于优化器的决定
  • 赋值符号 := (等同于 =) ,优先级非常低,赋值表达式应该使用明确的括号
  • 使用未定义的变量不会产生任何语法错误,如果没有意识到这一点,非常容易犯错
-- 使用自定义变量计算排名
set @curr_cnt := 0, @prev_cnt := 0, @rank := 0;
select actor_id ,
	@curr_cnt := cnt as cnt,
	@rank := if (@prev_cnt <> @curr_cnt, @rank + 1, @rank),
	@prev_cnt := @curr_cnt as dummy from
	(
    	select actor_id, count(*) as cnt
        from actor_film
        group by actor_id
        order by cnt desc
        limit 10
    ) as t1
-- 编写偷懒的UNION
select id from users where id = 123 
	union all 
select id from users_archived where id = 123;
-- 正常的union语句 如果第一条sql查询出结果,第二条也会查询,此sql适用于只需要有结果,而不是需要全部的结果
select greatest(@found := -1, id) as id , 'users' as t1 from users where id = 1
	union all 
select id , 'users_archived' from users_archived where id  = 1 and @found is null
	union all
select 1, 'reset' from dual where (@found:=null) is not null	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值