myslq 面试题一

107 阅读22分钟

1 基础 1.1 基本概念 1、MySQL有哪些数据库类型? 数值类型 有包括 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示 1 字节、2 字节、3 字节、4 字节、8 字节的整数类型。

1)任何整数类型都可以加上 UNSIGNED 属性,表示无符号整数。

2)任何整数类型都可以指定长度,但它不会限制数据的合法长度,仅仅限制了显示长度。

还有包括 FLOAT、DOUBLE、DECIMAL 在内的小数类型。

字符串类型 包括 VARCHAR、CHAR、TEXT、BLOB。

注意:VARCHAR(n) 和 CHAR(n) 中的 n 并不代表字节个数,而是代表字符的个数。

日期和时间类型 常用于表示日期和时间类型为 DATETIME、DATE 和 TIMESTAMP。

尽量使用 TIMESTAMP,空间效率高于 DATETIME。

ref MySQL 数据类型

2、CHAR 和 VARCHAR 区别? 1)首先可以明确的是 CHAR 是定长的,而 VARCHAR 是可以变长。

CHAR 会根据声明的字符串长度分配空间,并会使用空格对字符串右边进行尾部填充。所以在检索 CHAR 类型数据时尾部空格会被删除,如保存的是字符串 'char ',但最后查询到的是 'char'。又因为长度固定,所以存储效率高于 VARCHAR 类型。

VARCHAR 在 MySQL 5.0 之后长度支持到 65535 字节,但会在数据开头使用额外 1~2 个字节存储字符串长度(列长度小于 255 字节时使用 1 字节表示,否则 2 字节),在结尾使用 1 字节表示字符串结束。

2)再者,在存储方式上,CHAR 对英文字符(ASCII)占用 1 字节,对一个汉字使用用 2 字节。而 VARCHAR 对每个字符均使用 2 字节。

虽然 VARCHAR 是根据字符串长度分配存储空间的,但在内存中依旧使用声明长度进行排序等作业,故在使用时仍需综合考量字段长度。

3、CHAR 和 VARCHAR 如何选择? 1)对于经常变更的数据来说,CHAR 比 VARCHAR更好,因为 CHAR 不容易产生碎片。

2)对于非常短的列或固定长度的数据(如 MD5),CHAR 比 VARCHAR 在存储空间上更有效率。

4)使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。

4)尽量避免使用 TEXT/BLOB 类型,查询时会使用临时表,导致严重的性能开销。

4、CHAR,VARCHAR 和 Text 的区别? 1)长度区别

Char 范围是 0~255。

Varchar 最长是 64k(注意这里的 64k 是整个 row 的长度,要考虑到其它的 column,还有如果存在 not null 的时候也会占用一位,对不同的字符集,有效长度还不一样,比如 utf-8 的,最多 21845,还要除去别的column),但 Varchar 在一般情况下存储都够用了。

如果遇到了大文本,考虑使用 Text,最大能到 4G(其中 TEXT 长度 65,535 bytes,约 64kb;MEDIUMTEXT 长度 16,777,215 bytes,约 16 Mb;而 LONGTEXT 长度 4,294,967,295 bytes,约 4Gb)。

2)效率区别

效率来说基本是 Char > Varchar > Text,但是如果使用的是 Innodb 引擎的话,推荐使用 Varchar 代替 Char。

3)默认值区别

Char 和 Varchar 支持设置默认值,而 Text 不能指定默认值。

1.2 数据库设计 1、什么是三大范式? 第一范式(1NF):字段(或属性)是不可分割的最小单元,即不会有重复的列,体现原子性

第二范式(2NF):满足 1NF 前提下,存在一个候选码,非主属性全部依赖该候选码,即存在主键,体现唯一性,专业术语则是消除部分函数依赖

第三范式(3NF):满足 2NF 前提下,非主属性必须互不依赖,消除传递依赖

ref:如何理解关系型数据库的常见设计范式?

除了三大范式外,还有BC范式和第四范式,但其规范过于严苛,在生产中往往使用不到。

2、什么是范式和反范式,以及各自优缺点? 范式是符合某一种级别的关系模式的集合。构造数据库必须遵循一定的规则。在关系数据库中,这种规则就是范式。

