草庐IT

MySql性能优化(七)查询优化

我本半山人 2023-04-03 原文

文章目录

优化数据访问

思路

1.查询性能低下的主要原因是因为访问数据太多,某些查询不可避免的需要筛选大量的数据,我们可以通过减少访问数据的方式进行数据优化

1.确认应用程序是否在检索大量超过需要的数据
2.确认mysql服务器层是否在分析大量超过需要的数据行

2 . 是否数据库请求了不需要的数据

1.查询不需要的记录

	mysql实际上总会返回全部结果再进行计算,优化方式就是在查询后面加上limit
	比如 明知道只有一条的数据,在结尾加上 limit 1
	
2.多表关联时返回全部列
	
3.总是取出全部列

4.重复查询相同的数据
	
	如果不断的重复执行相同的查询,每次都返回相同的数据
	优化方式:可以将这部分数据缓存起来提高查询效率

执行过程优化


1.查询缓存

在解析一个查询语句之前,如果查询缓存是打开的,mysql会有限检查这个查询是否命中缓存当中的数据,如果刚好命中,那么返回结果之前会检查用户权限,如果权限没问题,那么mysql会跳过所有的阶段直接从缓存当中拿到结果返回客户端


2.查询优化处理

MySql查询完缓存之后会经过以下几个步骤,解析SQL、预处理、优化SQL执行计划,这几个步骤出现任何的错误,都可能会终止查询


2.1 词法解析器预处理

mysql通过关键字将sql语句进行解析,并且生成一颗解析树,mysql解析器将使用mysql语法规则进行验证和解析查询,例如验证是否使用了错误的关键字,顺序是否正确等等,预处理器会进一步检查解析树是否合法,例如表名和列名是否存在,是否有歧义,还会验证权限等等


2.2 查询优化器

当语法树没有问题之后,由优化器将其转化为执行计划,一条查询语句可以使用非常多的执行方式,最后都可以得到对应的结果,但是不同的执行方式带来的效率是不同的,优化器的主要目的就是选择最有效的执行计划。
mysql使用的是基于成本的优化器,在优化的时候会尝试预测一个查询使用某种查询计划时候的成本,并选择其中成本最小的一个。

CBO 基于成本的优化,RBO基于规则的优化

Question: 在很多情况下mysql会选择错误的执行计划,原因如下:

1.统计信息不准确

innoDB因为其mvcc的架构,并不能维护一个数据表的行数的精确统计信息

2.执行计划的成本估算不等同于实际的执行成本

有时候某个执行计划虽然需要读取更多的页面,但是成本确更小,因为如果这些页面都是顺序读或者已经在内存当中的话,它的访问将很小。mysql层面并不知道哪些页面在内存当中哪些页面在磁盘当中,所以查询当中需要耗费多少IO是无法得知的。

3.MySql不考虑其他并发执行的查询

MySql的优化是基于成本模型的优化,但是可能不是最快的优化

4.MySql不会考虑不受其控制的操作成本

执行存储过程或者用户自定义函数的成本


2.3 优化器的优化策略

静态优化

直接对解析树进行分析,并完成优化

动态优化

动态优化与查询的上下文有关,也可能跟取值,索引对应的行数有关

TIPS

MySql对静态优化只需要一次,但动态优化在每次执行时都需要重新评估


2.4 优化器的优化类型

  1. 重新定义关联表的顺序

     数据表的关联不总是按照查询中指定的顺序进行,决定关联顺序是优化器的一个重要功能。
    
  2. 将外连接转换成内连接,内连效率高于外连

  3. 使用等价交换的规则,mysql可以使用一些等价变化来简化并规划表达式

  4. 优化count(),min(),max()

     索引和列是否为空可以帮助mysql优化这些类型的表达式,
     比如要找到某一列的最小值,只需要查询索引最左端的数据即可,不必全表扫描
    
  5. 预估并转化常数表达式,当mysql检查到一个表达式可以转化为常数的时候,就会一直把该表达式当作常数处理

  6. 索引覆盖扫描,当索引值的列包含所有查询中所需要使用的列的时候,可以使用覆盖索引

  7. 子查询优化

     mysql在某些情况下可以将子查询转换成一种效率更高的形式,从而减少多个查询多次对数据进行访问,例如将经常查询的数据放入缓存当中
    
  8. 等值传播

     比如两张表使用id进行关联,where指定的时候只指定了一个id的范围(对另外一张表也生效)
    

2.5 关联查询

官方文档-Nested-Loop Join Algorthims
1.Simple Nested-Loop Join
简单嵌套循环连接实际上就是简单粗暴的嵌套循环,如果table1有1万条数据,table2有1万条数据,那么数据比较的次数=1万 * 1万 =1亿次,这种查询效率会非常慢。

2.Index Nested-Loop Join(索引嵌套循环连接)
索引嵌套循环是使用索引减少扫描的次数来提高效率的,所以要求非驱动表上必须有索引才行。
在查询的时候,驱动表会根据关联字段的索引进行查询,当索引上找到符合的值,才会进行回表查询。如果非驱动表的关联字段是主键的话,查询效率会非常高(主键索引结构的叶子结点包含了完整的行数据(InnoDB)),如果不是主键,每次匹配到索引后都需要进行一次回表查询(根据二级索引(非主键索引)的主键ID进行回表查询),性能肯定弱于主键的查询。

