MySQL常见面试题

191 阅读29分钟

MySQL的结构

image.png

  • 连接器: 身份认证和权限相关(登录MYsql的时候)
  • 查询缓存:执行查询语句的时候,会先查询缓存(MYSQL8.0后移除,因为这个功能不实用)
  • 分析器:没有命中缓存的话,SQL语句就会经过分析器,主要是看SQL语句要干嘛,再检查SQL语句是否正确
  • 优化器: 按照MYSQL认为的最优方案去执行
  • 执行器: 执行语句,然后从存储引擎返回数据。执行语句之前会判断是否有权限,如果没有权限的话,会报错
  • 插件式存储引擎:主要负责数据的存储和读取,采用的是插件式架构。

一条SQL语句在MySQL执行的流程

  • 通过权限校验之后,如果执行查询语句,就会查询缓存,(MYSQL8.0后移除), 没有命中缓存,SQL就会经过分析器,查看SQL语句要干嘛,再检查SQL语句是否正确。通过后到达优化器,MYSQL会按照最优的方案去执行,最后执行器执行语句,从存储引擎返回数据。、

MySql数据库引擎有哪些(区别从行级锁、外键、事务、数据库异常崩溃后安全恢复,MVCC等方面。)

  • MYISAM: 全表锁
    • 优势:执行速度快,占用空间小;索引文件和数据时分离的
    • 缺点: 不支持事务,不支持外键,并发性能差
    • 业务:多适用于主要依赖 Select, Insert的业务中
  • InnoDB:行级锁(MVCC,行级锁的升级)
    • 优势:支持事务,具有提交回滚功能,默认使用REPEATABLE-READ(可重读)隔离级别,解决幻读的问题,支持外键,自动增长列,并发能力强, 支持数据库异常崩溃后安全恢复,这个过程依赖redo log; 数据文件本身就是索引文件,表数据文件本身按照B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。
    • 缺点:执行速度相对于MYISAM较差一点,占用空间是MYISAM的2.5倍,但并发时性能高
    • 业务:相对于更适合业务复杂的场景,比如银行转账,崩溃时能够回滚
  • 如何使用命令查看:
    • 查看mysql现在提供的存储引擎: show engines;
    • 查看mysql当前默认的存储引擎: show variables like '%storage_engine%'

MySQL事务:

  • 事务或数据库事务:在一次事务中,所有动作要么全部执行,要么都不执行。

ACID

  • Atomic 原子性:事务是执行的最小单位,不可分割,要么都执行,要么都不执行。
  • Consistency 一致性:执行事务前后,数据保持一致。
  • Isloation 隔离性:一个事务执行不会被其他事务所干扰,并发执行的事务数据库是独立的。
  • Duration 持久性:一次事务之后,对数据库的操作是持久的,就是发生故障也不会对其有任何影响。

并发事务带来了什么影响? 幻读和不可重复度的区别?

  • 脏读:读到还未提交的数据

  • 幻读: 在一个事务中多次执行同一查询时,由于其他事务的插入操作,导致多次的结果集不一致。

  • 不可重复读:在一个事务中多次执行同一查询时,由于其他事务修改,导致结果即不一致。

  • 不可重复读的重点是内容修改和记录减少,比如多次读取一条数据时发现内容被修改

  • 幻读的重点是记录的行数。

  • 幻读是不可重复读的一种特殊情况。

    • 执行 deleteupdate 操作的时候,可以直接对记录加锁,保证事务安全。而执行 insert 操作的时候,由于记录锁(Record Lock)只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁(Gap Lock)。也就是说执行 insert 操作的时候需要依赖 Next-Key Lock(Record Lock+Gap Lock) 进行加锁来保证不出现幻读

事务的隔离级别:

  • 读未提交:读到别人还没有提交的数据,会导致脏读,幻读,不可重复
  • 读已提交:只能读到别人已经提交的数据,解决脏读,但是会引起幻读,不可重复读
  • 可重复读:在一个事务中的多次查询中,最后得到的结果都是一致的。解决幻读和不可重复度。使用MVCC,读的时候会拷贝一个副本。
  • 串行化:锁表执行事务,可以解决所有并发问题,但效率低下。