名称 优点 缺点 范式 范式化的表减少了数据冗余,数据表更新操作快、占用存储空间少。 查询时通常需要多表关联查询,更难进行索引优化 反范式 反范式的过程就是通过冗余数据来提高查询性能,可以减少表关联和更好进行索引优化 存在大量冗余数据,并且数据的维护成本更高 所以在平时工作中,我们通常是将范式和反范式相互结合使用。

2 索引 首先了解一下什么是索引,索引是对数据库表中一列或多列的值进行排序的数据结构,用于快速访问数据库表中的特定信息。

1、索引的几种类型或分类? 1)从物理结构上可以分为聚集索引和非聚集索引两类:

聚簇索引指索引的键值的逻辑顺序与表中相应行的物理顺序一致,即每张表只能有一个聚簇索引,也就是我们常说的主键索引;

非聚簇索引的逻辑顺序则与数据行的物理顺序不一致。

2)从应用上可以划分为一下几类:

普通索引:MySQL 中的基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了提高查询效率。通过 ALTER TABLE table_name ADD INDEX index_name (column) 创建;

唯一索引:索引列中的值必须是唯一的,但是允许为空值。通过 ALTER TABLE table_name ADD UNIQUE index_name (column) 创建;

主键索引:特殊的唯一索引,也成聚簇索引,不允许有空值,并由数据库帮我们自动创建;

组合索引:组合表中多个字段创建的索引,遵守最左前缀匹配规则;

全文索引:只有在 MyISAM 引擎上才能使用,同时只支持 CHAR、VARCHAR、TEXT 类型字段上使用。

2、索引的优缺点? 先来说说优点:创建索引可以大大提高系统的性能。

通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

可以大大加快数据的检索速度,这也是创建索引的最主要的原因。

可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。

在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。

通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

既然增加索引有如此多的优点,为什么不对表中的每一个列都创建一个索引呢?这是因为索引也是有缺点的:

创建和维护索引需要耗费时间,这种时间随着数据量的增加而增加,这样就降低了数据的维护速度。

索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。如果要建立聚簇索引,那么需要的空间就会更大。

3、索引设计原则? 选择唯一性索引; 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。

为常作为查询条件的字段建立索引; 如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。

为经常需要排序、分组和联合操作的字段建立索引; 经常需要 ORDER BY、GROUP BY、DISTINCT 和 UNION 等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。

限制索引的数目; 每个索引都需要占⽤用磁盘空间,索引越多,需要的磁盘空间就越大,修改表时,对索引的重构和更新很麻烦。

小表不建议索引(如数量级在百万以内); 由于数据较小,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果。

尽量使用数据量少的索引; 如果索引的值很长,那么查询的速度会受到影响。此时尽量使用前缀索引。

删除不再使用或者很少使用的索引。 4、索引的数据结构? 索引的数据结构和具体存储引擎的实现有关,MySQL 中常用的是 Hash 和 B+ 树索引。

Hash 索引底层就是 Hash 表,进行查询时调用 Hash 函数获取到相应的键值(对应地址),然后回表查询获得实际数据.

B+ 树索引底层实现原理是多路平衡查找树,对于每一次的查询都是从根节点出发,查询到叶子节点方可以获得所查键值,最后查询判断是否需要回表查询.

5、Hash 和 B+ 树索引的区别? Hash 1)Hash 进行等值查询更快,但无法进行范围查询。因为经过 Hash 函数建立索引之后,索引的顺序与原顺序无法保持一致,故不能支持范围查询。同理,也不支持使用索引进行排序。

2)Hash 不支持模糊查询以及多列索引的最左前缀匹配,因为 Hash 函数的值不可预测,如 AA 和 AB 的算出的值没有相关性。

3)Hash 任何时候都避免不了回表查询数据.

4)虽然在等值上查询效率高,但性能不稳定,因为当某个键值存在大量重复时,产生 Hash 碰撞,此时查询效率反而可能降低。

B+ Tree

1)B+ 树本质是一棵查找树,自然支持范围查询和排序。

2)在符合某些条件(聚簇索引、覆盖索引等)时候可以只通过索引完成查询,不需要回表。

3)查询效率比较稳定,因为每次查询都是从根节点到叶子节点,且为树的高度。

6、为何使用 B+ 树而非二叉查找树做索引? 我们知道二叉树的查找效率为 O(logn),当树过高时,查找效率会下降。另外由于我们的索引文件并不小,所以是存储在磁盘上的。

