MySQL(六)

news2024/9/22 17:25:08

查询优化

在编写快速的查询之前,需要清楚一点,真正重要的是响应时间,而且要知道在整个SQL语句的执行过程中每个步骤都花费了多长时间,要知道哪些步骤是拖垮执行效率的关键步骤,想要做到这点,必须要知道查询的生命周期,然后进行优化,不同的应用场景有不同的优化方式,不要一概而论,具体情况具体分析,

  1. 查询慢的原因
  • 网络
  • CPU
  • IO
  • 上下文切换
  • 系统调用
  • 生成统计信息
  • 锁等待时间
  1. 优化数据访问
  • 查询性能低下的主要原因是访问的数据太多,某些查询不可避免的需要筛选大量的数据,我们可以通过减少访问数据量的方式进行优化
    • 确认应用程序是否在检索大量超过需要的数据
    • 确认mysql服务器层是否在分析大量超过需要的数据行
  • 是否向数据库请求了不需要的数据
    • 查询不需要的记录
    • 我们常常会误以为mysql会只返回需要的数据,实际上mysql却是先返回全部结果再进行计算,在日常的开发习惯中,经常是先用select语句查询大量的结果,然后获取前面的N行后关闭结果集。
      优化方式是在查询后面添加limit
    • 多表关联时返回全部列
      • select * from actor inner join film_actor using(actor_id) inner join film using(film_id) where film.title=‘Academy Dinosaur’;
        select actor.* from actor…;
    • 总是取出全部列
      • 在公司的企业需求中,禁止使用select *,虽然这种方式能够简化开发,但是会影响查询的性能,所以尽量不要使用
    • 重复查询相同的数据
      • 如果需要不断的重复执行相同的查询,且每次返回完全相同的数据,因此,基于这样的应用场景,我们可以将这部分数据缓存起来,这样的话能够提高查询效率
  1. 执行过程的优化
  • 查询缓存
    • 在解析一个查询语句之前,如果查询缓存是打开的,那么mysql会优先检查这个查询是否命中查询缓存中的数据,如果查询恰好命中了查询缓存,那么会在返回结果之前会检查用户权限,如果权限没有问题,那么mysql会跳过所有的阶段,就直接从缓存中拿到结果并返回给客户端
  • 查询优化处理
    • 语法解析器和预处理
      mysql通过关键字将SQL语句进行解析,并生成一颗解析树,mysql解析器将使用mysql语法规则验证和解析查询,例如验证使用使用了错误的关键字或者顺序是否正确等等,预处理器会进一步检查解析树是否合法,例如表名和列名是否存在,是否有歧义,还会验证权限等等
    • 查询优化器
      当语法树没有问题之后,相应的要由优化器将其转成执行计划,一条查询语句可以使用非常多的执行方式,最后都可以得到对应的结果,但是不同的执行方式带来的效率是不同的,优化器的最主要目的就是要选择最有效的执行计划
      mysql使用的是基于成本的优化器,在优化的时候会尝试预测一个查询使用某种查询计划时候的成本,并选择其中成本最小的一个
      • select count(*) from film_actor;
        show status like ‘last_query_cost’;
        可以看到这条查询语句大概需要做1104个数据页才能找到对应的数据,这是经过一系列的统计信息计算来的

        • 每个表或者索引的页面个数
        • 索引的基数
        • 索引和数据行的长度
        • 索引的分布情况
      • 在很多情况下mysql会选择错误的执行计划,原因如下:

        • 统计信息不准确
          • InnoDB因为其mvcc的架构,并不能维护一个数据表的行数的精确统计信息
        • 执行计划的成本估算不等同于实际执行的成本
          • 有时候某个执行计划虽然需要读取更多的页面,但是他的成本却更小,因为如果这些页面都是顺序读或者这些页面都已经在内存中的话,那么它的访问成本将很小,mysql层面并不知道哪些页面在内存中,哪些在磁盘,所以查询之际执行过程中到底需要多少次IO是无法得知的
        • mysql的最优可能跟你想的不一样
          • mysql的优化是基于成本模型的优化,但是有可能不是最快的优化
        • mysql不考虑其他并发执行的查询
        • mysql不会考虑不受其控制的操作成本
          • 执行存储过程或者用户自定义函数的成本
      • 优化器的优化策略

        • 静态优化
          • 直接对解析树进行分析,并完成优化
        • 动态优化
          • 动态优化与查询的上下文有关,也可能跟取值、索引对应的行数有关
        • mysql对查询的静态优化只需要一次,但对动态优化在每次执行时都需要重新评估
      • 优化器的优化类型

        • 重新定义关联表的顺序
          • 数据表的关联并不总是按照在查询中指定的顺序进行,决定关联顺序时优化器很重要的功能
        • 将外连接转化成内连接,内连接的效率要高于外连接
        • 使用等价变换规则,mysql可以使用一些等价变化来简化并规划表达式
        • 优化count(),min(),max()
          • 索引和列是否可以为空通常可以帮助mysql优化这类表达式:例如,要找到某一列的最小值,只需要查询索引的最左端的记录即可,不需要全文扫描比较
        • 预估并转化为常数表达式,当mysql检测到一个表达式可以转化为常数的时候,就会一直把该表达式作为常数进行处理
          • explain select film.film_id,film_actor.actor_id from film inner join film_actor using(film_id) where film.film_id = 1
        • 索引覆盖扫描,当索引中的列包含所有查询中需要使用的列的时候,可以使用覆盖索引
        • 子查询优化
          • mysql在某些情况下可以将子查询转换一种效率更高的形式,从而减少多个查询多次对数据进行访问,例如将经常查询的数据放入到缓存中
        • 等值传播
          • 如果两个列的值通过等式关联,那么mysql能够把其中一个列的where条件传递到另一个上:
            explain select film.film_id from film inner join film_actor using(film_id
            ) where film.film_id > 500;
            这里使用film_id字段进行等值关联,film_id这个列不仅适用于film表而且适用于film_actor表
            explain select film.film_id from film inner join film_actor using(film_id
            ) where film.film_id > 500 and film_actor.film_id > 500;
      • 关联查询
        mysql的关联查询很重要,但其实关联查询执行的策略比较简单:mysql对任何关联都执行嵌套循环关联操作,即mysql先在一张表中循环取出单条数据,然后再嵌套到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行为止。然后根据各个表匹配的行,返回查询中需要的各个列。mysql会尝试再最后一个关联表中找到所有匹配的行,如果最后一个关联表无法找到更多的行之后,mysql返回到上一层次关联表,看是否能够找到更多的匹配记录,以此类推迭代执行。整体的思路如此,但是要注意实际的执行过程中有多个变种形式:

        • join的实现方式原理
          • Simple Nested-Loop Join
            在这里插入图片描述

          • Index Nested-Loop Join
            在这里插入图片描述

          • Block Nested-Loop Join
            在这里插入图片描述

            • (1)Join Buffer会缓存所有参与查询的列而不是只有Join的列。
              (2)可以通过调整join_buffer_size缓存大小
              (3)join_buffer_size的默认值是256K,join_buffer_size的最大值在MySQL 5.1.22版本前是4G-1,而之后的版本才能在64位操作系统下申请大于4G的Join Buffer空间。
              (4)使用Block Nested-Loop Join算法需要开启优化器管理配置的optimizer_switch的设置block_nested_loop为on,默认为开启。
            • show variables like ‘%optimizer_switch%’
        • 案例演示
          查看不同的顺序执行方式对查询性能的影响:
          explain select film.film_id,film.title,film.release_year,actor.actor_id,actor.first_name,actor.last_name from film inner join f
          ilm_actor using(film_id) inner join actor using(actor_id);
          查看执行的成本:
          show status like ‘last_query_cost’;
          按照自己预想的规定顺序执行:
          explain select straight_join film.film_id,film.title,film.release_year,actor.actor_id,actor.first_name,actor.last_name from fil
          m inner join film_actor using(film_id) inner join actor using(actor_id);
          查看执行的成本:
          show status like ‘last_query_cost’;
      • 排序优化
        无论如何排序都是一个成本很高的操作,所以从性能的角度出发,应该尽可能避免排序或者尽可能避免对大量数据进行排序。
        推荐使用利用索引进行排序,但是当不能使用索引的时候,mysql就需要自己进行排序,如果数据量小则再内存中进行,如果数据量大就需要使用磁盘,mysql中称之为filesort。
        如果需要排序的数据量小于排序缓冲区(show variables like ‘%sort_buffer_size%’),mysql使用内存进行快速排序操作,如果内存不够排序,那么mysql就会先将树分块,对每个独立的块使用快速排序进行排序,并将各个块的排序结果存放再磁盘上,然后将各个排好序的块进行合并,最后返回排序结果

        • 排序的算法
          • 两次传输排序
            第一次数据读取是将需要排序的字段读取出来,然后进行排序,第二次是将排好序的结果按照需要去读取数据行。
            这种方式效率比较低,原因是第二次读取数据的时候因为已经排好序,需要去读取所有记录而此时更多的是随机IO,读取数据成本会比较高
            两次传输的优势,在排序的时候存储尽可能少的数据,让排序缓冲区可以尽可能多的容纳行数来进行排序操作
          • 单次传输排序
            先读取查询所需要的所有列,然后再根据给定列进行排序,最后直接返回排序结果,此方式只需要一次顺序IO读取所有的数据,而无须任何的随机IO,问题在于查询的列特别多的时候,会占用大量的存储空间,无法存储大量的数据
          • 当需要排序的列的总大小超过max_length_for_sort_data定义的字节,mysql会选择双次排序,反之使用单次排序,当然,用户可以设置此参数的值来选择排序的方式
    1. 优化特定类型的查询
      • 优化count()查询
        count()是特殊的函数,有两种不同的作用,一种是某个列值的数量,也可以统计行数
        • 总有人认为myisam的count函数比较快,这是有前提条件的,只有没有任何where条件的count(*)才是比较快的
        • 使用近似值
          在某些应用场景中,不需要完全精确的值,可以参考使用近似值来代替,比如可以使用explain来获取近似的值
          其实在很多OLAP的应用中,需要计算某一个列值的基数,有一个计算近似值的算法叫hyperloglog。
        • 更复杂的优化
          一般情况下,count()需要扫描大量的行才能获取精确的数据,其实很难优化,在实际操作的时候可以考虑使用索引覆盖扫描,或者增加汇总表,或者增加外部缓存系统。
      • 优化关联查询
        • 确保on或者using子句中的列上有索引,在创建索引的时候就要考虑到关联的顺序
          当表A和表B使用列C关联的时候,如果优化器的关联顺序是B、A,那么就不需要再B表的对应列上建上索引,没有用到的索引只会带来额外的负担,一般情况下来说,只需要在关联顺序中的第二个表的相应列上创建索引
        • 确保任何的groupby和order by中的表达式只涉及到一个表中的列,这样mysql才有可能使用索引来优化这个过程
      • 优化子查询
        子查询的优化最重要的优化建议是尽可能使用关联查询代替
      • 优化limit分页
        在很多应用场景中我们需要将数据进行分页,一般会使用limit加上偏移量的方法实现,同时加上合适的orderby 的子句,如果这种方式有索引的帮助,效率通常不错,否则的化需要进行大量的文件排序操作,还有一种情况,当偏移量非常大的时候,前面的大部分数据都会被抛弃,这样的代价太高。
        要优化这种查询的话,要么是在页面中限制分页的数量,要么优化大偏移量的性能
        • 优化此类查询的最简单的办法就是尽可能地使用覆盖索引,而不是查询所有的列
        • select film_id,description from film order by title limit 50,5
        • explain select film.film_id,film.description from film inner join (select film_id from film order by title limit 50,5) as lim using(film_id);
        • 查看执行计划查看扫描的行数
      • 优化union查询
        mysql总是通过创建并填充临时表的方式来执行union查询,因此很多优化策略在union查询中都没法很好的使用。经常需要手工的将where、limit、order by等子句下推到各个子查询中,以便优化器可以充分利用这些条件进行优化
        • 除非确实需要服务器消除重复的行,否则一定要使用union all,因此没有all关键字,mysql会在查询的时候给临时表加上distinct的关键字,这个操作的代价很高
      • 推荐使用用户自定义变量
        用户自定义变量是一个容易被遗忘的mysql特性,但是如果能够用好,在某些场景下可以写出非常高效的查询语句,在查询中混合使用过程化和关系话逻辑的时候,自定义变量会非常有用。
        用户自定义变量是一个用来存储内容的临时容器,在连接mysql的整个过程中都存在。
        • 自定义变量的使用
          • set @one :=1
          • set @min_actor :=(select min(actor_id) from actor)
          • set @last_week :=current_date-interval 1 week;
        • 自定义变量的限制
          • 1、无法使用查询缓存
          • 2、不能在使用常量或者标识符的地方使用自定义变量,例如表名、列名或者limit子句
          • 3、用户自定义变量的生命周期是在一个连接中有效,所以不能用它们来做连接间的通信
          • 4、不能显式地声明自定义变量地类型
          • 5、mysql优化器在某些场景下可能会将这些变量优化掉,这可能导致代码不按预想地方式运行
          • 6、赋值符号:=的优先级非常低,所以在使用赋值表达式的时候应该明确的使用括号
          • 7、使用未定义变量不会产生任何语法错误
        • 自定义变量的使用案例
          • 优化排名语句
            • 1、在给一个变量赋值的同时使用这个变量
              select actor_id,@rownum:=@rownum+1 as rownum from actor limit 10;
            • 2、查询获取演过最多电影的前10名演员,然后根据出演电影次数做一个排名
              select actor_id,count(*) as cnt from film_actor group by actor_id order by cnt desc limit 10;
          • 避免重新查询刚刚更新的数据
            当需要高效的更新一条记录的时间戳,同时希望查询当前记录中存放的时间戳是什么
            • update t1 set lastUpdated=now() where id =1;
              select lastUpdated from t1 where id =1;
            • update t1 set lastupdated = now() where id = 1 and @now:=now();
              select @now;
          • 确定取值的顺序
            在赋值和读取变量的时候可能是在查询的不同阶段
            • set @rownum:=0;
              select actor_id,@rownum:=@rownum+1 as cnt from actor where @rownum<=1;
              因为where和select在查询的不同阶段执行,所以看到查询到两条记录,这不符合预期

            • set @rownum:=0;
              select actor_id,@rownum:=@rownum+1 as cnt from actor where @rownum<=1 order by first_name
              当引入了orde;r by之后,发现打印出了全部结果,这是因为order by引入了文件排序,而where条件是在文件排序操作之前取值的

            • 解决这个问题的关键在于让变量的赋值和取值发生在执行查询的同一阶段:
              set @rownum:=0;
              select actor_id,@rownum as cnt from actor where (@rownum:=@rownum+1)<=1;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt之调色板类QPalette的使用

