MySQL调优思路(回表、LRU算法、索引下推、预读取失效、缓冲区污染、刷脏、sql执行流程、redo、undo、bin log)

本文主要是介绍MySQL调优思路(回表、LRU算法、索引下推、预读取失效、缓冲区污染、刷脏、sql执行流程、redo、undo、bin log),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

回表、避免回表、索引下推、刷脏、LRU算法、索引覆盖、预读取失效、缓冲区污染、rodo Log、undo Log、bin Log、sql执行流程

文章目录

  • 前言
  • 一、一条Sql在MySQL的执行流程?
    • 1、总体流程:
    • 1.1、查询操作流程(query):
      • 1.1.1、具体介绍每个节点
    • 1.2、更新操作流程(update):
    • 1.3、相关概念
      • 1.3.1、redo Log & undo Log & bin Log
      • 1.3.2、刷脏(刷盘)
  • 二、MySQL的存储引擎相关
    • 1.innodb
      • 1.1、innodb三大特性:
    • 2.LRU算法
      • 2.1、传统的LRU算法
      • 2.2、传统LRU算法的问题
      • 2.3、MySQL的LRU算法
  • 三.索引
    • 1、索引分类(主键索引&普通索引)
    • 2、B+Tree
      • 2.1、B-Tree和B+Tree区别?
      • 2.2、B+Tree特点
      • 2.3、B+Tree缺点
    • 3、索引结构
    • 4、回表&怎么避免回表
    • 5、索引下推
      • 5.1、索引下推解释
      • 5.2、索引下推的条件:
  • 四、explain的参数
    • 4.1、type字段(性能从好到差)
    • 4.2、key
    • 4.3、rows
    • 4.4、extra
  • 五、调优方向
    • 5.1、单表大(分区、分表)
    • 5.2、没索引查不动
    • 5.3、有索引查不动
  • 六、总结


前言

闲来无事,就准备写一篇MySQL的文章来帮助一些萌新童鞋更好的了解MySQL数据库。MySQL文档官方网址
当然,本人水平有限,如有误导,欢迎斧正,一起学习,共同进步!


一、一条Sql在MySQL的执行流程?

正常的一条sql写完是怎么在数据库中执行的呢,它的具体执行流程是什么呢?

1、总体流程:

1.1、查询操作流程(query):

查询缓存(query cache)–>查询解析器(parser)–>预处理器(pre processor)–>查询优化器(query optimizer)–>执行计划(explain plan)–>执行器(Executor)–>存储引擎(Storage Engine)
在这里插入图片描述

1.1.1、具体介绍每个节点

查询缓存:是说第一次查询以后,第二次如果是同样的sql,就不查数据了,直接从缓存中拿结果。但是这个MySQL官网已经不推荐使用了,一来是不好用(命中效率低),二来是有替代它(Buffer Pool)的了。

不好用的原因是因为:但凡sql有一丁点不一样,就不会走缓存。如下示例
select name from user where subject = ‘语文’
select name from user as u where subject = ‘语文’ ##加了别名,不会走缓存
select name from user where subject = ‘语文’
select name from user as u where subject = ‘语 文’ ##加了空格,不会走缓存

查询解析器:查询解释器主要作用是看有没有语法错误的,有些类似于Java的编译,编译通过了才能进行下一步的操作
查询解析器:查询解释器主要作用是看有没有语法错误的,有些类似于Java的编译,编译通过了才能进行下一步的操作。内部有词法解析、语法解析,生成解析树,通过判断关键词来判断sql的语法有没有问题。
预处理器:预处理器是进一步校验上面的解析树是否合法,校验语法是不是通的。可以简单的理解为,查询解释器是用来看这句话有没有语病,预处理器是看这句话能不能读的通。
查询优化器:这步主要是对你的sql进行优化的。比如说,你写的sql语句不符合最左匹配原则,如果按你的sql查的话,是不会走对应的索引的,但是结果却是走索引了,这就是查询优化器给你做的优化。又或者是多表查询时,你把数据量小的表(筛选力度大的表)写在后面了,优化器也会帮你优化的。
执行计划:Explain是我们Sql优化时最常用的关键字。在下面会详细介绍。可以先简单介绍一下,explain的参数中,key有的比没有的效率高;rows小的比大的效率高,type类型中前面分类的比后面分类的效率高
执行器:执行器就是去存储引擎中查数据的。

