SQL性能优化


mysql架构图

大体来说,MySQL可以分为Server层存储引擎层两部分。

  • 包括连接器查询缓存分析器优化器执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程触发器视图等。
  • 负责数据的存储和提取。其架构模式是插件式的,支持InnoDBMyISAMMemory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎
    也就是说,你执行create table建表的时候,如果不指定引擎类型,默认使用的就是InnoDB。不过,你也可以通过指定存储引擎的类型来选择别的引擎,比如在create table语句中使用engine=memory, 来指定使用内存引擎创建表。不同存储引擎的表数据存取方式不同,支持的功能也不同,在后面的文章中,我们会讨论到引擎的选择。
查询缓存

不建议使用,查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。除非你的业务就是有一张静态表,很长时间才会更新一次。比如,一个系统配置表,那这张表上的查询才适合使用查询缓存。

分析器

如果没有命中查询缓存,就要开始真正执行语句了。首先,MySQL需要知道你要做什么,因此需要对SQL语句做解析。

索引


事务

提到事务,你肯定不陌生,和数据库打交道的时候,我们总是会用到事务。最经典的例子就是转账,你要给朋友小王转100块钱,而此时你的银行卡只有100块钱。
转账过程具体到程序里会有一系列的操作,比如查询余额、做加减法、更新余额等,这些操作必须保证是一体的,不然等程序查完之后,还没做减法之前,你这100块钱,完全可以借着这个时间差再查一次,然后再给另外一个朋友转账,如果银行这么整,不就乱了么?这时就要用到“事务”这个概念了。
简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。 在MySQL中,事务支持是在引擎层实现的。你现在知道,MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如MySQL原生的MyISAM引擎就不支持事务,这也是MyISAM被InnoDB取代的重要原因之一

隔离性与隔离级别

提到事务,你肯定会想到ACID(Atomicity、Consistency、Isolation、Durability,即原子性一致性隔离性持久性),接下来我们就来说说其中I,也就是“隔离性”。
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。
在谈隔离级别之前,你首先要知道,因此很多时候,我们都要在二者之间寻找一个平衡点。SQL标准的事务隔离级别包括:读未提交(read uncommitted)读提交(read committed)可重复读(repeatable read)串行化(serializable )。下面我逐一为你解释:


  • 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
    给个图方便理解一下这几个比较抽象的概念
mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);
image.png

我们来看看在不同的隔离级别下,事务A会有哪些不同的返回结果,也就是图里面V1、V2、V3的返回值分别是什么。

  • 若隔离级别是“读未提交”, 则V1的值就是2。这时候事务B虽然还没有提交,但是结果已经被A看到了。因此,V2、V3也都是2。
  • 若隔离级别是“读提交”,则V1是1,V2的值是2。事务B的更新在提交后才能被A看到。所以, V3的值也是2。
  • 若隔离级别是“可重复读”,则V1、V2是1,V3是2。之所以V2还是1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
  • 若隔离级别是“串行化”,则在事务B执行“将1改成2”的时候,会被锁住。直到事务A提交后,事务B才可以继续执行。所以从A的角度看, V1、V2值是1,V3的值是2。

各数据库隔离级别

  • Oracle 读提交
  • Mysql 可重复读
  • SqlServer 读提交


引擎

对比项 InnoDB MyIsAM
事务 支持 不支持
支持MVCC行锁 表锁
外键 支持 不支持
存储空间 存储空间由于需要高速缓存,较大 可压缩
适用场景 有一定量的update和Insert 大量的select

