Mysql 面试必知必会

852 阅读7分钟

存储引擎

存储引擎负责对表中的数据进行读取写入,常用的存储引擎InnoDBMyISAMMenory等。

InnoDB和MyISAM的区别
InnoDB MyISAM
事务 支持 不支持
支持MVCC行锁、表锁 表锁
外键 支持 不支持
存储空间 由于需要高速缓存,较大 可压缩
适用场景 有一定量的update和Insert 大量的查询

InnoDB存储引擎

读取数据的方式

把数据分成若干个页,当需要从磁盘读取数据的时候,每次会从磁盘加载16KB的数据到内存中。

行格式

常见的行格式有Compact、Redundant、Dynamic、Compressed

索引

索引是帮助MySQL高效获取数据的数据结构

B+树索引和Hash索引的区别
  • B+树是一个平衡的多叉树,Hash索引采用的是一定的哈希算法
  • Hash索引更适用于等值查询,只需要经过一次hash就能找到相应的值,有可能会出现hash碰撞;B+树索引更适用于范围查询检索
  • 哈希索引无法利用索引进行排序
  • 哈希索引不支持联合索引的最左匹配原则

常见的索引种类有:主键索引(primary)、唯一索引(unique)、普通索引(indx)、联合索引。

主键索引又称聚簇索引,其余的都成为非聚簇索引,或者也称为非主键索引,或者二级索引

聚簇索引和非聚集索引

聚集索引采用的数据结构是B+树。

聚集索引的叶子节点存储的是整行数据,非聚集索引的叶子节点存储的是索引列和主键

如果要是通过非聚集索引获取整行数据的某个字段值,那么流程应该是这样的:

  • 首先通过查询条件找到对应的主键id
  • 从id主键的B+树上找到对应id的数据。
  • 从整行数据中获取对应的字段值。

这个流程的过程叫做回表

覆盖索引

覆盖索引,即从非聚簇索引中就能查到记录,不需要再次从主键索引中获取记录。

优点:避免回表的产生,减少I/O,提高性能。

可以通过explain命令,查看Extra中是否有Using index。

建立索引的原则
  • 符合最左前缀原则,联合索引要把查询频率最高的列放在最左边。

  • 查询频率比较高的列。

  • 索引列不能参与计算,最好使用简单的数字类型。

  • 主键自增

  • 尽量使用覆盖索引进行查询,避免回表带来的性能损耗。

事务

事务的隔离级别
Read uncommitted(未提交读)

即便事务没有提交,但是仍然可以获取到未提交的数据,这是所有隔离级别中最低的一种。

带来的问题:会出现脏读:一个事务读取到另外一个事务未提交更新的数据。

Read committed(已提交读)(RC)

只能读取其他事务已经提交的数据,未提交的数据读取不到。

带来的问题:

  • 在同一个事务会话中,可能会出现读取两次结果不同。这就造成了不可重复读
  • 范围查询导致查询到的次数不同,这就造成了幻读
Repetable read(可重复读)(RR)-->InnoDB默认级别

在同一事务内的查询都是事务开始时刻一致的。

一个事务第一次读取某条记录之后,即使其他事务修改了该记录的值并且提交了,该事务读取的值仍然是第一次读取到的值,解决了不可重复读。对于Mysql而言,也解决了幻读

Serializable(串行化)

其他会话对该表进行写操作,将会被挂起。

版本链

对于每一次记录的改动,都会记录一条undo日志,每条undo日志都会有一个roll_pointer属性,把这些undo日志都连接起来,串成一个链表,就形成了一个版本链

ReadView

ReadView中主要包含当前系统中有哪些活跃的读写事务,把事务id放到一个列表中,这个列表的名称叫m_ids

对于 Read committed ,每次读取数据前都会生成一个ReadView。

对于 Repetable Read ,只会在第一次读取数据的时候生成一个ReadView。

MVVC

MVVC(Multi-Version Concurrentcy Contrl),多版本并发控制:在使用read commitrepetable read这两种隔离级别的不同的事务读-写写-读操作并发执行,从而提高性能。

读锁和写锁
  • S-共享锁:又称读锁。其他事务可以继续加共享锁,但是不能继续加排他锁。
  • X-排他锁:又称写锁。一旦加了写锁,其他事务就不能加锁了。

纵轴表示已有的锁,横轴表示尝试获取的锁。

S-共享锁 X-排他锁
S-共享锁 兼容 冲突
X-排他锁 冲突 冲突

兼容性:事务A获得一个锁后,事务B如果也能立即获取到该锁,则成兼容,反之叫冲突。

意向锁

意向锁在InnoDB中是表级锁,用来表示一个事务想要获取什么。

  • 意向共享锁:表达一个事务想要获取一张表中的某几行的共享锁。
  • 意向排他锁:表达一个事务想要获取一张表中的某几行的排他锁。
自增长锁

自增长锁是一种特殊的表锁机制,可以提升并发插入性能。特点如下:

  • 在SQL执行完就释放锁,并不是事务执行完。
  • 对于大数据量插入会影响插入性能,因为会堵塞另外一个事务的执行。

InnoDB加锁算法

记录锁(Record-Lock)

记录锁封锁住的是索引记录,而不是我们真正的数据记录。

形成记录锁的方式一
-- id 列为主键列或唯一索引列
SELECT * FROM table WHERE id = 1 FOR UPDATE;

id为1的记录行会被锁住。

需要注意的是:

  • id列必须为唯一索引列或者主键列,否则该sql加的锁就会变成临建锁
  • 查询语句为精确查询,不能为范围查询或者模糊查询,否则将退化为临建锁
形成记录锁的方式二

通过主键索引或者唯一索引对数据进行update操作时,也会对该行数据添加记录锁

-- id 列为主键列或唯一索引列
UPDATE SET age = 50 WHERE id = 1;
间隙锁(Gap Lock)

间隙锁是基于 非唯一索引 ,它锁定的是一段范围的索引记录。

间隙锁基于Next-Key Loking算法,间隙锁锁住的是一个区间,而不仅仅是这个区间的每一条数据。

SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;

这个sql会把所有在(1,10)区间的记录行锁住。id在[2,9]之间的数据行的插入都会被堵塞,但是1和10两条记录并不会锁住。

临建锁(Next-key Lock)

临建锁可以理解为一种特殊的间隙锁,可以理解为一种特殊的算法。

通过临建锁可以解决幻读的问题,每个数据行上的非唯一索引上都会存在一把临建锁,当某个事物持有该数据行的临建锁时,会锁住一段左开右闭区间的数据。

死锁

死锁成因

死锁一般是事务相互等待对方资源,最后形成环路造成的。

如何尽可能避免死锁?
  • 以固定的顺序访问表和行。
  • 同一个事务中,尽可能做到一次锁定所需要的所有资源。
  • 为表添加合理的索引。如果不走索引,将会为表的每一行记录添加上锁,死锁的概率大大增大。
如何定位死锁?
  • 通过应用业务日志定位到问题代码,找到相对应的sql。

  • 确定数据库隔离级别

    执行select @@global.tx_isolation ,可以确定数据库的隔离级别,如果我们数据库的隔离级别是RC,这样就可以很大概率上排除间隙锁造成的死锁嫌疑。

  • 执行 show InnoDB status 查看最近死锁的日志。