1.2、更新操作流程(update):

在这里插入图片描述
比如说, name=“天明” 改成(update) name=“明天”

  1. 开启事务
  2. client >
  3. server > …(还是查询那一套) 查询到name=“天明”
  4. engine 引擎中
  5. buffer pool & log buffer
  6. 修改name=“明天” (此时修改的是内存,不是db file磁盘)
  7. 记录name=”明天“到redo log便于重做(刷盘,刷脏)
  8. 记录bin log日志以便于备份恢复
  9. 记录undo log name=“天明” 以便于回滚(注意,这里是写反的)
    10.提交事务,修改db file
  10. doubleWrite双写机制确保刷脏成功

1.3、相关概念

1.3.1、redo Log & undo Log & bin Log

  • redo Log:数据恢复。比如你写数据写到一半,数据库宕机了,出问题了,那么怎么还原数据呢,就是通过这个redo 日志去实现的(redo log是用来恢复数据的 用于保障,已提交事务的持久化特性)。mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Boffer Pool(缓冲池)里头,把这个当作缓存来用。然后使用后台线程去做缓冲池和磁盘之间的同步。那么问题来了,如果还没来的同步的时候宕机或断电了怎么办?还没来得及执行上面图中红色的操作。这样会导致丢部分已提交事务的修改信息!所以引入了redo log来记录已成功提交事务的修改信息,并且会把redo log持久化到磁盘,系统重启之后在读取redo log恢复最新数据。
  • undoLog:事务回滚时用的。(提交事务之前,先把undo log写好,然后再去修改db file 文件。在你修改db file文件的过程中,如果事务有问题了,那么就通过这个undo log去回滚)而且这个undo log日志,存的是相反的。就是比如你的是insert操作,那么undolog中存的就是delete操作。如果你update的是天明,那么undo log存的就是明天
  • binlog:做备份和恢复的。将全部的 DDL、DML 操作记录下来,一旦数据库宕机或异常,就把全部的bin Log日志执行一遍,来达到恢复数据的效果。

1.3.2、刷脏(刷盘)

刷脏刷脏,什么是脏,比如说 update name = “天” 。update之前 name是"明"。那首先查出来的,肯定是明。那么buffer pool里面的,就是叫明。然后你update之后,你的库里面的name现在是天了,但是你的buffer pool里面还是明,是对不上的。如果不一样的时候,我们就称之为脏数据。为了保证数据一致,就有一个双写机制,也就是刷脏。

二、MySQL的存储引擎相关

以下是从官网截取的截图,感兴趣的童鞋可以去官网中自行查看(前言部分有官网地址),在次我作着重介绍InnoDB和MyISAM。在这里插入图片描述

1.innodb

1.1、innodb三大特性:

1、Buffer Pool:意思是,第一次查库,然后就放到buffer pool中了,然后下次就从buffer pool中查。
2、Adaptive Hash Index:发现使用二级索引,频繁查询(大于等于3次的时候,)就会把数据当做是热数据放到这个自适应hash索引中去。
3、DoubleWrite:(引入的目的是)刷脏失败时,通过redo Log 确保重做能正常。日志的预写机制(涉及到刷脏)

2.LRU算法

LRU(least Recently Used):最近最少使用原则。大概意思是,谁用的频率高,我就把谁放在前面,让你更快的被查到。下次直接从内存(Buffer Pool)里面去拿,就不用从数据库中去查了。因为查内存比直接查磁盘速度更快,性能更好。

2.1、传统的LRU算法

就是说,缓存有这么大,你让哪些数据往前,哪些数据往后呢。这个是buffer pool嘛,是内存中加载的。
假设我的内存现在就只能放10个,然后每个里是16kb的数据,然后假设我这次是查的编号是4的内容,那么我就会把4放在最前面。那假设我现在查的数据,这个buffer Pool里面没有,我怎么办呢,那么查出来以后,就会加载到最前面,然后把最后面的(7)给挤出去,就不在内存中了,下次想查,就得从磁盘中查了。
在这里插入图片描述