默认的事务隔离级别是什么? 能解决幻读吗?

  • 默认的事务隔离级别: 可重复读
  • 能解决幻读:
    • 快照读:由MVCC保证不出现幻读
    • 当前读:通过next-key-lock 保证不出现幻读,通过行锁和间隙锁来保证,行锁只能锁住已存在的行,无法避免新增记录,所以需要间隙锁。

什么是MVCC ? 有什么用? 原理是什么?

  • MVCC:多版本并发控制, 用于保持多个并发事务执行时数据的一致性和隔离性。

  • 隔离级别中可重复读使用MVCC保证不出现幻读。

  • MVCC通过创建多个版本和读取快照实现并发控制。读操作,使用旧版本数据快照。写操作,使用创建新版本,并确保原始版本仍可用。这样不同事务在一定程度并发执行,而不会互相干扰,从而提高数据库的并发性能和数据一致性。

    • 聚簇索引记录中有两个必要的隐藏列:

      trx_id:用来存储每次对某条聚簇索引记录进行修改的时候的事务id。

      roll_pointer:每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。

      这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。

      (注意插入操作的undo日志没有这个属性,因为它没有老版本)

      已提交读和可重复读的区别就在于它们生成ReadView的策略不同。

      开始事务时创建ReadView,ReadView维护当前活动的事务id,即未提交的事务id,排序生成一个数组。

      访问数据,获取数据中的事务id,对比ReadView:

      如果在ReadView的左边(比ReadView都小),可以访问(在左边意味着该事务已经提交)

      如果在ReadView的右边(比ReadView都大)或者就在ReadView中,不可以访问,获取roll_pointer,取上一版本重新对比(在右边意味着,该事务在ReadView生成之后出现,在ReadView中意味着该事务还未提交)

      已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,而可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。

      这就是Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView生成策略的不同实现不同的隔离级别

表级锁和行级锁有什么区别?

  • 表级锁:MySQL中较大粒度的锁,是针对非索引字段加的锁,是对整张表操作进行加锁,虽然资源消耗少,加锁快,不会出现死锁,但是容易造成锁冲突。MyIsAM和Innodb的存储引擎都支持表锁。
  • 行级锁:MySQL中最小粒度的锁,是针对索引字段加的锁,是对操作的行进行加锁,行级锁能大大降低锁冲突,粒度最小,但是加锁开销大,加锁慢,容易造成死锁。

行级锁有什么使用的注意事项?

  • 行级锁是针对索引字段进行加锁,如果在使用update、delete操作中where没有命中索引或者索引失效,就会导致扫描全表,并对所有的行进行加锁。

共享锁和排他锁:

  • 共享锁:s锁,是读锁,事务在读取数据中获取锁,允许多个读取事务同时获取(锁兼容)
  • 排他锁:x锁,是写锁/独占锁,每次只能有一个进行加锁,后面的只能等待释放。一个事务修改记录时获取到锁,不允许多个事务同时获取。
  • 共享锁与共享锁能兼容,排他锁与其他锁都不兼容。
  • 不论是表级锁和行级锁都存在共享锁和排他锁。

意向锁:

  • 判断是否对某个表使用表锁,意向锁是表锁。

  • 意向共享锁,IS锁:事务有意向对某些记录加 共享锁,加锁前先要获取到意向共享锁IS.

  • 意向排他锁,IX锁:事务有意向对某些记录加 排他锁,加锁前先要获取到意向排他锁IX.

  • 意向共享锁、意向排他锁都是兼容的

  • 只有意向共享锁和表级共享锁是兼容的,其他都不兼容。

Innodb有哪几种行锁:

  • 记录锁Record Lock: 属于某行记录上的锁
  • 间隙锁Gap Lock:锁定一个范围,不包括自身
  • 临键锁Next-key Lock: Recorde Lock + Gap Lock, 锁定一个范围,并包含自身。用来解决幻读问题(插入)。记录锁只能锁住当前这行的数据不被篡改,需要间隙锁来保证无法插入。
  • Innodb默认的隔离级别为 REPEATABLE-READ 可重复读, 行锁默认为临键锁Next-key Lock。如果当前操作的索引是唯一索引或主键,Innodb会优化为Record Lock,只会索引本身加锁,而不是范围。

