Mysql面试题,sql优化,存储引擎,数据结构,基础知识等

目录

一、相关知识

什么是MySQL?SQL是什么?

SQL的生命周期?

什么是超键、候选键、主键、外键?

 数据库有哪几个范式,谈谈理解?

MySQL的binlog有有几种录入格式?分别有什么区别?

企业场景如何选择binlog模式

二、数据类型相关

扫描二维码关注公众号,回复: 13238570 查看本文章

int(数值)类型怎么设置有符号,无符号

int(11)中的‘11’有什么用?对存储数值的范围有影响吗?

float(M,D)、double(M,D)、decimal(M,D)中的M代表什么,D代表什么,如果不设置(M,D)会怎么样?

说说char(M)、varchar(M)的区别;enum、set的区别

timestamp和datetime的区别

三、sql语言相关

UNION和UNION ALL的区别

mysql中 in 和 exists 区别

drop、delete与truncate的区别

四、约束相关

约束是什么?常见的约束有哪些?

 企业中经常使用外键约束吗?

五、事务相关

什么是事务?事务的四大特性?

事务并发情况下可能会产生的问题有哪些

事务的隔离级别

mysql和oracle的隔离级别区别

七、视图、存储过程、触发器相关

为什么要使用视图?什么是视图?

视图有哪些特点?

视图的使用场景有哪些?

视图的优点和缺点

什么是游标?

什么是存储过程?有哪些优缺点?

什么是触发器?触发器的使用场景有哪些?

MySQL中都有哪些触发器?

六、存储引擎相关

MyISAM和InnoDB的区别

InnoDB的特点

为什么MyISAM会比Innodb的查询速度快

InnoDB为什么推荐使用自增ID作为主键?

为什么InnoDB没有记录表行数这个变量呢?

七、索引相关

什么是索引?索引有什么优势?有什么缺点?

说说mysql中的索引分类

说说hash、btree、b+tree的数据结构

说说btree和b+tree的区别

为什么Innodb要选择B+tree作为数据结构

什么是聚簇索引?何时使用聚簇索引与非聚簇索引

联合索引是什么?为什么需要注意联合索引中的顺序?

b+tree中innodb不需要回表查询吗?myisam一定会回表查询吗?

什么情况使用了索引,查询还是慢

什么情况下适合建索引什么适合下不适合建索引?

八、锁相关

说说mysql中有那些锁?

有对mysql进行过数据备份和恢复吗?怎么进行的?

什么是死锁?怎么解决?

innodb有哪些行锁算法

九、数据库优化相关

先聊一聊explain各个列代表的内容

根据explain的结果来看,一般要达到什么标准才算合格的sql

常用SQL查询语句优化方法

索引失效的几种情况

两表连接时该如何优化

既然你知道了怎么优化sql,那你说说你是通过什么方式找到性能较差的sql的?

超大量删除一个表中的数据有什么好的方法?

聊一聊limit分页方法的不足点以及如何优化?


一、相关知识

什么是MySQL?SQL是什么?

MySQL:是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品, 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。

SQL:是一种结构化查询语言,是一种数据库查询语言,用来管理数据库(表的操作、数据的操作等)

SQL的生命周期?

  1. 连接:应用服务器与数据库服务器建立一个连接
  2. 获得请求SQL:数据库进程拿到请求sql
  3. 查询缓存:如果查询命中缓存则直接返回结果
  4. 语法解析和预处理:
  5. 首先通过mysql关键字将语句解析,会生成一个内部解析树,mysql解析器将对其解析,查看是否是有错误的关键字,关键字顺序是否正确;预处理器则是根据mysql的规则进行进一步的检查,检查mysql语句是否合法,如库表是否存在,字段是否存在,字段之间是否模棱两可等等,预处理器也会验证权限。
  6. 查询优化器:sql语句在优化器中转换成执行计划,一条sql语句可以有多种方式查询,最后返回的结果肯定是相同,但是不同的查询方式效果不同,优化器的作用就是:选择一种合适的执行计划。mysql是基于成本的优化器,他将预测执行此计划的成本,并选择成本最小的那条
  7. 执行计划,执行SQL:在解析和优化后,MySQL将生成查询对应的执行计划,由执行计划调用存储引擎的API来执行查询
  8. 将结果返回给客户端
  9. 关掉连接,释放资源

什么是超键、候选键、主键、外键?

超键:在关系中能唯一识别元组的属性集成为关系模式的超键,超键可以是单个,也可以是几个属性组合成的

例:学号,姓名,性别,年龄,专业这几个属性中,身份证能够唯一识别一个人,所以身份证是超键;(身份证+姓名)能唯一识别一个人,所以(身份证+姓名)也是超键。

候选键:最小的超键,单个属性

例:学号,姓名,性别,年龄,专业这几个属性中,身份证是唯一的,所以身份证可以是候选键,姓名(假设唯一)是唯一的,所以姓名可以做候选键

主键:用户根据种种因素选择出一个最适合代表元组的能够唯一标识元组的属性

外键:外键指向另一个表主键的,用于两个表之间建立联系,可以用来约束外键表

例:学生表(外键表)有学号(主键)、姓名、年龄、专业编号(外键)等属性,专业表(被外键引用表)有专业编号(主键)、专业课程、专业老师等属性。学生表的专业编号就作为外键指向了专业表的专业编号,当往学生表插入数据时,如果专业编号在专业表中没有这个一样的值的话,就无法插入数据,以此来限制学生表)

关系图(网图):    

 数据库有哪几个范式,谈谈理解?

数据库的范式有三大类:

第一范式(1NF):是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

例:地址这一个属性,如果后面需要获取省份和地市,那么地址这一统称就不满足第一范式,应分割为省、市、详细地址(除了省、市以外的地址信息)

第二范式(2NF):在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

例:在订单信息表中,一个订单会有多个商品,这里使用订单编号和商品编号作为联合主键,这时候如果表这样设计:订单编号、商品编号、商品名称、数量、商品价格、客户、所属单位、联系方式。在这个设计中,商品名称、商品价格仅仅是跟商品编号相关,并不是跟订单编号和商品编号这个联合主键相关,这样设计就不符合第二范式,应该将订单编号、客户、所属单位、联系方式作为订单信息表,将订单编号、商品编号、数量作为订单项目表,将商品编号、商品名称、商品价格作为商品信息表。不符合第二范式不仅造成数据冗余,还造成删除信息的时候会将其他信息删除

第三范式(3NF):消除第二范式的传递依赖,每一个非主属性都只能与主属性直接依赖,而不能传递依赖