文章目录QPalette调色板类前言代码知识点讲解QPalette调色板类 前言 Qt提供的调色板类QPalette专门用于管理部件的外观显示&#xff0c;相当于部件或对话框的调色板&#xff0c;管理他们所有的颜色信息。每个部件都包含一个QPalette对象&#xff0c;在显示时&#xff0c;按照…

OnGUI Color 控件||Unity 3D GUI 简介||OnGUI TextField 控件

Unity 3D Color 控件与 Background Color 控件类似&#xff0c;都是渲染 GUI 颜色的&#xff0c;但是两者不同的是 Color 不但会渲染 GUI 的背景颜色&#xff0c;同时还会影响 GUI.Text 的颜色。具体使用时&#xff0c;要作如下定义&#xff1a;public static var color:Color;…

Go 管道关闭引发的探索

前言 在日常开发中, 经常会使用chan来进行协程之间的通信. 对chan的操作也无外乎读写关. 而本次, 就是从chan的关闭而来. 假设我们对外提供的方法如下: type Chan struct {ch chan int }func (c *Chan) Close() {close(c.ch) }func (c *Chan) Send(v int) {c.ch <- v }那…

Qt 工程师进阶技术23种设计模式

Qt 工程师进阶技术23种设计模式【1】23种设计模式【1】23种设计模式 设计模式是解决特定问题的一系列套路&#xff0c;这套方案提高代码可复用性、可读性、稳健性、可维护性及安全性。 23种设计模式可分为三类:结构型模式(侧重类与对象之间的组合)、行为型模式&#xff08;侧重…