索引是什么?有什么作用?优缺点?

  • 索引可以看作是一种快速查找和检索数据的数据结构,或者排序好的数据结构。索引相当于书的目录。
  • 优点:索引可以大大加快检索数据的速度,(减少检索数据的数据量),减少IO次数,通过创建唯一索引,来保证每行数据的唯一性。
  • 缺点:创建索引和维护索引会耗费很多时间,当对数据进行增删改时,如果有索引,还需要对索引动态修改,增加了SQL执行的时间。 而且索引会使用物理文件存储,会耗费一定空间。

索引底层的数据结构?

  • 排除其他选型的原因:

    • Hash,是由于hash冲突,通过链表+红黑树来解决,但是MySQL没有采用是因为不能顺序查询和范围查询
    • 二叉树:容易不平衡,相当于遍历查询
    • AVL平衡树:通过旋转进行自平衡,但是会频繁的自旋来保持平衡,会有较大的计算消耗,影响数据库写操作的性能。
    • 红黑树:与AVL不同,它是追求一个大致的平衡,所以,红黑树的高度可能会比较高,查询的次数会比较多,会增加磁盘Io的次数,降低查询效率。
    • B树:每个节点都存放key索引和data数据,而且每个叶子节点都是独立的。 范围查找时,需要先找到下限,然后中序遍历,直到找到上限
  • B+Tree: 只有叶子节点才会存放key索引和data数据,内节点只有索引key, 并且每个叶子节点也有引用链指向相邻的叶子节点。检索数据时,每次都是从根节点查询到叶子节点。范围查询时,只需要遍历链表即可。

  • MYISAM采用非聚簇索引(data存储地址值,去查找文件), Innodb采用聚簇索引(data存储数据)。

B树和B+树的区别?

  • 存储:B树的所有节点既存放键值也存放数据; B+树只有叶子节点能存放键值和数据,其他节点只存放键值
  • 叶子节点: B树的叶子节点都是独立的; B+树的叶子节点有一条引用链指向它相邻的叶子节点,构成链表
  • 检索:B树的检索过程,相当于在范围内的每个节点的关键字进行二分查找,可能没有到达叶子节点就检索到了数据; B+树的检索过程就比较稳定,都是从根节点到叶子节点
  • 范围查找: B树的范围查找,只能是先找到下限,再通过B树的中序遍历,直到找到上限;而B+树只需要遍历链表即可。
  • B+树相比于B树,IO次数更少,更适合范围查找,查找效率更稳定。

索引的划分

  • 按照底层存储方式划分:

    • 聚簇索引
    • 非聚簇索引
  • 按照应用的维度来划分:

    • 主键索引
    • 普通索引:
    • 唯一索引:
    • 覆盖索引:
    • 联合索引:
    • 全文索引:对文本的内容进行分词,进行搜索。目前只有 CHARVARCHARTEXT 列上可以创建全文索引。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
    • 前缀索引:对文本的前几个字符创建索引,相比普通索引建立的数据更小,因为只取前几个字符。

主键索引和二级索引

  • 主键索引:数据表的主键列使用的索引就是主键索引。

    • 主键:唯一、不为null,一个数据表只有一个主键。
    • 如果数据表未指定主键索引,innodb会根据数据列查看是否有唯一且不为null的列,作为主键索引,如果没有找到,最自动创建一个6byte的自增索引。
  • 二级索引:二级索引的叶子节点数据存储的是主键的值,也就是可以通过二级索引找到主键的位置,又称辅助索引/非主键索引

    • 唯一索引:主要是一种约束,使用的数据列不允许出现重复,可以为NULL,一张表允许创建多个唯一索引。主要是为了唯一性,而不是提升查询效率。
    • 普通索引:主要是为了提升查询效率,一个表允许创建多个普通索引。并且允许数据重复和为null.
    • 前缀索引:只适用于字符型的数据,会根据数据的前面几个字符进行查询
    • 全文索引:主要是为了检索大文本数据中的关键字信息,目前搜索引擎数据库使用的一种技术。MySQL5.6之前只有MyISAM支持,5.6之后只有Innodb支持。