例:订单表有订单编号(主键)、订单项目、负责人、客户姓名、客户联系方式、客户住址等。

在这个表中,客户联系方式、客户住址都依赖于客户姓名,而客户姓名依赖于订单编号,之间有传递依赖,这时候可以分为两个表:表一(订单编号(主键)、订单项目、负责人、客户编号);表二(客户编号、客户姓名、客户联系方式、客户住址)

MySQL的binlog有有几种录入格式?分别有什么区别?

  有以下三种

Statement:每一条会修改数据的sql都会记录在binlog中,不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能,由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息

优点:

  • binlog文件较小
  • 日志是包含用户执行的原始SQL,方便统计和审计
  • 出现最早,兼容较好

缺点:

  • 存在安全隐患,可能导致主从不一致
  • 对一些系统函数不能准确复制或是不能复制

Row:不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大

优点:

  • 相比statement更加安全的复制格式
  • 在某些情况下复制速度更快(SQL复杂,表有主键)
  • 系统的特殊函数也可以复制
  • 对于DDL修改表结构时也是用statument

缺点:

  • binlog比较大(myql5.6支持binlog_row_image)
  • 单语句更新(删除)表的行数过多,会形成大量binlog
  • 无法从binlog看见用户执行SQL(5.6中增加binlog_row_query_log_events记录用户的query)
     

Mixed: 是以上两种level的混合使用,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种.新版本的MySQL中队row level模式也被做了优化,并不是所有的修改都会以row level来记录,像遇到表结构变更的时候就会以statement模式来记录。至于update或者delete等修改数据的语句,还是会记录所有行的变更。

特点:

  • 混合使用row和statement格式,对于DDL记录statument,对于table里的行操作记录为row格式。
  • 如果使用innodb表,事务级别使用了READ_COMMITTED 或者 READ_UMCOMMITTED日志级别只能使用row格式,但是使用ROW格式中DDL语句还是会记录成statement格式。

以下情况会从statument变成row

  • 当DML语句更新一个NDB表时。
  • 当函数中包含UUID()时。
  • 两个及以上包含AUTO_INCREMENT字段的表被更新时。
  • 任何INSERT DELAYED语句时。
  •  使用UDF(自定义函数)时。
  • 视图中必须要求使用RBR时,例如创建视图是使用了UUID()函数

企业场景如何选择binlog模式

  • 互联网公司,使用MySQL的功能相对少(存储过程、触发器、函数) 选择默认的语句模式,Statement Level(默认)
  • 公司如果用到使用MySQL的特殊功能(存储过程、触发器、函数) 则选择Mixed模式
  • 公司如果用到使用MySQL的特殊功能(存储过程、触发器、函数)又希望数据最大化一致,此时最好选择Row level模式

二、数据类型相关

int(数值)类型怎么设置有符号,无符号

有符号:创建表时默认的就是有符号类型

无符号:

方式一  CREATE TABLE intTable  ( t INT UNSIGNED );#添加关键字UNSIGNED

方式二  CREATE TABLE intTable  ( t INT(11) ZEROFILL);#设置以0填充(后面会将)会变成无符号

int(11)中的‘11’有什么用?对存储数值的范围有影响吗?

作用:会预留11位展示位置,但是要配合ZEROFILL使用(上面方式二的建表语句)

例:当我们配合ZEROFILL建表后(会变成无符号类型),如果设置的长度是11,但是我们存入的值为500,那么在数据库中存的值为00000000500,如果设置的长度是2,存入的值为5000,那么存入数据库的值也是5000,总之就是存入的长度不足设置的长度,以0填充;存入的长度超过设置的长度,显示存入的。第二个问题的答案也出来了,是无影响的,范围只跟选择的类型有关,tinyint,smallint、mediumint、int、bigint等。

float(M,D)、double(M,D)、decimal(M,D)中的M代表什么,D代表什么,如果不设置(M,D)会怎么样?

M的意思是整数部分加小数部分的长度是M,D代表小数部分的长度,如果超出范围则自动存入临界值。如float(5,2)的数据类型中存入1200.65,存入数据库的值是999.99;

M和D都可以忽略(创建表的时候),如果是float或者是double,精度会随着 插入的值的变化而变化(只要在范围内可以随意插入数据) ,而dec则会默认给长度为(10,0),小数点后不能有数值

例: 建表时三个类型都不写明(M,D) 分别存入999.99,floa和double的值都是999.99,但是decimal的值是1000

说说char(M)、varchar(M)的区别;enum、set的区别

char(M): 

  • 固定长度的字符 , M最大字符数,比较耗费空间,但是效率高
  • CHAR是定长的,根据定义的字符串长度分配足够的空间。
  • CHAR会根据需要使用空格进行填充方便比较。
  • CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
  • CHAR存储的内容超出设置的长度时,内容同样会被截断

varchar(M):

  • 可变长度的字符,M最大字符数,比较省空间,但是效率相对低
  • VARCHAR用于存储可变长字符串,它比定长类型更节省空间。
  • VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。
  • VARCHAR存储的内容超出设置的长度时,内容会被截断
  • VARCHAR的字符贴进了设置,虽然是可变长度,但是设置的越大,会消耗更多内存,性能就会降低

enum:把不重复的数据存储为一个预定义的集合,存入数据只能存入预定义一个的字符

例:建表CREATE TABLE tab_char( c ENUM('a','b','c')); 当存入数据的时候只能存入‘a'、’b'、‘c'中的一个,如果存其他的值也不会报错,但是只存入一个空字符(例如存入d则存入一个空字符)

set:把不重复的数据存储为一个预定义的集合,存入数据只能存入预定义一个或多个的字符

例:建表CREATE TABLE tab_char( c SET('a','b','c')); 当存入数据的时候只能存入‘a'、’b'、‘c'中的一个或者多个(’ab'),如果存其他的值也不会报错,但是只存入一个空字符(例如存入d则存入一个空字符)