day47【代码随想录】动态规划之买卖股票的最佳时机III、买卖股票的最佳时机IV、最佳买卖股票时机含冷冻期、买卖股票的最佳时机含手续费

文章目录前言一、买卖股票的最佳时机III&#xff08;力扣123&#xff09;二、买卖股票的最佳时机IV&#xff08;力扣188&#xff09;三、最佳买卖股票时机含冷冻期&#xff08;力扣309&#xff09;四、买卖股票的最佳时机含手续费&#xff08;力扣714&#xff09;股票买卖问题总…

office365 word 另存为 pdf 的注意事项和典型设置

0. 操作环境介绍 Office 版本&#xff1a;Office 365 版本 不同版本的操作可能有所不同 1. 基本操作 – 另存为 pdf 【文件】 --> 【另存为】&#xff0c;选择适当的文件路径、文件名保存类型选择【PDF】点击【保存】 1. 导出的pdf包含目录标签 word中&#xff0c;可使用…

Head First设计模式---1.策略模式

4.1策略模式&#xff1a; 策略模式是一种行为设计模式&#xff0c; 它能让你定义一系列算法&#xff0c; 并将每种算法分别放入独立的类中&#xff0c; 以使算法的对象能够相互替换。 问题 一天&#xff0c;我们需要做一个鸭子游戏&#xff0c;游戏中会出现各种鸭子&#xff…