2.2、传统LRU算法的问题

但是传统的 LRU 会有俩问题:预读取失效、缓冲区污染
预读取失效:你提前把数据写到缓存中去了,但是mysql最终也没有从缓存里面读取数据,都是从库查的,没用到你。
缓冲区污染:比如我一个很大数据的表,然后我还写了个select * from,那么这个select * 查到的数据就会把我缓存区给全部替换了。因为你查的多嘛,然后就算你16k能塞100条数据,你内存(Buffer Pool)一共能放100个16k,那你数据量一大的话,内存(Buffer Pool)中的数据还是会全部的替换成你刚查的这个数据,其他的数据都会被这个数据给挤出去。

2.3、MySQL的LRU算法

其实有点类似于jvm,jvm的堆内存空间嘛,新生代比老年代默认是1比2(其实早已不是1:2了。).mysql的lru是把内存(Buffer Pool)分为两部分,一部分New、一部分Old。默认是New占63%,Old占37%。这是默认。new区和old区是首尾相连的,new的最后一个连着old的开头。大概是,查到数据以后,优先塞到 old区。然后确定这个数据是热点数据了,好多人都查这个数据话,就把这个数据移动到new区(默认是,过了1秒钟,热度仍在,就会移动到new区)。这样的话,即使是污染数据,也只是污染了old区,而对真正的热点数据存的new区影响不大。
在这里插入图片描述

三.索引

1、索引分类(主键索引&普通索引)

其实对于Mysql来说,只有两类索引:Secondary Index(二级/辅助索引)、Clustered Index(聚集/主键索引)。他们最主要的区别就是,是否存了行数据row date
常见的数据结构有:数组、链表、哈希、树、网、图。其中适合做索引的有树、哈希。

Clustered Index(聚集/主键索引):如果有主键id,则id自然而然是聚集索引;如果没有主键,那么就会找唯一且非空的索引做聚集索引;如果既没有主键id,又没有唯一且非空的索引,就会创建一个rowId来当聚集索引。
Secondary Index(二级/辅助索引):其实也是b树,只不过最下面的叶子节点,只存了id,没有存完整的数据;而主键索引是存的完整的数据(行数据)的。
一般一个表中,只有主键是Clustered Index,其他索引是普通索引。对于索引来说,只要有一个索引(比如说索引1)存全部的数据就行,其他索引(比如是索引2)存一个前面索引(索引1)的id就行。就比如说,Java中的一个方法写好一个逻辑,其他方法直接调用前面的方法就行,而不是把逻辑在写一遍。所以,主键索引的叶子节点存行的全部数据,普通索引的叶子节点存主键索引的id。因为如果你的普通索引也像主键索引一样,叶子节点存全部的行数据的话,不仅会造成冗余,重复造轮子,而且这样的话索引的数据量太大,光索引就这么大,那数据量呢,索引也失去了它原有的意义。

2、B+Tree

2.1、B-Tree和B+Tree区别?

先说一下原理,二叉树可以是那种左右对称的平衡二叉树,但是也可以是那种一条腿的二叉树,而如果是一条腿的,效率就会很差。b+tree就是为了避免这个情况出现的。
区别
B+Tree是BTree的一个变种,设d为树的度数,h为树的高度,B+Tree和BTree的不同主要在于:
①、B+Tree中的非叶子结点不存储数据,只存储键值;B 树存储数据。
②、B+Tree的查询更稳定,因为不会出现那种一条腿的情况。
③、B+Tree的范围查询效率更高。因为B+Tree的数据都存在叶子节点,并且有相邻节点的指针的指向,而B树的数据是存在每个节点上的,更加分散。
④、B+Tree的叶子结点没有指针,所有键值都会出现在叶子结点上,且key存储的键值对应data数据的物理地址;
⑤、B+Tree的每个非叶子节点由n个键值key和n个指针point组成;
好处
①、磁盘读写代价更低
那么提升查找速度的关键就在于尽可能少的磁盘I/O,那么可以知道,每个节点中的key个数越多,那么树的高度越小,需要I/O的次数越少,因此一般来说B+Tree比BTree更快,因为B+Tree的非叶节点中不存储data,就可以存储更多的key
②、查询速度更稳定
由于B+Tree非叶子节点不存储数据(data),因此所有的数据都要查询至叶子节点,而叶子节点的高度都是相同的,因此所有数据的查询速度都是一样的。
很多存储引擎在B+Tree的基础上进行了优化,添加了指向相邻叶节点的指针,形成了带有顺序访问指针的B+Tree,这样做是为了提高区间查找的效率,只要找到第一个值那么就可以顺序的查找后面的值。