timestamp和datetime的区别

  • 其中 timestamp受时区影响,较能贴近本地当前时间(如果设置时区为A地区,存一个时间进入数据库,不修改表的情况下,当前时区切换到B,那么之前存入的值也会跟着做出变化,做出计算后变成B时区的时间。datetime不会跟随时区变化,在A时区存一个时间进数据库,不修改表的情况下,当前时间切换到B,之前存入的值不会有任何变化
  • timestamp是能表达的时间范围较小,在时间范围内尽量用timestamp作为时间类型,效率也高一点。
  • timestamp类型在添加数据或者修改数据的时候会自动添加默认时间(当前时间);

三、sql语言相关

UNION和UNION ALL的区别

在联合查询中,union会自动去除重复的行,而union all会将所有的结果罗列出来,效率方面union all 会高一些

mysql中 in 和 exists 区别

mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。

如果查询的两个表大小相当,那么用in和exists差别不大。
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。
not in 和not exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。

drop、delete与truncate的区别

  • delete可以添加where条件,truncate不能添加where条件(直接清空整个表)
  • truncate删除的效率要比delete高一点,truncate不记录日志中,delete写入日志
  • 如果使用delete删除后,再插入数据,自增长的列从断点值开始,而使用truncate删除后,自增长的列从1开始
  • truncate删除没有返回值,delete删除有返回值(x行受影响)
  • truncate删除不能回滚,delete删除能回滚(事务)
  • Drop 不可以添加where条件,直接删除整个表包括表结构,事务不可忽滚,效率最高

四、约束相关

约束是什么?常见的约束有哪些?

含义:约束就是一种限制,用于限制表中的数据,为了保证数据的准确性和可靠性

六大约束:

NOT NULL :非空约束,用于保证该字段的值不为空 

DEFAULT:  默认约束,用于保证该字段有默认值

PRIMARY KEY: 主键约束,用于保证该字段的值具有唯一性,并且非空

特点:一个表的主键最多只能有一个,但是可以用多个列混合组成一个主键

UNIQUE :  唯一约束,用于保证该字段的值具有唯一性,但是可以为空
 

特点:一个表的唯一约束可以有多个,也可以由多个列混合组成混合唯一约束,

并且唯一约束的列最多只能有一个null,当出现两个null就不唯一了,于是报错。

CHECK:检查约束(mysql中不支持,但是语法不报错,就是不起作用)

FOREIGN: 外键约束  用于限制两个表的关系,保证该字段值必须来自于主表的关联列的值
特点:要在从表中设置外检;主表中关联列的类型必须与从表中外检列的类型相同或兼容,名称可以不相同;主表中的关联列必须是一个key(一般是主键或者唯一键,外键也可以);插入数据时,先插入主表再插入从表,删除数据时先删除从表,再删除主表。
 

 企业中经常使用外键约束吗?

不经常,因为增加外键之后后期数据库不好维护,并且性能低下,例如要新增一条数据,要去扫描主表,看下是否满足外键约束,再例如数据迁移的时候,从表要迁移进数据,主表也要迁移相关的数据

五、事务相关

什么是事务?事务的四大特性?

事务:一个或一组sql语句组成的一个执行单元,这个执行单元里面的sql要么都执行,要么都不执行

四大特性:

 事务的特性(网图)

原子性:

原子性指事务时一个不可分割的工作单位,事务中的操作要么都执行要么都不执行

一致性:
从一个正确的状态,迁移到另一个正确的状态.什么叫正确的状态呢?就是当前的状态满足预定的约束就叫做正确的状态.

隔离性:
事务的隔离性是指一个事务的执行不能被其他事务干,即一个事务内部的操作以及使用的数据对并发的其他事务时隔离的,并发执行的各个事务之前互不干扰

持久性:
事务一旦提交,它对数据库中的数据的改变就是永久性的
 

事务并发情况下可能会产生的问题有哪些

脏读:
对于两个事务T1、T2,T1读取了已经被T2更新但还没提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的

不可重复读:
对于两个事务T1、T2,T1读取了一个字段,然后T2更新了该字段之后,T1再读取一次该字段,值不相同

幻读:
对于两个事务T1、T2,T1从一个表中读取了一个字段,然后T2再在该表中插入一些新数据,之后T1再次读取同一个表,就会多出几行
 

事务的隔离级别

  • READ UNCOMMITTED  读未提交,该模式下会出现并发事务的所有可能出现的问题
  • READ COMMITTED  读已提交,能够成功解决脏读现象
  • REPEATABLE READ(默认的隔离级别)  可重复读,能够成功解决不可重复读现象,性能几乎不受影响
  • SERIALIZABLE 串行化,能够解决并发事务下的所有问题,通过禁止另一个事务增删改的方式,效率非常低下,在分布式事务下使用

mysql和oracle的隔离级别区别

  • oracle支持两种隔离级别:READ COMMITED、SERIALIZABLE,默认是 READ  COMMITED
  • mysql支持四种隔离级别:(以上四种),默认是REPEATABLE READ

七、视图、存储过程、触发器相关


为什么要使用视图?什么是视图?


为了提高复杂SQL语句的复用性和表操作的安全性,MySQL数据库管理系统提供了视图特性。所谓视图,本质上是一种虚拟表,在物理上是不存在的,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以储存的数据值形式存在。行和列数据来自定义视图的查询所引用基本表,并且在具体引用视图时动态生成。视图使开发者只关心感兴趣的某些特定数据和所负责的特定任务,只能看到视图中所定义的数据,而不是视图所引用表中的数据,从而提高了数据库中数据的安全性。

视图有哪些特点?

  • 视图的列可以来自不同的表,是表的抽象和在逻辑意义上建立的新关系。
  • 视图是由基本表(实表)产生的表(虚表)。
  • 视图的建立和删除不影响基本表。
  • 对视图内容的更新(添加,删除和修改)直接影响基本表。
  • 当视图来自多个基本表时,不允许添加和删除数据。
  • 视图的操作包括创建视图,查看视图,删除视图和修改视图。

视图的使用场景有哪些?

  • 视图根本用途:简化sql查询,提高开发效率。如果说还有另外一个用途那就是兼容老的表结构。
  • 下面是视图的常见使用场景:
  • 重用SQL语句;
  • 简化复杂的SQL操作。在编写查询后,可以方便的重用它而不必知道它的基本查询细节;
  • 使用表的组成部分而不是整个表;
  • 保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限;
  • 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。

视图的优点和缺点

视图的优点

  • 查询简单化。视图能简化用户的操作
  • 数据安全性。视图使用户能以多种角度看待同一数据,能够对机密数据提供安全保护
  • 逻辑数据独立性。视图对重构数据库提供了一定程度的逻辑独立性

视图的缺点

  • 性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。
  • 修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的
  • 这些视图有如下特征:1.有UNIQUE等集合操作符的视图。2.有GROUP BY子句的视图。3.有诸如AVG\SUM\MAX等聚合函数的视图。 4.使用DISTINCT关键字的视图。5.连接表的视图(其中有些例外)

什么是游标?


游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区都有一个名字。用户可以通过游标逐一获取记录并赋给主变量,交由主语言进一步处理。


什么是存储过程?有哪些优缺点?


存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需要创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。

优点

  • 存储过程是预编译过的,执行效率高。
  • 存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。
  • 安全性高,执行存储过程需要有一定权限的用户。
  • 存储过程可以重复使用,减少数据库开发人员的工作量。

缺点

  • 调试麻烦,但是用 PL/SQL Developer 调试很方便!弥补这个缺点。
  • 移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。
  • 重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。
  • 如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦。


什么是触发器?触发器的使用场景有哪些?


触发器是用户定义在关系表上的一类由事件驱动的特殊的存储过程。触发器是指一段代码,当触发某个事件时,自动执行这些代码。

使用场景

  • 可以通过数据库中的相关表实现级联更改。
  • 实时监控某张表中的某个字段的更改而需要做出相应的处理。
  • 例如可以生成某些业务的编号。
  • 注意不要滥用,否则会造成数据库及应用程序的维护困难。

MySQL中都有哪些触发器?


在MySQL数据库中有如下六种触发器:

  • Before Insert
  • After Insert
  • Before Update
  • After Update
  • Before Delete
  • After Delete

六、存储引擎相关

MyISAM和InnoDB的区别

  • InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务; 

  • InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败; 
  • InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。也就是说:InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。
  • InnoDB不保存表的具体行数,而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快(注意不能加有任何WHERE条件);
  • InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁,InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
  • InnoDB表必须有唯一非空索引(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当聚簇索引),而Myisam可以没有
  • InnoDB插入数据是按照主键(唯一非空索引)大小有序插入的,而Myisam是按照记录顺序插入
  • Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI ;Innodb:frm是表定义文件,ibd是数据文件;Myisam:frm是表定义文件,myd是数据文件,myi是索引文件
  • MyISAM表格可以被压缩后进行查询操作,但是不能进行修改(通过myisampack进行打包,myisampack完之后必须进行myisamchk才能使用压缩后的表,而且是只读的)

InnoDB的特点

1)插入缓冲(insert buffer):对于非聚集索引的插入和更新,不是每一次直接插 入索引页中,而是首先判断插入的非聚集索引页是否在缓冲池中,如果在,则直 接插入,否则,先放入一个插入缓冲区中。好似欺骗数据库这个非聚集的索引已 经插入到叶子节点了,然后再以一定的频率执行插入缓冲和非聚集索引页子节点 的合并操作,这时通常能将多个插入合并到一个操作中,这就大大提高了对非聚 集索引执行插入和修改操作的性能。