掘金数据时代2022年度隐私计算评选活动火热报名中!

开放隐私计算 开放隐私计算开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神&#xff0c;专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播&#xff0c;愿成为中国 “隐私计算最后一公里的服务区”。183篇原创内容公众号…

全网最全虚拟机的封装

1.服务器初始化 系统环境RHEL7.6 2.禁用selinux [rootserver1 ~]# vim /etc/sysconfig/selinux SELINUXdisabled reboot 3.禁用防火墙 [rootserver1 ~]# systemctl disable --now firewalld 4.配置yum源 [rootserver1 ~]# vim /etc/fstab /dev/mapper/rhel…

AC的改进算法——TRPO、PPO

两类AC的改进算法 整理了动手学强化学习的学习内容 1. TRPO 算法&#xff08;Trust Region Policy Optimization&#xff09; 1.1. 前沿 策略梯度算法即沿着梯度方向迭代更新策略参数 。但是这种算法有一个明显的缺点&#xff1a;当策略网络沿着策略梯度更新参数&#xff0c…

(考研湖科大教书匠计算机网络)第五章传输层-第五节:TCP拥塞控制

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;拥塞控制概述二&#xff1a;拥塞控制四大算法&#xff08;1&#xff09;慢开始和拥塞避免A&#xff1a;慢启动&#xff08;slow start&#xff09;…