2.2、B+Tree特点

1、非叶子节点没有date数据,存键值(索引的值,指针的键)信息
2、所有的叶子节点都有一个双向的指针
3、data全放在叶子节点上

2.3、B+Tree缺点

既然b+树这么好(叶子节点是双向链表,范围、等值查都快;非叶子节点不存数据只存索引,让树更矮,进行io操作更少),那能不能全部的索引都用b+树呢,这样的话,就相当于你重复造轮子了,比如说你有一个逻辑,然后用a方法(主键索引)封装好了,你b方法(普通索引)又把一模一样的逻辑封装了一遍,还不如直接b方法里面调用a方法。MySQL就是这么做的,主键索引存全部的行数据,普通索引存主键索引的主键id。b+树也有缺点,就是占的空间太大了,因为是全部的数据都存起来,而不是只存索引。

3、索引结构

在这里插入图片描述
如图所示,如果是主键索引的话,那么叶子节点中,就会把这行的数据都存下来。如果是普通索引的话,最下面的叶子节点只会存一个主键索引的id。这就涉及到了一个回表的操作。

4、回表&怎么避免回表

回表:第一次走索引树查数据,没有把完整的数据获取到,还需要根据主键id再查一次,把其他的数据也查出来。(select name,type from user) 但是你只给了name加索引了,那么就需要回表。先根据name找到索引的叶子节点,因为name是普通索引,那么叶子节点的数据只会存主键索引的索引id,主键索引的叶子节点,才存的是完整的数据行。即:先根据name索引,去找到主键索引的索引id,再根据主键索引的索引id,去查到完整的数据行数据(即拿到type的值),这就是回表,很明显的发现,进行了两次io操作。

那怎么避免回表呢?
①、就是覆盖索引。什么是覆盖索引呢?就是比如说你查 name、type。需要回表,那你直接建立一个name、type的联合索引,不就不需要回表了,因为全部的数据都被你查出来了啊
②、索引下推(索引下推的目的就是为了减少回表的次数)。

5、索引下推

5.1、索引下推解释

索引下推 icp(Index Condition Pushdown)。要想有索引下推,前提条件就是走的二级索引,
目的:为了减少查询的次数,提高查询的效率
设计思路:将部分上层(服务层)负责的事,交给下层(引擎层)去处理
标识:在explain的 Extra 字段中 Using index condition 表示出现了索引下推。
正常的查询流程是: 客户端访问数据库服务端,数据库服务端去调搜索引擎(innoDB,MyISAM),搜索引擎返回数据给服务端,数据库服务端返回给客户端。
在这里插入图片描述

比如说,
你有一个 name + phone 的联合索引。如果你的数据中,有 天明、天赐、天宇等三条数据,且天明的phone是15631698081。

explain select * from group_user where name like '天%' and phone = 15631698081;

如果没有索引下推(icp) 的话,是客户端查数据库服务端,数据库服务端查数据引擎,数据引擎根据“天”去联合索引中查,查到这些数据(此时是天明、天赐、天宇这三数据)的索引id,然后回表查到完整数据行(回表3次,因为给server端肯定是要给全部的数据行的,不能说给部分数据的。)。然后返回给数据库服务端,数据库服务端在根据phone去进行过滤(引擎是查出了全部的“天“开头的数据,然后回表查全部的“天”数据,然后都推给了服务端,数据库服务端自己去过滤)。
如果有索引下推 的话,则同样是客户端调用数据库服务端,服务端调用引擎端,然后数据引擎根据“天”去查出全部的数据以后,在根据phone的条件去过滤。然后再根据筛选后的数据(此时只有天明一条数据)的索引id去进行回表操作(此时只回表1次,),然后将数据推送给数据库服务端。服务端已经不用再过滤了。这样一来,回表的次数就减少了。