2)两次写(double write):两次写给 InnoDB 带来的是可靠性,主要用来解决 部分写失败(partial page write)。doublewrite 有两部分组成,一部分是内存中的 doublewrite buffer ,大小为 2M,另外一部分就是物理磁盘上的共享表空间中 连续的 128 个页,即两个区,大小同样为 2M。当缓冲池的作业刷新时,并不 直接写硬盘,而是通过 memcpy 函数将脏页先拷贝到内存中的 doublewrite buffer,之后通过 doublewrite buffer 再分两次写,每次写入 1M 到共享表空间 的物理磁盘上,然后马上调用 fsync 函数,同步磁盘。

3).预读(read ahead):
InnoDB使用两种预读算法来提高I/O性能:线性预读(linear read-ahead)和随机预读(randomread-ahead)
为了区分这两种预读的方式,我们可以把线性预读放到以extent(区)为单位,而随机预读放到以extent中的page为单位。线性预读着眼于将下一个extent提前读取到buffer pool中,而随机预读着眼于将当前extent中的剩余的page提前读取到buffer pool中。

线性预读(linear read-ahead):
线性预读方式有一个很重要的变量控制是否将下一个extent预读到buffer pool中,通过使用配置参数innodb_read_ahead_threshold,可以控制Innodb执行预读操作的阈值。如果一个extent中的被顺序读取的page超过或者等于该参数变量时,Innodb将会异步的将下一个extent读取到buffer pool中,innodb_read_ahead_threshold可以设置为0-64的任何值,默认值为56,值越高,访问模式检查越严格
例如,如果将值设置为48,则InnoDB只有在顺序访问当前extent中的48个pages时才触发线性预读请求,将下一个extent读到内存中。如果值为8,InnoDB触发异步预读,即使程序段中只有8页被顺序访问。你可以在MySQL配置文件中设置此参数的值,或者使用SET GLOBAL需要该SUPER权限的命令动态更改该参数。
在没有该变量之前,当访问到extent的最后一个page的时候,Innodb会决定是否将下一个extent放入到buffer pool中。
随机预读(randomread-ahead):
随机预读方式则是表示当同一个extent中的一些page在buffer pool中发现时,Innodb会将该extent中的剩余page一并读到buffer pool中,由于随机预读方式给Innodb code带来了一些不必要的复杂性,同时在性能也存在不稳定性,在5.5中已经将这种预读方式废弃。要启用此功能,请将配置变量设置innodb_random_read_ahead为ON。

4)自适应哈希索引(Adaptive Hash Index,AHI):
哈希(hash)是一种非常快的查找方法,在一般情况下这种查找的时间复杂度为O(1),即一般仅需要一次查找就能定位数据。而B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般为3~4层,故需要3~4次的查询。

InnoDB存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。AHI是通过缓冲池的B+树页构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。

AHI效果

  • 根据InnoDB存储引擎官方的文档显示,启用AHI后,读取和写入速度可以提高2倍,辅助索引的连接操作性能可以提高5倍。毫无疑问,AHI是非常好的优化模式,其设计思想是数据库自优化的(self-tuning),即无需DBA对数据库进行人为调整。

AHI缺点

  • hash自适应索引会占用innodb buffer pool;
  • 自适应hash索引只适合搜索等值的查询,如select * from table where index_col='xxx',而对于其他查找类型,如范围查找,是不能使用的;
  • 极端情况下,自适应hash索引才有比较大的意义,可以降低逻辑读。

为什么MyISAM会比Innodb的查询速度快
 

INNODB在做SELECT的时候,要维护的东西比MYISAM引擎多很多:

  • INNODB要缓存数据块,MYISAM只缓存索引块;
  •  innodb寻址要映射到块,再到行,MYISAM记录的直接是文件的OFFSET,定位比INNODB要快
  • INNODB还需要维护MVCC一致;虽然你的场景没有,但他还是需要去检查和维护MVCC (Multi-Version Concurrency Control)多版本并发控制 