3.Block Nested-Loop Join(缓存块嵌套循环连接)
如果存在索引,那么会使用index的方式进行join,如果join的列没有索引,被驱动表要扫描的次数太多了,每次访问被驱动表,其表中的记录都会被加载到内存中,然后再从驱动表中取一条与其匹配,匹配结束后清除内存,然后再从驱动表中加载一条记录 然后把被驱动表的记录在加载到内存匹配,这样周而复始,大大增加了IO的次数。为了减少被驱动表的IO次数,就出现了Block Nested-Loop Join的方式。
不再是逐条获取驱动表的数据,而是一块一块的获取,引入了join buffer缓冲区,将驱动表join相关的部分数据列(大小是join buffer的限制)缓存到join buffer中,然后全表扫描被驱动表,被驱动表的每一条记录一次性和join buffer中的所有驱动表记录进行匹配(内存中操作),将简单嵌套循环中的多次比较合并成一次,降低了非驱动表的访问频率。
3.1 TIPS

  1. join buffer 会缓存所有参与查询的列而不是只有join的列
  2. 可以调整join_buffer_size的大小
  3. join_buffer_sizc的默i认值是256K,join_buffer_siz的最大值在MySQL5.1.22版本前是4G-1,而之后的版本才在64位操作系统下申请大于4G的们oin Buffers空间。
  4. 使用Block Nested-Loop Join!算法需要开启优化器管理配置的optimizer switch的设置block_nested_loop为on,默认为开启
  5. show variables like ‘%optimizer_switch%’

2.6 排序优化

1.两次传输排序

第一次数据读取是将需要排序的字段读取出来,然后进行排序

第二次是将排好序的结果按照需要去读取数据行,这种方式效率比较低,原因是第二次读取数据的时候因为已经排好序,需要去读取所有的记录,此时更多的是随机O。读取故据成本会比较高。

两次传输的优势,在排序的时候存储尽可能少的数据,让排序缓冲区可以尽可能多的容纳行数来进行排序操作

2.单次传输排序

先读取查询所需要的所有列,然后再根据给定列进行排序,最后直接返回排序结果,此方式只需要一次顺序IO读取所有的放据,而无须任何的随机IO。

问题在于查可的列特别多的时候,会占闲大量的存储空间,无法存储大量的数据

3.TIPS

当需要排序的列的总大小加上order_by的列大小超过max_length_for_sort_data定义的字节,mysql会选择两次传输排序,反之使用单次传输排序。当然用户可以设置此参数的值来设置排序的方式


优化特定类型的查询

1. 优化count() 查询

1. myinsm 的count函数只有没有任何where条件的时候,count(*)才比较快
2. 一般情况下,count()需要扫描大量的行才能获取到准确的数据,其实不太容易优化,
       在实际操作的过程当中可以考虑使用索引覆盖扫描,或者增加汇总表,或者增加外部缓存系统

2. 优化关联查询

 1.确保on或者using子句当中的列上有索引,在创建索引时就需要考虑到关联的顺序
 2.确保任何的groupby和orderby中表达式只涉及到一个表中的列,这样mysql才能用索引来优化这个过程 

3. 优化子查询

 尽量使用关联查询代替子查询(子查询筛掉的值多的话还是子查询,不然临时表io不如直接join了)

4. 优化limit分页

 尽量使用覆盖索引,而不是查询所有的列

4. 优化union查询

除非确实需要服务器消除重复的行,否则一定要使用union all,因为没有all关键字的时候,mysql会在查询的时候给临时表加上distinct关键字,这个操作代价很高

有关MySql性能优化(七)查询优化的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  3. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  4. sql - 查询忽略时间戳日期的时间范围 - 2

    我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

  5. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

  6. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  7. ruby-on-rails - solr 清理查询 - 2

    我在Rails上使用带有ruby​​的solr。一切正常,我只需要知道是否有任何现有代码来清理用户输入,比如以?开头的查询。或* 最佳答案 我不知道执行此操作的任何代码,但理论上可以通过查看parsingcodeinLucene来完成并搜索thrownewParseException(只有16个匹配!)。在实践中,我认为您最好只捕获代码中的任何solr异常并显示“无效查询”消息或类似信息。编辑:这里有几个“sanitizer”:http://pivotallabs.com/users/zach/blog/articles/937-s

  8. ruby - Ruby 性能中的计时器 - 2

    我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

  9. ruby-on-rails - Rails 3 在一个查询中包含多个表 - 2

    我正在为锦标赛开发一个Rails应用程序。我在这个查询中使用了三个模型:classPlayertruehas_and_belongs_to_many:tournamentsclassTournament:destroyclassPlayerMatch"Player",:foreign_key=>"player_one"belongs_to:player_two,:class_name=>"Player",:foreign_key=>"player_two"在tournaments_controller的显示操作中,我调用以下查询:Tournament.where(:id=>params

  10. ruby-on-rails - Sunspot:如何对具有不同值的多个字段进行全文查询? - 2

    我想用sunspot重现以下原始solr查询q=exact_term_text:fooORterm_textv:foo*ORalternate_text:bar*但我无法通过标准的太阳黑子界面理解这是否可能以及如何实现,因为看起来:fulltext方法似乎不接受多个文本/搜索字段参数我不知道将什么参数作为第一个参数传递给fulltext,就好像我通过了"foo"或"bar"结果不匹配如果我传递一个空参数,我得到一个q=*:*范围过滤器(例如with(:term).starting_with('foo*')(顾名思义)作为过滤器查询应用,因此不参与评分。似乎可以手动编写字符串(或者可能使

随机推荐