CTFer成长之路之举足轻重的信息搜集

举足轻重的信息搜集CTF 信息搜集 常见的搜集 题目描述: 一共3部分flag docker-compose.yml version: 3.2services:web:image: registry.cn-hangzhou.aliyuncs.com/n1book/web-information-backk:latestports:- 80:80启动方式 docker-compose up -d 题目Flag n1book{in…

设计模式-代理模式

控制和管理访问 玩过扮白脸&#xff0c;扮黑脸的游戏吗&#xff1f;你是一个白脸&#xff0c;提供很好且很友善的服务&#xff0c;但是你不希望每个人都叫你做事&#xff0c;所以找了黑脸控制对你的访问。这就是代理要做的&#xff1a;控制和管理对象。 监视器编码 需求&…

数据挖掘,计算机网络、操作系统刷题笔记49

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记49 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…

Spring Cloud Alibaba 微服务简介

微服务简介 1 什么是微服务 2014年&#xff0c;Martin Fowler&#xff08;马丁福勒 &#xff09; 提出了微服务的概念&#xff0c;定义了微服务是由以单一应用程序构成的小服务&#xff0c;自己拥有自己的进程与轻量化处理&#xff0c;服务依业务功能设计&#xff0c;以全自动…

将Nginx 核心知识点扒了个底朝天(四)

为什么 Nginx 不使用多线程&#xff1f; Apache: 创建多个进程或线程&#xff0c;而每个进程或线程都会为其分配 cpu 和内存&#xff08;线程要比进程小的多&#xff0c;所以 worker 支持比 perfork 高的并发&#xff09;&#xff0c;并发过大会榨干服务器资源。 Nginx: 采用…

程序员35岁中年危机不是坎,是一把程序员自己设计的自旋锁

有时候&#xff0c;我会思考35岁这个程序员的诅咒&#xff0c;确切来说是中国程序员的独有的诅咒。 优秀的程序员思维逻辑严谨&#xff0c;弄清楚需求的本质是每天重复的工作&#xff0c;也是对工作的态度&#xff0c;那弄清楚诅咒的来源&#xff0c;义不容辞。 被诅咒的35岁 …

【爬虫】自动获取showdoc指定项目中的所有文档

▒ 目录 ▒&#x1f6eb; 导读需求1️⃣ 格式分析官方下载文件内容prefix_info.json文件格式2️⃣ 封包分析/api/page/info/api/item/info3️⃣ 编码代码特点问题&#x1f4d6; 参考资料&#x1f6eb; 导读 需求 showdoc是一个API文档、技术文档工具网站&#xff0c;经常能搜到…

String intern方法理解

1、原理 参考学习视频&#xff1a; https://www.bilibili.com/video/BV1WK4y1M77t/?spm_id_from333.337.search-card.all.click&vd_source4dc3f886f5ce1d43363b603935f02bd1 String s1 “hello”; String s1 "hello"; 代码原理解释如下图String s1 new Str…

进程章节总结性实验

进程实验课笔记 本节需要有linux基础&#xff0c;懂基本的linux命令操作即可。 Ubuntu镜像下载 https://note.youdao.com/s/VxvU3eVC ubuntu安装 https://www.bilibili.com/video/BV1j44y1S7c2/?spm_id_from333.999.0.0 实验环境ubuntu22版本&#xff0c;那个linux环境都可以…