天天看点

面试必问之mysql基础

mysql存储引擎

如何选择mysql存储引擎

先得了解下各个存储引擎区别

功能 MylSAM MEMORY InnoDB Archive
功能 MylSAM MEMORY InnoDB Archive
存储限制 256TB RAM 64TB None
支持事务 No No Yes No
支持全文索引 Yes No No No
支持树索引 Yes Yes Yes No
支持哈希索引 No Yes No No
支持数据缓存 No N/A Yes No
支持外键 No No Yes No

可以根据以下的原则来选择 MySQL 存储引擎:

  • 如果要提供提交、回滚和恢复的事务安全(ACID 兼容)能力,并要求实现并发控制,InnoDB 是一个很好的选择。
  • 如果数据表主要用来插入和查询记录,则 MyISAM 引擎提供较高的处理效率。
  • 如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存的 MEMORY 引擎中,MySQL 中使用该引擎作为临时表,存放查询的中间结果。
  • 如果只有 INSERT 和 SELECT 操作,可以选择Archive 引擎,Archive 存储引擎支持高并发的插入操作,但是本身并不是事务安全的。Archive 存储引擎非常适合存储归档数据,如记录日志信息可以使用 Archive 引擎。

提示:使用哪一种引擎要根据需要灵活选择,一个数据库中多个表可以使用不同的引擎以满足各种性能和实际需求。使用合适的存储引擎将会提高整个数据库的性能。

详细说一下innodb与MyISAM的区别

MyISAM和InnoDB的区别:

  1. InnoDB支持事务,MyISAM不支持。对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
  2. InnoDB支持外键,而MyISAM不支持。
  3. InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
  4. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快。
  5. Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;5.7以后的InnoDB支持全文索引了。
  6. InnoDB支持表、行级锁(默认),而MyISAM支持表级锁。
  7. InnoDB表必须有主键(用户没有指定的话会自己找或生产一个主键),而Myisam可以没有。
  8. Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI。 Innodb:frm是表定义文件,ibd是数据文件。 Myisam:frm是表定义文件,myd是数据文件,myi是索引文件。

索引

什么是索引

索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据。

有哪几种索引

按照功能划分

  • 普通索引:最基本的索引,没有任何约束。
  • 唯一索引:与普通索引类似,但具有唯一性约束。
  • 主键索引:特殊的唯一索引,不允许有空值。
  • 复合索引:将多个列组合在一起创建索引,可以覆盖多个列。
  • 外键索引:只有InnoDB类型的表才可以使用外键索引,保证数据的一致性、完整性和实现级联操作。
  • 全文索引:MySQL 自带的全文索引只能用于 InnoDB、MyISAM ,并且只能对英文进行全文检索,一般使用全文索引引擎(ES,Solr)。

按照结构划分

  • Hash索引
  • B+ Tree索引

我们常用的innodb默认是B+索引

为什么采用B+ 树吗?

B+树是为磁盘及其他存储辅助设备而设计一种平衡查找树(不是二叉树)。B+树中,所有记录的节点按大小顺序存放在同一层的叶节点中,各叶节点用指针进行连接。

数据库中B+树索引分为聚集索引(clustered index)和非聚集索引(secondary index).这两种索引的共同点是内部都是B+树,高度都是平衡的,叶节点存放着所有数据。不同点是叶节点是否存放着一整行数据。

B+树有如下特点:

  • B+树每个节点可以包含更多的节点,这样做有两个原因,一个是降低树的高度。另外一个是将数据范围变为多个区间,区间越多,数据检索越快。
  • 每个节点不再只是存储一个key了,可以存储多个key。
  • 非叶子节点存储key,叶子节点存储key和数据。
  • 叶子节点两两指针相互链接,顺序查询性能更高。

通俗的讲

  • B+树的非叶子节点只是存储key,占用空间非常小,因此每一层的节点能索引到的数据范围更加的广。换句话说,每次IO操作可以搜索更多的数据。
  • 叶子节点两两相连,符合磁盘的预读特性。比如叶子节点存储50和55,它有个指针指向了60和62这个叶子节点,那么当我们从磁盘读取50和55对应的数据的时候,由于磁盘的预读特性,会顺便把60和62对应的数据读取出来。这个时候属于顺序读取,而不是磁盘寻道了,加快了速度。
  • 支持范围查询,而且部分范围查询非常高效,每个节点能索引的范围更大更精确,也意味着 B+树单次磁盘IO的信息量大于B-树,I/O效率更高。