文件系统需要从磁盘读取数据时,一般以页为单位进行读取,假设一个页内的数据过少,那么操作系统就需要读取更多的页,涉及磁盘随机 I/O 访问的次数就更多。将数据从磁盘读入内存涉及随机 I/O 的访问,是数据库里面成本最高的操作之一。

因而这种树高会随数据量增多急剧增加,每次更新数据又需要通过左旋和右旋维护平衡的二叉树,不太适合用于存储在磁盘上的索引文件。

7、为何使用 B+ 树而非 B 树做索引? 在此之前,先来了解一下 B+ 树和 B 树的区别:

B 树非叶子结点和叶子结点都存储数据,因此查询数据时,时间复杂度最好为 O(1),最坏为 O(log n)。而 B+ 树只在叶子结点存储数据,非叶子结点存储关键字,且不同非叶子结点的关键字可能重复,因此查询数据时,时间复杂度固定为 O(log n)。

B+ 树叶子结点之间用链表相互连接,因而只需扫描叶子结点的链表就可以完成一次遍历操作,B 树只能通过中序遍历。

为什么 B+ 树比 B 树更适合应用于数据库索引?

B+ 树减少了 IO 次数。 由于索引文件很大因此索引文件存储在磁盘上,B+ 树的非叶子结点只存关键字不存数据,因而单个页可以存储更多的关键字,即一次性读入内存的需要查找的关键字也就越多,磁盘的随机 I/O 读取次数相对就减少了。

B+ 树查询效率更稳定 由于数据只存在在叶子结点上,所以查找效率固定为 O(log n),所以 B+ 树的查询效率相比B树更加稳定。

B+ 树更加适合范围查找 B+ 树叶子结点之间用链表有序连接,所以扫描全部数据只需扫描一遍叶子结点,利于扫库和范围查询;B 树由于非叶子结点也存数据,所以只能通过中序遍历按序来扫。也就是说,对于范围查询和有序遍历而言,B+ 树的效率更高。

ref 为什么 B+ 树比 B 树更适合应用于数据库索引?

8、什么是最左匹配原则? 顾名思义,最左优先,以最左边为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。

如建立 (a,b,c,d) 索引,查询条件 b = 2 是匹配不到索引的,但是如果查询条件是 a = 1 and b = 2 或 a=1 又或 b = 2 and a = 1 就可以,因为优化器会自动调整 a,b 的顺序。

再比如 a = 1 and b = 2 and c > 3 and d = 4,其中 d 是用不到索引的,因为 c 是一个范围查询,它之后的字段会停止匹配。

最左匹配的原理

上图可以看出 a 是有顺序的(1、1、2、2、3、3),而 b 的值是没有顺序的(1、2、1、4、1、2)。所以 b = 2 这种查询条件无法利用索引。

同时我们还可以发现在 a 值相等的情况下(a = 1),b 又是顺序排列的,所以最左匹配原则遇上范围查询就会停止,剩下的字段都无法使用索引。

ref 最左匹配原则

9、什么是覆盖索引? 在 B+ 树的索引中,叶子节点可能存储了当前的键值,也可能存储了当前的键值以及整行的数据,这就是聚簇索引和非聚簇索引。 在 InnoDB 中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则隐式的生成一个键来建立聚簇索引。

当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询。

10、什么是索引下推? 索引下推(Index condition pushdown) 简称 ICP,在 Mysql 5.6 版本上推出的一项用于优化查询的技术。

在不使用索引下推的情况下,在使用非主键索引进行查询时,存储引擎通过索引检索到数据,然后返回给 MySQL 服务器,服务器判断数据是否符合条件。

而有了索引下推之后,如果存在某些被索引列的判断条件时,MySQL 服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合 MySQL 服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给 MySQL 服务器。

索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少 MySQL 服务器从存储引擎接收数据的次数。

ref Mysql性能优化:什么是索引下推?

3 存储 3.1 存储引擎 1、有哪些常见的存储引擎? ref 几种MySQL数据库引擎优缺点对比

2、MyISAM 和 InnoDB 的区别? 1)InnoDB 支持事务,而 MyISAM 不支持。

2)InnoDB 支持外键,而 MyISAM 不支持。因此将一个含有外键的 InnoDB 表 转为 MyISAM 表会失败。

3)InnoDB 和 MyISAM 均支持 B+ Tree 数据结构的索引。但 InnoDB 是聚集索引,而 MyISAM 是非聚集索引。