聚簇索引和非聚簇索引

  • 聚簇索引:索引和数据一起存放的索引,主键索引就是聚簇索引。
    • 优点:
      • 查找速度快:找到索引,就能直接得到数据,减少了一次Io的次数
      • 排序查找和范围查找比较快
    • 缺点:
      • 依赖主键的顺序性,如果无法排序,插入和查找的速度就会比较慢
      • 更新代价大:索引列更新修改数据时,索引对应的索引也要跟着一起修改。所以对于主键索引列,数据一般都是不可被修改的。
  • 非聚簇索引:索引单独存放,data数据存放的是地址值 或 是二级索引的叶子节点存储的是主键, 是会通过主键再去回表查询数据。
    • 优点:
      • 更新代价比聚簇索引的代价小,非聚簇索引叶子节点不存放数据
    • 缺点:
      • 依赖有序数据:无序的会查找慢
      • 可能会二次回表:可能会根据data的地址值查找到数据文件进行查询,或者根据主键进行回表查询。

回表

  • 在innodb存储引擎中,非主键索引的叶子节点包含的值是主键值时,意味着,进行此非主键查询时,数据库会先找到对应的主键值,再根据主键索引查找定位对应的行数据,这种情况叫做回表。

覆盖索引:

  • 覆盖索引:一个索引包含所有要查询的字段,直接根据索引就能查到数据,就无需回表查询

联合索引:

  • 就是使用表中多个字段创建的索引,也叫组合索引或复合索引。

Select 的七大子句

select 内容(字段名、函数、表达式) ① from 表名 ② on 设置关联条件(去除笛卡尔积) ③ where 设置筛选条件 ④ group by 字段名 ⑤ having 筛选条件(分组之后进行操作) ⑥ order by 排序 ⑦ limit 分页

三大范式

  • 第一范式:保证每个列的原子性,即不可拆分。
  • 第二范式:保证每个列直接依赖与主键(在第一范式的基础上,消除非主属性对于主键的部分依赖)
  • 第三范式:非主键字段之间不能出现传递依赖的关系
  • 作用: 使得表结构清晰,减少数据冗余(重复),提高查询和存储效率。

优化口诀:避免索引失效

  • 【select * 前提下】 全值匹配我最爱,最左前缀要遵循 带头大哥不能死,中间兄弟不能断 索引列上少计算,范围之后全失效 LIKE百分写最右,覆盖索引不写* 不等空值还有OR,索引影响要注意 VAR引号不可丢,SQL优化有诀窍

    • 最佳左前缀法则: 如果索引了多列,要遵循最左前缀法则:查询从索引的最左前列开始并且不跳过索引中的列。

    • 不在索引列做任何操作(计算、函数、(自动Or手动)类型转换),会导致索引失效而转向全表扫描

    • 存储引擎不能使用索引中范围条件右边的列

    • 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少 select *

    • 注意null/not null对索引的可能影响

    • like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作、

    • 字符串不加单引号索引失效

    • mysql 在使用不等于(!= 或者 <>)的时候有时候无法使用索引会导致全表扫描

    • 少用or,用它来连接时会索引失效

    • 解决上述索引失效的问题:覆盖索引:select 到 from 查询到的列 <= 使用的索引列 + 主键

正确使用索引的建议:

  • 选择合适的字段创建索引:

    • 不为NULL的:数据库比较难解析NULL, 如果要频繁查询,又无法避免可以使用0,1,true,false这些短值进行代替。
    • 被频查询的字段
    • 被where条件查询的字段
    • 频繁需要排序的字段
    • 频繁需要被连接的字段:就是多表联合查询时,对于频繁作为连接的字段,增加索引提高查询效率。
  • 被频繁更新的字段,慎重选择创建索引:因为维护索引的成本不小,如果当前查询次数少,更新频繁的就不要创建索引了。

  • 限制每张表的索引数量: 不超过5个,mysql优化器会选择合适的索引来生成执行计划,如果可以查询的索引过度,会影响生成执行计划的时间。

  • 尽可能考虑使用联合索引而不是单列索引:因为每个索引都是需要空间的,选择联合索引,节省了空间,还增加修改索引的效率。

  • 删除长期未使用的索引

  • 避免索引失效的场景:

    • select *,并且where指向的范围过大
    • 使用联合索引时,查询条件没有遵从最左前缀原则
    • 索引列进行了计算、函数、类型转换等操作
    • 以 % 开头的LIKE查询比如 LIKE %abc
    • 查询中使用OR,如果OR的前后有个没有使用到索引列,被涉及索引就都不会被使用了。
    • IN(not in)使用的范围过大时,会导致全表扫描: in 在结果集 大于 30%的时候索引失效
    • order by