其实这又引出了最左匹配原则。因为你可能觉得,为啥查出来的是“天明”、“天赐”、“天宇”这三数据,而不是同时根据name和phone来联合查出来的呢。就是因为,最左匹配原则,因为InnoDB的索引的默认结构是B+树,而构建b+树只能是一个值来构建节点,但你又是联合索引,那怎么存呢,就是最左边的字段。

5.2、索引下推的条件:

1、二级索引
2、InnoDB引擎和MyISAM引擎
3、只能用于type是 range、ref、eq_ref、ref_or_null 访问方法

四、explain的参数

在这里插入图片描述
对于mysql来说,直接explain关键字就行。其中值得在意的有三个字段,type、key、rows、extra。

4.1、type字段(性能从好到差)

system:该表只有一行(相当于系统表),system是const类型的特例
const:针对主键或者唯一索引的等值查询结果,最多只返回一行。查询速度很快,因为只读取一次即可。
eq_ref:使用了索引的全部组成部分,并且索引是primary key或者unique not null 才会使用该类型
ref:满足了索引的最左前缀原则,或者索引不是主键也不是唯一索引的时候会走这个。如果使用的索引只会匹配一小部分,性能也是不错的。

index:全索引扫描,和all类似,只不过index是全盘扫描了索引的数据。(两种场景会触发:a、只查索引就会满足查询中所需的数据;b、按索引的顺序去查找数据行,执行了全表扫描,比如说分页查 10000到10010的数据)
all:全表扫描,性能最差。

4.2、key

其实就是单纯的索引的名称,如果有就是你定义的索引名;没走索引就是null

4.3、rows

此次查询所涉及的行,行数越小越好

4.4、extra

主要是一些标识。比如说 extra的值为Useing index condition 是索引下推。

五、调优方向

其实调优嘛。也就那几个方向。单表数据量大、没索引、有索引查的慢。

5.1、单表大(分区、分表)

单表数据量大的话,可以分区,可以分表。

  • 分区是说,比如你的订单数据,一年的量太大了,你按季度来分,第一季度是一个表,第二季度是一个表,如果单个季度表的数据还大的话,还可以继续分,第一个月是一个表,第二个月是一个表等等。
  • 分表是说,其实也是一个大表拆分为小表。不管是MyCat还是Sharding-JDBC,原理都是一样的,都是根据索引规则来分片(比如说对id求hash,hash值是1的存这个表,hash值是2的存那个表),然后存到不同的地方。只不过一个是客户端代理,一个是服务器端代理而已。

5.2、没索引查不动

经过上面的操作,大表已经变成了小表,小表还查不动的话,就加索引呗,加了索引一般都会变快。当然,也有情况是,不加索引(全表扫描)更快。加了索引反而更慢。不过这种情况比较少见,属于具体情况具体分析了。

5.3、有索引查不动

有索引查不动,就explain看原因,就是explain的几个参数来看原因,找问题。


六、总结

其实只要合力的创建表结构,合力的构建表索引,即使你的单表是千万级别的数据,依然能查的很快。因为通常情况下,索引的B+Tree是3、4层,而第1、2层节点几乎都能加载到内存中,第3、4层需要进行io操作,也就是2次io就能查到数据,所以效率还是很可观的。

这篇关于MySQL调优思路(回表、LRU算法、索引下推、预读取失效、缓冲区污染、刷脏、sql执行流程、redo、undo、bin log)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/261124

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

如何去写一手好SQL

MySQL性能 最大数据量 抛开数据量和并发数,谈性能都是耍流氓。MySQL没有限制单表最大记录数,它取决于操作系统对文件大小的限制。 《阿里巴巴Java开发手册》提出单表行数超过500万行或者单表容量超过2GB,才推荐分库分表。性能由综合因素决定,抛开业务复杂度,影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考,并非铁律。 博主曾经操作过超过4亿行数据

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间