InnoDB为什么推荐使用自增ID作为主键?

    答:自增ID可以保证每次插入时B+索引是从右边扩展的,可以避免B+树和频繁合并和分裂(对比使用UUID)。如果使用字符串主键和随机主键,会使得数据随机插入,效率比较差

为什么InnoDB没有记录表行数这个变量呢?

    因为InnoDB的事务特性,在同一时刻表中的行数对于不同的事务而言是不一样的,因此count统计会计算对于当前事务而言可以统计到的行数,而不是将总行数储存起来方便快速查询。InnoDB会尝试遍历一个尽可能小的索引除非优化器提示使用别的索引。如果二级索引不存在,InnoDB还会尝试去遍历其他聚簇索引。
    如果索引并没有完全处于InnoDB维护的缓冲区(Buffer Pool)中,count操作会比较费时。可以建立一个记录总行数的表并让你的程序在INSERT/DELETE时更新对应的数据。和上面提到的问题一样,如果此时存在多个事务的话这种方案也不太好用。如果得到大致的行数值已经足够满足需求可以尝试SHOW TABLE STATUS
 

七、索引相关

什么是索引?索引有什么优势?有什么缺点?

索引:索引是帮助mysql高效获取数据的数据结构,一般来说索引本身也很大,不能全部存在内存中,因此索引往往保存在硬盘中

优点:提高了检索效率,降低了数据io成本,通过索引对数据进行排序,降低了数据排序的成本,降低了cpu的消耗

缺点:虽然索大大提高了查询速度,但是同时降低了更新表的速度,比如对表进行更新了之后,mysql不仅要更新数据,还要更新索引文件

索引也是一张表,保存了主键与索引字段并指向实体表的记录,所以也占用了不少空间

说说mysql中的索引分类

按照功能分类:

  • 普通索引:最基本的索引,没有任何限制
  • 唯一索引:索引列的值必须唯一,但允许空,如果是组合索引,列值组合必须唯一
  • 主键索引:一种特殊的唯一索引,不允许空,在建表时主键列同时创建主键索引
  • 联合索引:将单列索引进行组合
  • 外键索引:只有InnoDB支持,用来保证数据一致性,完整性和实现级联操作
  • 全文索引:快速匹配全部文档的方式,innodb5.6版本后才支持。memory不支持        

按照数据结构分类:

  • B Tree索引:Mysql使用最频繁的索引,是Innodb和myisam存储引擎默认的索引类型,底层是基于b+tree
  • hash索引:mysql中menmory存储引擎默认支持的索引类型

说说hash、btree、b+tree的数据结构

 Btree的数据结构

(图片为网上转载)

 说明:上图中每一个黑色方块为一个磁盘块,P代表指向下一节点的指针,D代表携带的数据,蓝色方块中的数字代表键。

                Btree是一种多路查找树,Btree中所有节点的子树个数的最大值为Btree的阶,如上图中每一个节点的子树最大值为3,所以是一颗3阶树,一个m阶的Btree如果不为空,就必须具备以下性质:

  • 树中每个结点至多有m-1个关键字,即m棵子树。
  • 树中可看到真实存在的最后一排为终端节点而非叶子节点,叶子节点实际不存在,是btree查询时候为空的情况,上图中3、5、9、10等这一排实际是终端节点。
  • 除根节点以外,所有的非叶节点至少含有(m/2)-1个关键字,m/2棵子树
  • 根节点关键字可以小于(m/2)-1个,可以没有子树,如果有子树,则至少有两棵子树

B+tree的数据结构

(图片为网上转载)

  说明:一个m阶的B+tree如果不为空,就必须满足以下特性:

  • 树种每个节点至多含有m个关键字,m棵子树(节点的关键字和子树相同)
  • 除根节点外,所有非叶节点至少含有m/2个关键字,m/2棵子树
  • 根节点的关键字个数可以小于m/2,可以没有子树,如果有子树,至少有两棵
  • 所有叶结点中包含了全部关键字和关键字指向记录的指针,叶节点内的关键字也是有序排列的,叶节点之间也是有序排列的,指针相连,实际是个双向链表
  • 所有的非叶节点仅仅携带关键字和指向下一节点的指针

B+tree分为聚集索引和非聚集索引,如果是聚集索引的话,叶子节点存放的是一整行记录的数据。如果是非聚集索引的话,仅仅存放着主键,还要通过主键回表查询那一行记录的数据。

说说btree和b+tree的区别

以m阶树表示

  • B+tree由分块查找进化而来,B树由二叉排序树进化而来
  • 在B+tree中每个非根节点的关键字数的取值范围是m/2<= n<=m,子树个数为n;在Btree中每个非根节点的关键字树的取值范围是(m/2)-1<=n<m-1,子树个数为n+1
  • 在B+tree中,只有叶节点包含信息,非叶节点只起到索引所用;Btree中全部节点的关键字都包含信息
  • 在B+tree中,叶节点包含了全部关键字,非叶节点中出现的关键字一定会出现在叶节点中;在Btree中,任何节点中的关键字都不会重复
  • B+tree支持顺序查找和多路查找,Btree只支持多路查找
  •  B+tree中,查找成功或失败都会到达最后一层(叶子节点);而Btree中查找成功时,随时停止搜索。
  • B+tree的叶子节点有一条链相连;Btree中终端节点各自独立
  • 增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。

为什么Innodb要选择B+tree作为数据结构

B+tree每次都要访问叶子节点,遍历层次来看更加的稳定

b+tree的叶子节点使用指针连接在一起,方便了顺序遍历,既能满足范围查找又能满足多路查找

B+Tree 只需要去遍历叶子节点就可以实现整棵树的遍历

B+Tree 的非叶子节点并没有保存关键字的具体数据信息,内部节点相对B-Tree更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了
增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。


什么是聚簇索引?何时使用聚簇索引与非聚簇索引

  • 聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
  • 非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因
     

联合索引是什么?为什么需要注意联合索引中的顺序?

MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。

具体原因为:

MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。

当进行查询时,此时索引仅仅按照name严格有序,因此必须首先使用name字段进行等值查询,之后对于匹配到的列而言,其按照age字段严格有序,此时可以使用age字段用做索引查找,以此类推。因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。

如上图所示他们是按照a来进行排序,在a相等的情况下,才按b来排序。

因此,我们可以看到a是有序的1,1,2,2,3,3。而b是一种全局无序,局部相对有序状态!什么意思呢?

从全局来看,b的值为1,2,1,4,1,2,是无序的,因此直接执行b = 2这种查询条件没有办法利用索引。