4)InnoDB 不保存表中数据行数,执行 select count(*) from table 时需要全表扫描。而 MyISAM 用一个变量记录了整个表的行数,速度相当快(注意不能有 WHERE 子句)。

那为什么 InnoDB 没有使用这样的变量呢?因为InnoDB的事务特性,在同一时刻表中的行数对于不同的事务而言是不一样的。

5)InnoDB 支持表、行(默认)级锁,而 MyISAM 支持表级锁。

InnoDB 的行锁是基于索引实现的,而不是物理行记录上。即访问如果没有命中索引,则也无法使用行锁,将要退化为表锁。

6)InnoDB 必须有唯一索引(如主键),如果没有指定,就会自动寻找或生产一个隐藏列 Row_id 来充当默认主键,而 Myisam 可以没有主键。

ref MyISAM与InnoDB 的区别(9个不同点)

3、InnoDB 的四大特性? 插入缓冲insert buffer) 二次写(double write) 自适应哈希索引(ahi) 预读(read ahead) 4、InnoDB 为何推荐使用自增主键? 自增 ID 可以保证每次插入时 B+ 树索引是从右边扩展的,因此相比自定义 ID (如 UUID)可以避免 B+ 树的频繁合并和分裂。如果使用字符串主键和随机主键,会使得数据随机插入,效率比较差。

5、如何选择存储引擎? 默认使用 InnoDB,MyISAM 适用以插入为主的程序,比如博客系统、新闻门户。

3.2 存储结构 1、什么是 InnoDB 的页、区、段? 页(Page) 首先,InnoDB 将物理磁盘划分为页(page),每页的大小默认为 16 KB,页是最小的存储单位。页根据上层应用的需要,如索引、日志等,分为很多的格式。我们主要说数据页,也就是存储实际数据的页。

区(Extent) 如果只有页这一个层次的话,页的个数是非常多的,存储空间的分配和回收都会很麻烦,因为要维护这么多的页的状态是非常麻烦的。

所以,InnoDB 又引入了区(Extent) 的概念。一个区默认是 64 个连续的页组成的,也就是 1MB。通过 Extent 对存储空间的分配和回收就比较容易了。

段(Segment) 为什么要引入段呢,这要从索引说起。我们都知道索引的目的是为了加快查找速度,是一种典型的用空间换时间的方法。

B+ 树的叶子节点存放的是我们的具体数据,非叶子结点是索引页。所以 B+ 树将数据分为了两部分,叶子节点部分和非叶子节点部分,也就我们要介绍的段 Segment,也就是说 InnoBD 中每一个索引都会创建两个 Segment 来存放对应的两部分数据。

Segment 是一种逻辑上的组织,其层次结构从上到下一次为 Segment、Extent、Page。

2、页由哪些数据组成? 首先看数据页的基本格式,如下图:

File Header 用于描述数据页的外部信息,比如属于哪一个表空间、前后页的页号等。

Page Header 用来描述数据页中的具体信息,比如存在多少条纪录,第一条纪录的位置等。

infimum 和 supremum 纪录 infimum 和 supremum 是系统生成的纪录,分别为最小和最大纪录值,infimum 的下一条是用户纪录中键值最小的纪录,supremum 的上一条是用户纪录中键值最大的纪录,通过 next_record 字段来相连。

User Records 用户纪录,也就是数据库表中对应的数据,这里我们说常用的 Compact 格式。

InnoDB 除了我们插入的数据外,还有一些隐藏列,transaction_id(事务ID)、roll_pointer(回滚指针)是一定添加的。

row_id 则不一定,根据以下策略生成:优先使用用户建表时指定的主键,若用户没有指定主键,则使用unique键。若unique键都没有,则系统自动生成row_id,为隐藏列。

Free Space 页中目前空闲的存储,可以插入纪录。

Page Dictionary 类似于字典的目录结构,根据主键大小,每隔 4-8 个纪录设置一个槽,用来纪录其位置,当根据主键查找数据时,首先一步到位找到数据所在的槽,然后在槽中线性搜素。这种方法比从前到后遍历页的链表的效率更快。

Page Tailer File Header存储刷盘前内存的校验和,Page Tailer储存刷盘后的校验和。当刷盘的时候,出现异常,Page Tailer和File Header中的校验和不一致,则说明出现刷盘错误。