关联查询优化

  • 保证被驱动表的join字段被索引
  • left join时,选择小表作为驱动表,大表作为被驱动表
  • inner join时,mysql会自己帮你把小结果集的表选为驱动表
  • 子查询尽量不要放在被驱动表,有可能使用不到索引
  • 能够直接多表关联的尽量直接多表关联,不用子查询。

排序分组优化

  • Order by关键字优化:
    • order by 子句,尽量使用Index方式排序,避免使用FileSort方式排序
    • 尽可能在索引列上完成排序操作,遵循索引键的最佳左前缀

image.png

  • group by关键字优化:
    • group by实质是先排序后进行分组,遵照索引键的最佳左前缀
    • where 高于 having, 能写在where限定的条件就不要去having限定了
    • group by 使用的索引原则几乎与order by 一致,唯一区别是 group by 即使没有过滤条件用到索引,也可以直接使用索引。

MySQL中常见的日志

  • 错误日志(err.log):对MySQL的启动、运行、关闭进行记录。
  • 二进制日志(binlog):主要记录修改数据库数据的SQL语句。
  • 一般查询日志:已建立的客户端向数据库发送的查询sql语句进行记录,因为SQL量大,默认不开启,不推荐开启。
  • 慢查询日志(slow query log): 记录执行时间超过 long_query_time 时间的查询。解决慢查询时会用到
  • 事务日志(redo log和 undolog): redo log是重做日志,undo log是回滚日志
  • 中继日志(relay log): relay log是复制过程中产生的日志,大多数与bin log差不多。主要针对主从复制的从库。
  • DDL(metadata log): DDL执行元数据的操作。

慢查询日志的作用?

  • 慢查询: 记录所有执行超过long_query_time时间的查询(默认是10是,一般设置为1s),通常是解决SQL慢查询问题时用到。
  • 可以使用 show variables like 'slow_query_log'; 来查看慢查询日志是否开启,默认关闭
  • 实际项目中,慢查询日志会比较大,比较难分析,推荐使用Mysql官方慢查询分析工具 mysqldumpslow MySQL :: MySQL 5.7 Reference Manual :: 4.6.8 mysqldumpslow — Summarize Slow Query Log Files
  • 然后使用explain分析工具分析慢查询语句。

binlog主要记录了什么?又什么作用?

  • 记录了对所有数据修改的所有操作,(DDL和DML),包括表相关的操作(CREATE\DROP\ALTER TABLE等),数据相关的操作(insert\update\delete), 而不会记录查询且不会修改数据的操作(select show)

  • binlog日志的格式:statment的日志文件比row小,但是准确性要差一些

    • statement:记录每个对数据修改的语句sql,比如(update delete insert)
    • row: 记录每行具体的变更事件
    • Mixed: statement和row的混合模式,默认使用statement, 出现特殊情况,使用row进行记录。
    • 使用show variables like '%binlog_format%' 查询使用的格式
  • 作用:主要是主从复制,主备,主主,主从,都依赖binlog来同步数据,保持数据的一致性

    • 主从复制的原理:
      • 主库将数据写入到binlog中
      • 从库连接主库
      • 从库创建IO线程向主库请求binlog日志
      • 主库会新建一个binlog dump线程发送,向从库的IO线程接收
      • 从库将接收到的binlog存储到relay log中
      • 从库完成数据库同步操作(将relay log中的sql重新执行)
  • 作用二:用于误删除恢复,当误删除数据时,可以使用binlog进行重新恢复。