从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。因此,你执行a = 1 and b = 2是a,b字段能用到索引的。而你执行a > 1 and b = 2时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。

综上所示,最左匹配原则,在遇到范围查询的时候,就会停止匹配。

b+tree中innodb不需要回表查询吗?myisam一定会回表查询吗?

都不一定,先说innodb,在innodb中主键索引是采用聚簇索引的形式有以下三种情况

  • 如果通过主键查找,那么命中的时候,主键索引中就会包含那一行记录,就不用回表查询
  • 实现覆盖索引,当通过联合索引查询是,查询的数据刚好是联合索引的列,就不用回表查询,例如select name,age from student where name=‘zhangsan’ and age=18,联合索引(name,age) ,这种情况就不用回表,因为查找的内容就是索引本身
  • 如果通过普通查找所有列例如select * from student where name=‘zhangsan’,索引为name,这时候是需要回表查询的

再说myisam,myisam中都是非聚簇索引,所以仅仅当实现索引覆盖的时候不用回表,其他情况都需要回表查询

什么情况使用了索引,查询还是慢

  • 索引全表扫描
  • 索引过滤性不好
  • 频繁回表的开销

什么情况下适合建索引什么适合下不适合建索引?

适合建立索引的情况

  1.  主键自动创建索引
  2.  频繁作为查询条件的字段应该创建索引
  3.  查询中与其他表关联的字段,外键关系建立索引
  4.  排序和分组的字段
     

不适合建立索引的情况

  1.   频繁增删改的字段不适合创建索引
  2.  where条件里的字段不适合创建索引
  3.  数据重复且分布均匀的数据。
  4.  表的记录过少(建立索引无意义)

八、锁相关

说说mysql中有那些锁?

按操作分类:

共享锁:也叫读锁。针对同一数据,多个事务读取操作可以同时加锁互不影响,但是不能修改数据。

innodb 共享锁:sql+ lock in share mode;例 select * from student lock in share mode;

当开启共享锁的时候,当前事务可以查询数据、修改数据,并且可以更换锁的类型;其他事务可以对该行进行共享锁查询,但是不能加排他锁查询,也不能修改数据。

myisam 读锁:加锁:lock table 表名  read;解锁:unlock   tables;

可以多个会话同时对一个表添加读锁,但是不能修改数据以及添加写锁。同一个会话添加读锁后也不能修改当前表的数据,

排他锁:也叫做写锁。当前操作没完成时,会阻断其他操作的加锁读取和修改数据。

innodb 排他锁:sql+for update  例 select * from student for update;

当开启排他锁时,当前事务可以查找数据和修改数据,也可以修改锁的类型,但是即便从排他锁变成共享锁,其他事务也不能加锁查询,本质上还是排他锁,其他事务只能查询数据,不能加锁也不能修改数据。

myisam 写锁:lock table 表名 write ;解锁:unlock tables; 

当前会话可以查询和修改数据,其他会话不能查询和修改数据

按粒度分类:

  • 表级锁:锁住整个表,开销小,加锁快,锁粒度大,发生锁冲突概率高,并发力度低,不会出现死锁现象。

          myisam存储引擎支持的就是表锁,innodb存储引擎也支持表锁,但是默认的是行锁

  • 行级锁:锁住当前行,开销大,加锁慢,锁粒度小,发生锁冲突概率低,并发度高,会出现死锁现象。

           innodb存储引擎默认支持行锁,但是需要用索引当作检索条件命中数据才能施加行锁

按照使用方式:

  • 悲观锁:对数据被外界修改保持保守状态,认为数据随时会被修改,整个数据处理过程中需要将数据加锁,悲观锁一般都是依靠关系型数据库提供的锁机制,我们之前学过的锁(共享锁,排他锁,读锁,写锁)都是悲观锁;用于写多读少
  • 乐观锁每次自己操作数据的时候认为没有人会修改他,所以不去加锁,但是在更新的时候去判断再次期间数据有没有被修改,需要用户自己去实现,不会发生抢占资源,只有在提交操作的时候检查是否违反数据完整性;用于读多写少

方式:给数据表添加一个version列,每次 更新后都将这个列的值加一,读取数据时,将版本号取出来,在执行更新的时候,比较版本号,如果相同则执行,如果不相同说明这条数据已经发生变化了,用户自行根据这个通知来决定怎么处理,比如重新更新一次或者放弃更新。

有对mysql进行过数据备份和恢复吗?怎么进行的?

备份登录到mysql服务器,输入:mysqldump -u root -p  数据库名称>文件保存路径

:mysqldump -u root -p test >/root/test.sql;  #点击回车之后输入密码即可。

备份文件里面存储的是建表语句和插入数据语句。

恢复

  1. 先登录mysql :mysql -uroot -p;回车之后输入密码
  2. 删除要备份的数据库: drop datebase test ;
  3. 创建一个新的要恢复的数据库: create  datebase test:
  4. 使用数据库:use test;
  5. 还原数据库:source +备份文件的路径 例如 source  /root/test.sql   

恢复数据无外乎就是将备份文件里的指令从头到尾执行一次 

什么是死锁?怎么解决?


死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

常见的解决死锁的方法

  • 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
  • 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;
  • 如果业务处理不好可以用分布式事务锁或者使用乐观锁

innodb有哪些行锁算法

  • Record lock:单个行记录上的锁

当查询的索引含有唯一属性时,将next-key lock降级为record key

  • Gap lock:间隙锁,锁定一个范围,不包括记录本身

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。

关闭间隙锁的方法1、将事务隔离级别设置为read committed; 2、将参数innodb_locks_unsafe_for_binlog设置为1

  • Next-key lock:record lock+gap lock 锁定一个范围,包含记录本身

innodb对于行的查询使用next-key lock

九、数据库优化相关

先聊一聊explain各个列代表的内容

执行计划包含的信息

  • id 由一组数字组成。表示一个查询中各个子查询的执行顺序;

id相同执行顺序由上至下。

id不同,id值越大优先级越高,越先被执行。

id为null时,表示一个合并结果集的操作的执行id为null,常出现在包含union等查询语句中。

  • select_type 每个子查询的查询类型,一些常见的查询类型。
id select_type description
1 SIMPLE 不包含任何子查询或union查询
2 PRIMARY 包含子查询时最外层查询就显示为 PRIMARY
3 SUBQUERY 在select或where子句中出现的子查询
4 DERIVED from字句中出现的子查询
5 UNION union连接的两个select查询,第一个查询是dervied派生表,除了第一个表外,第二个以后的表select_type都是union。
6 UNION RESULT 包含union的结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为null
7 dependent subquery 与dependent union类似,表示这个subquery的查询要受到外部表查询的影响。
8 dependent union 与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响
  • table 显示的查询表名,如果查询使用了别名,那么这里显示的是别名。
  • type访问类型(非常重要,可以看到有没有走索引)