必须使用InnoDB存储引擎 解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高 来自58同城30条军规



  • 数据库的事务、ACID及隔离级别?
    事务:事务就是要保证一组数据库操作,要么全部成功,要么全部失败。ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)。
    当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。 SQL标准的事务隔离级别包括:读未提交、读提交、可重复读、和串行化
    注意: 隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。 mysql默认隔离级别为
  • 不考虑事务的隔离性,容易产生哪三种情况?
    脏读、幻读、可重复读
  • 数据库连接池原理?
    连接的建立和释放都会耗费系统资源,大型网站在频繁获取释放连接时势必会影响性能,为了解决资源的频繁分配﹑释放所造成的问题池化技术应运而生(参考线程池),数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。
  • MySQL数据库索引结构?
    问这个问题心里没点B+树吗?
  • 什么是索引?什么条件适合建立索引?什么条件不适合建立索引?
    索引是为了加速对表中数据行的检索而创建的一种分散存储的数据结构。一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。一本500页的书,如果你想快速找到其中的某一个知识点,在不借助目录的情况下,那我估计你可得找一会儿。同样,对于数据库的表而言,索引其实就是它的“目录”。
    适合建立索引的条件: 主键、经常作为查询条件的字段、离散型大的字段(性别这种字段就是离散型小)、占用空间小的字段
    不适合简历索引的条件: 上面反过来呗~ 更新过于频繁的字段不建议 加索引
    注意:索引一定不是越多越好,越全越好,一定是建合适的。 不要在更新十分频繁、区分度不高的属性上建立索引
  • 索引失效的原因有哪些?如何优化避免索引失效?
    1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)   要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
    2.对索引列运算,运算包括(+、-、*、/、!、<>、%、like’%_’(%放在前面)、or、in、exist等),导致索引失效。
    3.如果MySQL预计使用全表扫描要比使用索引快,则不使用索引
  • MySQL如何启动慢查询日志?
    建议去百度一下,我写在这也是贴过来的 MySQL如何启动慢查询日志?
  • MySQL如何使用show Profile进行SQL分析?
    答案是肯定的,因为前置步骤进行了双写,所以理论上数据迁移完之后,新库与旧库的数据应该完全一致。
  • 一条执行慢的SQL如何进行优化,如何通过Explain+SQL分析性能?
    使用explain关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的sql语句,可以帮助选择更好的索引和写出更优化的查询语句。。如何通过Explain+SQL分析性能?
  • 什么是行锁、表锁、读锁、写锁,说说它们各自的特性?
    读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
    写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
    表锁: 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突概率高,并发度最低。加锁方式:自动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁。
    行锁: 开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高。加锁方式:加锁的方式:自动加锁。查询操作(SELECT),会自动给涉及的所有表加读锁,更新操作(UPDATE、DELETE、INSERT),会自动给涉及的表加写锁。
    注意:无索引会导致行锁升级为表锁, InnoDB 是行锁,行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
  • 如何手动给一条记录加上行锁?
    select * from xxx for update
  • 什么情况下会出现间隙锁?
    在mysql的innoDB存储引擎中,如果更新操作是针对一个区间的,那么它会锁住这个区间内所有的记录,比如update xxx where id between a and b那么它会锁住a到b之间所有记录,注意是所有记录,甚至这个记录并不存在也会被锁住,这个时候,如果另外一个连接需要插入一条记录到a到b之间,那么它就必须等到上一个事务结束。 建议百度一下这玩意比较复杂。。
  • 什么情况下行锁变表锁?
    无索引会导致行锁升级为表锁, InnoDB 是行锁,行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
  • 谈谈你对MySQL的in和exists用法的理解?*
    如果查询的两个表大小相当,那么用in和exists差别不大。如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in
  • MySQL的数据库引擎有哪些,如何确定在项目中要是用的存储引擎?
    答案是肯定的,因为前置步骤进行了双写,所以理论上数据迁移完之后,新库与旧库的数据应该完全一致。
    not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快
    in 和 notin (null)不生效
  • count(星)、count(列名)和count(1)的区别?
    往常在工作中有人会说count(1)比count(*)会快,或者相反,首先这个结论肯定是错的,实际上count(1)和count(*)并没有区别。 不要被误导
    count(*)是针对于全表的,而count(列)是针对于某一列的,如果此列值为空的话,count(列)是不会统计这一行的。所以两者根本没有可比性
  • union和union all的区别?
    都是将两个结果集合并为一个
    1.对重复结果的处理:UNION在进行表链接后会筛选掉重复的记录,Union All不会去除重复记录。
    2.对排序的处理:Union将会按照字段的顺序进行排序;UNION ALL只是简单的将两个结果合并后就返回。
    3.从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复数据且不需要排序时的话,那么就使用UNION ALL。

极客时间MySQL实战45讲

数据库索引的底层原理?那是因为你心里没点b树

你可能感兴趣的:(SQL性能优化)