redolog是如何保证事务的持久性的?

  • Innodb是以数据页的数据结构来存储数据的,一般是从Buufer Pool(缓存池)中读取,如果Buffer Pool中没有,会从数据库中读取,缓存到Buffer Pool中,基本都是操作Buffer Pool中的页数据,最后写入到磁盘中。虽然加快了IO的读取,但是如果mysql出现宕机,数据就可能丢失,这时候就会破坏事务的持久性,redolog发挥作用了
  • redolog会记录页的修改,事务提交之后,redolog会根据刷盘策略,持久化到磁盘中,即使mysql宕机了,重启也能恢复未写入的数据,保证事务的持久性。
  • 刷盘策略:一定情况会自动刷盘,事务提交(1)、logbuffer空间不足时,定时任务、定期执行检查点操作
    • 0:表示每次事务提交时不进行刷盘
    • 1:表示每次事务提交时进行刷盘
    • 2:表示每次事务提交时,将redolog写入page Cache(文件系统缓存中)

页修改之后为什么不直接刷盘呢?

  • 性能非常差,innodb页的大小一般为16kb, 是磁盘和内存交互的基本单位,如果只是修改几个字节的数据,一次刷盘操作也需要将整个页都刷新到磁盘中,并且修改的这些页可能是不连续的,也就是随机IO
  • 采用redo log就可以避免这种操作,redolog刷盘性能高,redolog的写入属于顺序IO, 一行redolog只占几十字节。

binlog和redolog之间的区别?

  • 用途:
    • binlog:存储数据变化的语句,主要用于主从复制和 数据库还原,属于数据级别的恢复。
    • redolog: 保证事务的持久性,是事务级别的恢复。
  • 引擎特有:
    • redolog: innodb引擎特有(数据页)
    • binlog:所有引擎都有
  • 物理还是逻辑日志:
    • redolog: 是物理日志,存储页的变化记录
    • binlog: 是逻辑日志,记录数据变化的日志
  • 写入方式和大小:
    • binlog: 追加的方式写入,没有大小限制
    • redolog: 采用循环的方式写入,大小固定,写到结尾,会回到开头循环写入。

undolog是如何保证事务的原子性的?

  • undolog: 每个事务对数据的修改都会记录到undolog中,当事务执行出错或者需要执行回滚时,就是根据undolog将数据恢复到事务执行前的状态。
  • undolog是逻辑日志,记录SQL语句。

8.SQL执行的顺序(一般情况下)

  • 手写:
SELECT DISTINCT
 <select_list>
FORM
    <left_table> <jion type> JOIN <right_table> ON <join_condition>
WHERE
    <where_condition>
GROUP BY
    <group_by_list>
HAVING
    <having_condition>
ORDER BY
    <order_by_condition>
LIMIT <limit_number>
  • 机器读的顺序:
FORM <left_table> ON <join_conditon> <join_type> JOIN <right_table>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
SELECT DISTINCT
    <select_list>
ORDER BY <order_by_condition>
LIMIT <limit_number>

9.7中join图

image.png

  • 1 A、B两表共有
    • select * from tbl_emp a inner join tbl_dept b on a.deptId = b.id;
  • 2 A、B量表共有+A独有
    • select * from tbl_emp a left join tal_dept b on a.deptId = b.id;
  • 3 A、B两表共有+B独有
    • select * from tbl_emp a right join tbl_dept b on a.deptId = b.id;
  • 4 A的独有
    • select * from tbl_emp a left join tbl_dept b on a.deptId=b.id where b.id is null;
  • 5 B的独有
    • select * from tbl_emp a right join tbl_dept b on a.deptId=b.id where a.deptId is null;
  • 6.AB全由
    • select * from tbl_emp a left join tbl_dept b on a.deptId=b.id union select * from tbl_emp a right join tbl_dept b on a.deptId=b.id
  • 7.A的独有 + B的独有
    • select * from tbl_emp a left join tbl_dept b on a.deptId=b.id where b.id is null union select * from tbl_emp a right join tbl_dept b on a.deptId=b.id where a.deptId is null;

10. Innodb的行锁到底锁住了什么?

  • InnoDB的行锁,是通过锁住索引来实现的,如果加锁查询的时候没有使用到索引,会将整个聚簇索引都锁住,相当于锁表。
  • 命中索引锁行,没有命中锁表,问题会扩大化。