依次从好到差:system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL。

除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一个索引。

类型 描述
system 表中只有一行数据或者是空表,且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index。
const 使用唯一索引或者主键,返回记录是1行记录的等值where条件时,通常type是const。其他数据库也叫做唯一索引扫描。
eq_ref 出现在要连接多个表的查询计划中,驱动表只返回一行数据,且这行数据是第二个表的主键或者唯一索引,且必须为not null,唯一索引和主键是多列时,只有所有的列都用作比较时才会出现eq_ref。
ref 像eq_ref那样要求连接顺序,也没有主键和唯一索引的要求,只要使用相等条件检索时就可能出现,常见于普通索引的等值查找。或者多列主键、唯一索引中,使用第一个列之外的列作为等值查找也会出现,总之,返回数据不唯一的等值查找就可能出现。
fulltext 全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引。
ref_or_null 与ref方法类似,只是增加了null值的比较。实际用的不多。
unique_subquery 用于where中的in形式子查询,子查询返回不重复值唯一值。
index_subquery 用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。
range 索引范围扫描,常见于使用>,<,is null,between ,in ,like等运算符的查询中。
index_merge 表示查询使用了两个以上的索引,最后取交集或者并集。常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取多个索引,性能可能都不如range。
index 索引全表扫描。把索引从头到尾扫一遍,常见于使用索引列就可以处理,不需要读取数据文件的查询、可以使用索引排序或者分组的查询。
ALL 全表扫描数据文件
  • possible_keys 可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为 NULL时就要考虑当前的SQL是否需要优化了。
  • key 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。
  • key_length 索引长度
  • ref 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
  • rows 这里是执行计划中估算的扫描行数,不是精确值。
  • extra 的信息非常丰富,常见的有:

Using index 使用覆盖索引

Using where 使用了where子句来过滤结果集

Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。

Using temporary 使用了临时表

根据explain的结果来看,一般要达到什么标准才算合格的sql
 

【推荐】SQL性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描是小巫见大巫。

常用SQL查询语句优化方法

  • 不要使用select * from t,用具体的字段列表代替“*”,使用星号会降低查询效率,如果数据库字段改变,可能出现不可预知隐患。
  • 应尽量避免在where子句中使用!=或<>操作符,避免在where子句中字段进行null值判断,存储引擎将放弃使用索引而进行全表扫描。
  • 避免使用左模糊,左模糊查询将导致全表扫描。
  • IN语句查询时包含的值不应过多,否则将导致全表扫描。
  • 为经常作为查询条件的字段,经常需要排序、分组操作的字段建立索引。
  • 在使用联合索引字段作为条件时,应遵循最左前缀原则。
  • OR前后两个条件都要有索引,整个SQL才会使用索引,只要有一个条件没索引整个SQL就不会使用索引,即便有了索引也是走索引全表扫描。
  • 尽量用union all代替union,union需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。

索引失效的几种情况

  • 最佳左前置法则:带头索引不能死,中间兄弟不能断,否则部份失效
  • 不要再索引列上做任何操作(计算,函数,类型转换),否则会导致索引失效,转向全表扫描
  • 范围过后索引失效,但是用作范围的索引可用,侧重点之后
  •  != 《》 会导致索引失效 is null is not null 索引失效
  • like以通配符开头(%abc)也会导致索引失效
  • 字符串类型不能丢失单引号,否则索引失效
  • 字符串不加单引号索引失效
  • 用or连接如果有一边无索引会导致索引失效,即便有索引也是走索引全表扫描。所以不推荐使用or
  • 两个列的比较的时候要注意字符编码和数据类型要保持一致否则会失效
     

两表连接时该如何优化

当使用左右连接时,要将索引建立在从表,因为主表的数据是全部都有的,
关键在于我们通过主表利用on后的条件对从表数据进行搜查,所以从表是我们的
关键优化点,例如左连接 select * from AA a  left join BB b on a.id=b.id  这时候要
将索引建立在B.id上。
 尽可能减少join语句中nestedLoop的循环总次数:小表驱动大表
 当无法保证被驱动表的join条件字段被索引且内存资源充足的情况下,不要太吝啬JOINBUFFER的设置
 

既然你知道了怎么优化sql,那你说说你是通过什么方式找到性能较差的sql的?

一、慢查询日志

慢查询日志就是可以自动收集一些执行比较慢的sql,对于这个慢的阈值需要我们手动设置,sql收集的位置也需要我们手动设置下面是开启慢查询日志的两种方式

通过命令行的方式开启

  1. 打开慢查询日志set global slow_query_log='ON'
  2. 指定对于慢这个概念的阈值set global long_query_time=1;
  3. 指定保存路径以及文件名set global slow_query_log_file='bxg_mysql_slow.log'

通过配置文件永久打开(修改配置文件my.cnf中[mysql]下配置)

  1. 开启慢查询日志slow_query_log=1
  2. 指定对于慢这个概念的阈值long_query_time=3
  3. 指定日志输出格式log_output=FILE 
  4. 指定保存路径以及文件名slow_query_log_file=/var/lib/mysql/bxg_mysql_slow.log

二、mysqldumpslow抓取日志中比较紧急的sql

由于开启慢查询日志后,收集到的数据可能会非常非常的多,我们可以通过某些方式抓取出比较致命的sql:mysqldumpslow

使用这个工具需要在mysql的宿主机上使用,而不是进入mysql中输入指令

参数例举

s 表示按照何种方式排序  后面接着以下参数

  • c  访问次数
  • l 锁定时间
  • r 返回记录
  • t 查询时间
  • al 平均锁定时间
  • ar 平均返回记录
  • at 平均查找时间

t 表示需要抓取多少条慢的sql  后面接数量

g 表示按照一定的规则匹配,大小写不区分

常用的几条收集语句

  • 得到返回记录集最多的10个SQL: mysqldumpslow -s r -t 10 /var/lib/mysql/bxg_mysql_slow.log
  • 得到访问次数最多的 10 个 SQL: mysqldumpslow -s c -t 10 /var/lib/mysql/bxg_mysql_slow.log
  • 得到按照时间排序的前 10 条里面含有左连接的查询语句:mysqldumpslow -s t -t 10 -g “left join” /var/lib/bxg_mysql_slow.log
  • 另外建议在使用这些命令时结合 | 和 more 使用 ,否则有可能出现爆屏情况:mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more