B+树和Hash索引比较起来有什么优缺点吗?

  • Hash索引仅仅能满足"=","IN"和""查询,不能使用范围查询,因为经过相应的Hash算法处理之后的Hash值的大小关系,并不能保证和Hash运算前完全一样;
  • Hash索引无法被用来避免数据的排序操作,因为Hash值的大小关系并不一定和Hash运算前的键值完全一样;
  • Hash索引不能利用部分索引键查询,对于组合索引,Hash索引在计算Hash值的时候是组合索引键合并后再一起计算Hash值,而不是单独计算Hash值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash索引也无法被利用;
  • Hash索引在任何时候都不能避免表扫描,由于不同索引键存在相同Hash值,所以即使取满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要回表查询数据;
  • Hash索引遇到大量Hash值相等的情况后性能并不一定就会比B+树索引高。 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
  • 如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
  • 同理,哈希索引没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
  • 哈希索引也不支持多列联合索引的最左匹配规则;
  • B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。
  • 在大多数场景下,都会有范围查询、排序、分组等查询特征,用B+树索引就可以了。

为什么主键推荐自增长

因为使用自增 id 可以避免页分裂

什么是页分裂

mysql (注意本文讲的 mysql 默认为InnoDB 引擎)底层数据结构是 B+ 树,所谓的索引其实就是一颗 B+ 树,一个表有多少个索引就会有多少颗 B+ 树,mysql 中的数据都是按顺序保存在 B+ 树上的(所以说索引本身是有序的)。 然后 mysql 在底层又是以数据页为单位来存储数据的,一个数据页大小默认为 16k,当然你也可以自定义大小,也就是说如果一个数据页存满了,mysql 就会去申请一个新的数据页来存储数据。 如果主键为自增 id 的话,mysql 在写满一个数据页的时候,直接申请另一个新数据页接着写就可以了。

如果主键是非自增 id,为了确保索引有序,mysql 就需要将每次插入的数据都放到合适的位置上。

当往一个快满或已满的数据页中插入数据时,新插入的数据会将数据页写满,mysql 就需要申请新的数据页,并且把上个数据页中的部分数据挪到新的数据页上。

这就造成了页分裂,这个大量移动数据的过程是会严重影响插入效率的。

其实对主键 id 还有一个小小的要求,在满足业务需求的情况下,尽量使用占空间更小的主键 id,因为普通索引的叶子节点上保存的是主键 id 的值,如果主键 id 占空间较大的话,那将会成倍增加 mysql 空间占用大小。

mysql锁了解吗?

MySQL有哪几种锁,能说下吗?

1. 类型维度

  • 共享锁(读锁 / S 锁)
  • 排它锁(写锁 / X 锁) 类型细分: 意向共享锁 意向排他(互斥)锁
  • 悲观锁(使用锁,即 for update)
  • 乐观锁(使用版本号字段,类似 CAS 机制,即用户自己控制。缺点:并发很高的时候,多了很多无用的重试)

2. 锁的粒度(粒度维度)

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

3. 锁的算法(算法维度)

  • Record Lock(单行记录)
  • Gap Lock(间隙锁,锁定一个范围,但不包含锁定记录)
  • Next-Key Lock(Record Lock + Gap Lock,锁定一个范围,并且锁定记录本身, MySql 防止幻读,就是使用此锁实现)

默认读会上锁吗?

默认是 MVCC 机制(“一致性非锁定读”)保证 RR 级别的隔离正确性,是不上锁的。 可以选择手动上锁:select xxxx for update (排他锁); select xxxx lock in share mode(共享锁),称之为“一致性锁定读”。

使用锁之后,就能在 RR 级别下,避免幻读。当然,默认的 MVCC 读,也能避免幻读。

高并发情况下锁会带来有哪些问题?

在高并发的情况下事务的并发处理会带来几个问题

  • 脏读:指在事务 A 处理过程里读取到了事务 B 未提交的事务中的数据。比如在转账的例子中:小 A 开了一个事务给小 B 转了1000 块,还没提交事务的时候就跟小 B 说,钱已经到账了。这个时候小 B 去看了一下余额,发现果真到账了(然后就开开心心刷抖音去了),这个时候小 A 回滚了事务,把 1000 块又搞回去了。小 B 刷完抖音再去看下余额,发现钱又不见了。
  • 不可重复读:指在一个事务执行的过程中多次查询某一数据的时候结果不一致的现象,由于在执行的过程中被另一个事务修改了这个数据并提交了事务。比如:事务 A 第一次读小明的年龄是 18 岁,此时事务 B 将小明的年龄改成了 20 并提交了,这个时候事务 A 再次读取小明的年龄发现是 20,这就是同一条数据不可重复读。
  • 幻读:幻读通常指的是对一批数据的操作完成后,有其他事务又插入了满足条件的数据导致的现象。比如:事务 A 将数据库性别为男的状态都改成1 表示有钱人,这个时候事务 B 又插入了一条状态为 0 没钱人的记录,这个时候,用户再查看刚刚修改的数据时就会发现还有一行没有修改,这就出现了幻读。幻读往往针对 insert 操作,脏读和不可重复读针对 select 操作。

mysql是怎么处理的

mysql针对上述问题增加了事务的隔离级别

  • Read uncommitted (读未提交):最低级别,任何情况都无法保证。
  • Read committed (读已提交):可避免脏读的发生。
  • Repeatable read (可重复读):可避免脏读、不可重复读的发生。
  • Serializable (串行化):可避免脏读、不可重复读、幻读的发生。