3、页中插入记录的过程? 1)如果 Free Space 的空间足够的话,直接分配空间来添加纪录,并将插入前最后一条纪录的 next_record 指向当前插入的纪录,将当前插入纪录的 next_record 指向 supremum 纪录。

2)如果 Free Space的 空间不够的话,则首先将之前删除造成的碎片重新整理之后,按照上述步骤插入纪录。

3)如果当前页空间整理碎片之后仍然不足的话,则重新申请一个页,将页初始化之后,按照上述步骤插入纪录

ref MySQL之InnoDB物理存储结构

4、什么是 Buffer Pool? Buffer Pool 是 InnoDB 存储引擎层的缓冲池,不属于 MySQL 的 Server 层,注意跟 8.0 删掉的“查询缓存”功能区分。

内存中以页(page)为单位缓存磁盘数据,减少磁盘IO,提升访问速度。缓冲池大小默认 128M,独立的 MySQL 服务器推荐设置缓冲池大小为总内存的 80%。主要存储数据页、索引页更新缓冲(change buffer)等。

预读机制 Buffer Pool 有一项特技叫预读,存储引擎的接口在被 Server 层调用时,会在响应的同时进行预判,将下次可能用到的数据和索引加载到 Buffer Pool。

预读策略有两种,为线性预读(linear read-ahead)和随机预读(random read-ahead),其中 InnoDB 默认使用线性预读,随机预读已经基本废弃。

线性预读认为如果前面的请求顺序访问当前区(extent)的页,那么接下来的若干请求也会顺序访问下一个区的页,并将下一个区加载到 Buffer Pool。在 5.4 版本以后默认开启,默认值为 56,最大不能超过 64,表示顺序访问 N 个页后触发预读(一个页16K,一个区1M,一个区最多64个页,所以最大值64)。

换页算法 与传统的 LRU 算法不同,因为面临两个问题:

1)预读失效:由于提前把页放入了缓冲池,但最终 MySQL 并没有从页中读取数据。

要优化预读失效,则让预读失败的页停留在缓冲池里的时间尽可能短,预读成功的页停留时间尽可能长。具体将 LRU 链分代实现,即新生代和老年代(old subList),预读的页加入缓冲池时只加入到老年代头部,只有真正被预读成功,则再加入新生代。

2)缓冲池污染:当批量扫描大量数据时,可能导致把缓冲池的所有页都替换出去,导致大量热数据被换出,MySQL 性能急剧下降。

InnoDB 缓冲池加入了一个老生代停留时间窗口的机制,只有满足预读成功并且在老生代停留时间大于该窗口才会被放入新生代头部。

blog.csdn.net/wuhenyouyuy…

5、什么是 Change Buffer? ​如果每次写操作,数据库都直接更新磁盘中的数据,会很占磁盘IO。为了减少磁盘IO,InnoDB在Buffer Pool中开辟了一块内存,用来存储变更记录,为了防止异常宕机丢失缓存,当事务提交时会将变更记录持久化到磁盘(redo log),等待时机更新磁盘的数据文件(刷脏),用来缓存写操作的内存,就是Change Buffer

Change Buffer默认占Buffer Pool的25%,最大设置占用50%。

www.modb.pro/db/112469

3.3 InnoDB 1、InnoDB 架构设计? 以下主要从内存和线程的角度分析 InnoDB 的架构。

内存中的数据区域划分:

ref深入理解InnoDB – 架构篇

2、InnoDB 有哪些线程? 线程的作用:

1)负责刷新内存池中的数据,保证缓冲池的内存缓冲的是最近的数据

2)已修改的数据文件刷新到磁盘文件

3)保证数据库发生异常的情况下InnoDB能恢复到正常状态。

线程分类:

1)Master Thread

负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新,合并插入缓冲(INSERT BUFFER),UNDO页的回收等。

2)IO Thread

负责 AIO 请求的回调处理。

3)Purge Thread

事务提交后,undo log 可能不再需要,由 Purge Thread 负责回收并重新分配的这些已经使用的 undo 页。

4)Page Cleaner Thread

将Master Threader中刷新脏页的工作移至该线程,如上面说的FLUSH LRU LIST Checkpoint以及Async/Sync Flush Checkpoint。

3、什么是 doublewrite? 4、什么是自适应哈希? InnoDB 会监控对表上各索引页的查询执行情况,如发现建立哈希索引可以提升速度,则建立哈希索引,这是过程不需要用户干预。(默认开启)