三、使用show profile工具分析sql

是mysql可以用来分析当前会话中语句执行的资源消耗情况,可以用于sql的调优测量,默认情况下处于关闭状态,并自动保存最近15条的运行结果

开启profile:set  profiling = on

查询运行过的sql:  

        show profiles   

 根据具体的id查询整个sql在各个执行阶段所消耗的时间:

SHOW profile  cpu,block io  for query  878

show profile的常用查询参数

  • ALL:显示所有的开销信息。
  • BLOCK IO:显示块IO开销。
  • CONTEXT SWITCHES:上下文切换开销。
  • CPU:显示CPU开销信息。
  • IPC:显示发送和接受开销信息。
  • MEMORY:显示内存开销信息。
  • PAGE FAULTS:显示页面错误开销信息。
  • SOURCE:显示和Source_function,Source_file,Source_line相关的开销信息。
  • SWAPS:显示交换次数开销信息

如果status列中出现以下某一种情况,说明sql很不理想

  • converting HEAP to MyISAM 查询结果太大,内存不够用,往硬盘上搬
  • creating tmp table 创建临时表(涉及创建表,拷贝数据,删除表)
  • coping to tem table on disk 把内存中临时表复制到磁盘上
  • locked 

四、全局日志分析

可以查看开启之后的任意时段的sql,仅在开发测试环境中使用

方式一:在mysql.cnf中设置如下
开启全局日志general_log=1
记录日志文件的路径general_log_file=/path/logfile
输出格式log_output=FILE

方式二

set global general_log=1;
set global log_output=‘TABLE’

开启之后会将所执行的所有sql记录到mysql下的general_log表中 可以使用以下命令查看

select * from mysql.general_log;

超大量删除一个表中的数据有什么好的方法?

 情景:在MySQL数据库使用中,有的表存储数据量比较大,达到每天三百万条记录左右,此表中建立了三个索引,这些索引都是必须的,其他程序要使用。由于要求此表中的数据只保留当天的数据,所以每当在凌晨的某一时刻当其他程序处理万其中的数据后要删除该表中昨天以及以前的数据,使用delete删除表中的上百万条记录时,MySQL删除速度非常缓慢每一万条记录需要大概4分钟左右,这样删除所有无用数据要达到八个小时以上,这是难以接受的

方式一:

查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的,于是删除掉其中的两个索引后测试,发现此时删除速度相当快,一百万条记录在一分钟多一些,可是这两个索引其他模块在每天一次的数据整理中还要使用,于是想到了一个折中的办法:

在删除数据之前删除这两个索引,此时需要三分钟多一些,然后删除其中无用数据,此过程需要不到两分钟,删除完成后重新创建索引,因为此时数据库中的数据相对较少,约三四十万条记录(此表中的数据每小时会增加约十万条),创建索引也非常快,约十分钟左右。这样整个删除过程只需要约15分钟。对比之前的八个小时,大大节省了时间。

方式二:

删除大表的部分数据,通常采用以下步骤:

  1. 删除大表的多行数据时,会超出innod block table size的限制,最小化的减少锁表时间的方案是:
  2. 选择不需要删除的数据,并把它们存在一张相同结构的空表里
  3. 重命名原始表,并给新表命名为原始表名
  4. 删掉原始表

聊一聊limit分页方法的不足点以及如何优化?

问题:

limit偏移量不变,随着查询记录量越来越大,所花费的时间也会越来越多。

limit查询记录数不变,随着查询偏移的增大,尤其查询偏移大于10万以后,查询时间急剧增加。

原因分析

select * from user where sex = 1 limit 100,10
由于 sex 列是索引列,MySQL会走 sex 这棵索引树,命中 sex=1 的数据。
然后又由于非聚簇索引中存储的是主键 id 的值,且查询语句要求查询所有列,所以这里会发生一个回表的情况,在命中 sex 索引树中值为1的数据后,拿着它叶子节点上的值也就是主键 id 的值去主键索引树上查询这一行其他列(name、sex)的值,最后返回到结果集中,这样第一行数据就查询成功了。
最后这句 SQL 要求limit 100, 10,也就是查询第101到110个数据,但是 MySQL 会查询前110行,然后将前100行抛弃,最后结果集中就只剩下了第101到110行,执行结束。
小结一下,在上述的执行过程中,造成 limit 大偏移量执行时间变久的原因有:
limit a, b会查询前a+b条数据,然后丢弃前a条数据
MySQL数据库的查询优化器是采用了基于代价的方式,而查询代价的估算是基于CPU代价和IO代价。如果MySQL在查询代价估算中,认为全表扫描方式比走索引扫描的方式效率更高的话,就会放弃索引,直接全表扫描。

优化方式:

方式一:

使用覆盖索引
如果一条SQL语句,通过索引可以直接获取查询的结果,不再需要回表查询,就称这个索引为覆盖索引。
在MySQL数据库中使用explain关键字查看执行计划,如果extra这一列显示Using index,就表示这条SQL语句使用了覆盖索引。
让我们来对比一下使用了覆盖索引,性能会提升多少吧。
没有使用覆盖索引
select * from t5 order by text limit 1000000,10;
这次查询花了3.690秒,让我们看一下使用了覆盖索引优化会提升多少性能吧。
使用了覆盖索引
select id, `text` from t5 order by text limit 1000000, 10;
从上面的对比中,超大分页查询中,使用了覆盖索引之后,花了0.201秒,而没有使用覆盖索引花了3.690秒,提高了18倍多,这在实际开发中,就是一个大的性能优化了。

方式二:

子查询优化

因为实际开发中,用SELECT查询一两列操作是非常少的,因此上述的覆盖索引的适用范围就比较有限。
所以我们可以通过把分页的SQL语句改写成子查询的方法获得性能上的提升。
select * from t5 where id>=(select id from t5 order by text limit 1000000, 1) limit 10;
其实使用这种方法,提升的效率和上面使用了覆盖索引基本一致。
但是这种优化方法也有局限性:
这种写法要求主键ID必须是连续的
Where子句不允许再添加其他条件

方式三:

和上述的子查询做法类似,我们可以使用JOIN,先在索引列上完成分页操作,然后再回表获取所需要的列。

select a.* from t5 a inner join (select id from t5 order by text limit 1000000, 10) b on a.id=b.id;



 

猜你喜欢

转载自blog.csdn.net/Promise_J_Z/article/details/120872667
今日推荐