11. Explan

  • 是什么:使用EXPLAN关键字可以模拟优化器执行SQL查询语句,从而直到MySQL是如何处理你的SQL语句。分析你的查询语句或是表结构的性能瓶颈。

  • 使用: EXPLAN + SQL语句

    • 执行计划包含的信息: image.png
  • 作用:

    • 表的读取顺序
    • 数据读取操作的操作类型
    • 那些索引可以使用
    • 那些索引被实际使用
    • 表之间的引用
    • 每张表有多少行被优化器查询
  • EXPLAN的各个字段的解释:

    • id: select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

      • id相同,执行顺序由上至下
      • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
      • id相同不同,同时存在:id如果相同可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
      • 注意:id号每个号码,表示一趟独立的查询,一个SQL的查询趟数越少越好。
    • select_type: 查询的类型,主要用于区别普通查询、联合查询、子查询等复杂查询

      • SIMPLE:简单的select查询,查询中不包含子查询和UNION
      • PRIMARY: 若查询语句中包含复杂的子查询,那么最外层的查询被标记为PRIMARY
      • SUBQUERY: 在SELECT或WHERE中包含了子查询。
      • DERIVED: 在FROM 列表中包含的子查询被标记为DERIVED(衍生) MySQl会递归执行这些子查询,把结果放在临时表里
      • UNION: 若第二个SELECT 出现在UNION之后,则被标记为UNION; 若 UNION 包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
      • UNION: 从UNION表获取结果的SELECT
        image.png
    • table: 显示这一行的数据是关于哪张表的

    • partions: 代表分区表中的命中情况,如果没有进行过分操作的非分区表,该项为null

    • type: 显示的是访问类型,是较为重要的一个指标。 image.png

      • 结果值从最好到最坏依次是: system > const < eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
      • system > const >eq_ref > ref> range > index > ALL
      • 一般来说,的保证查询至少达到range级别,最好能达到ref
      • system:表只有一行记录(等于系统表),这是const类型的特例, 平时不会出现,这个也可以忽略不计
      • const: 表示通过索引一次就找到了,const 用于比较primary key 或者 unique 索引。 因为只匹配一行数据,所以很快将主键置于where列表中, MySQL就能将该查询转换为一个常量
      • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
      • ref: 非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体。
      • range: 只检索给定范围的行,使用一个索引来选择行。 key 列显示使用了哪个索引,一般就是在你where语句中出现了 between、< 、>、 like 、in 等查询。 这种范围扫描索引扫描比全表扫描要好,因为它只需要开始索引的某一点,而结束语另一点,不用扫描全部索引
      • index: FULL Index Scan, index与ALL却别为index类型只遍历索引树, 这通常比ALL快,因为索引文件通常比数据文件小。 (也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘中读取的)
      • all: FULL Table Scan, 将遍历全表以找到匹配的行
      • index_merage: 在查询过程中需要多个索引组合使用,通常出现在有 or 的关键字的sql中
      • ref_or_null: 对于某个字段既需要关联条件,也需要null值得其概况下。 查询优化器会选择使用ref_or_null 连接查询
      • index_subquery: 利用索引来关联子查询,不再全表扫描
      • unique_subquery: 该连接类型类似于index_subquery。子查询中唯一索引。
    • possible_keys: 显示可能应用在这张表中的索引,一个或多个。 查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

    • key: 实际使用的索引。 如果为nuLL, 则没有使用索引。 查询中若使用了覆盖索引,则该索引和查询的select字段重叠

    • key_len: 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。 葛奴据它可以判断索引的使用情况,尤其是在组合索引的时候,判断该索引有多少部分被使用到非常重要。
      image.png

    • ref: 显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。

    • rows: 显示MySQL认为它执行查询时必须检查的行数,值越小越好

    • filtered: 这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意时百分比,不是具体记录数。

    • Extra: 包含不适合在其他列中显示但十分重要的额外信息 -Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内索引顺序进行读取。 MySQL中无法利用索引完成的排序操作称为“文件排序”

      • Using temporary: 使用临时表保存中间结果,MySQL在对查询结果排序时使用临时表,常见于排序order by 和分组查询 group by

      • USING index: 表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效果不错! 如果同时出现using where, 表明索引被用来执行索引键值的查找; 如果没有同时出现using where,表明索引用来读取数据非执行查找动作。

      • USING where: 表明使用了where过滤。

SQL优化的技巧?

慢查询问题排查