【MySql面试篇】我以为我对MySql很了解,直到我面试了字节跳动

    技术2022-07-10  99

    【干货预警,关注收藏才有好姿势阅读哦~】

    作者寄语

        悟空来到字节跳动,在静待,此时一位长发飘飘的面试官走来。悟空心里想:哎啊,今天遇到个美女面试官,可要好好表现了!

    面试官:悟空,我看你简历上写了熟练掌握MySql和MySql的调优是吧? 悟空:是的,面试官。 面试官:说一下你在项目中如何使用MySql的? 悟空:增删改查,没了。 面试官:如何调优的你是? 悟空:加索引,没了。 面试官:我们公司的门你知道在哪里吧,用我送你吗?

        哈哈,上面场景是开玩笑的当然,言归正传,大家如果真遇到这种问题如何回答?数据库是如何调优的?或者说大家可能都清楚加索引是数据库调优方法之一,但是关于索引又知道多少呢?下面我们一起进入主题

    面试开始

    面试官:说一下in关键字和exists关键字吧

    我:好的,in关键字尽量用在内表小的地方,exists关键字用在外表小的地方,如果用not in ,则是内外表都全表扫描,无索引,效率低,可考虑使用not exists,也可以考虑用连接来优化。(内心OS,这能难得到我?) 接下来分析:in关键字是把外表和内表做hash连接,先查询内表,再把内表的结果和外表匹配,对外表使用索引(外表效率高,可以用大表),而内表都需要查询,使用in关键字可以加快效率。exists关键字是对外表做loop循环,每次循环对内表进行查询(对内表可以使用索引,查询效率高,可以用大表),而外表有多大都需要遍历,使用exists关键字可以加快效率。

        举个例子:select * from A where A.id in (select id from B);对A表使用索引,效率高,建议A为大表。select * from A where exists (select * from B where A.id=B.id);对B使用索引效率高,因为外表A总是要全表,而且要循环,所以B表建议使用大表。

    面试官:说一下COUNT吧

    我:COUNT关键字是用来进行不为NULL的行数统计的,有三种用法分别是:COUNT(列名)、COUNT(常量)和COUNT(*)。在《阿里巴巴Java开发手册》中强制要求不让使用 COUNT(列名)或 COUNT(常量)来替代 COUNT(※)。 区别:列名、 常量 和 ※这三个条件中,常量 是一个固定值,肯定不为NULL。※可以理解为查询整行,所以肯定也不为NULL,那么就只有列名的查询结果有可能是NULL了,所以COUNT(常量) 和 COUNT()表示的是直接查询符合条件的数据库表的行数。而COUNT(列名)表示的是查询符合条件的列的值不为NULL的行数。COUNT(※)是SQL92定义的标准统计行数的语法,因为他是标准语法,所以MySQL数据库对他进行过很多优化。

        SQL92,是数据库的一个ANSI/ISO标准。它定义了一种语言(SQL)以及数据库的行为(事务、隔离级别等)。

    面试官:你能说一下这些优化吗?

    我:(!@#¥%¥……我还真是自己给自己找麻烦)面带微笑,当然可以的。这里的介绍要区分不同的执行引擎,MySQL中比较常用的执行引擎就是InnoDB和MyISAM。我们知道MyISAM不支持事务,表级锁,而InnoDB支持事务,支持行级锁。     因为MyISAM是表级锁,所以在一张表上的操作是串行执行的。所以,MyISAM做了一个简单的优化,那就是它可以把表的总行数单独记录下来,如果从一张表中使用COUNT(*)进行查询的时候,可以直接返回这个记录下来的数值就可以了,当然,前提是不能有where条件。为什么MyISAM可以这样做呢?因为它是表级锁,不会有并发的数据库修改记录的行为,查询的行数是准确的。     对于InnoDB则不适合这种缓存操作了,它是支持事务和行级锁,表的行数可能会被并发修改,那么缓存记录下来的行数就不准确了。那么InnoDB则不可避免的要进行扫表了,于是从MySQL 8.0.13开始,SELECT COUNT(※) 在扫表的过程中做了一些优化,前提是查询语句中不包含WHERE或GROUP BY等条件。我们的目的只是为了统计总行数,并不关心查到的具体值,所以可以选择一个成本较低的索引进行,节省时间。而且InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值。所以,相比之下,非聚簇索引要比聚簇索引小很多,所以MySQL会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。 面试官:Mysql中 字段为什么要求定义为not null?

    我:null值会占用更多的字节,且会在程序中造成很多与预期不符的情况。

    面试官:此时面试官点了点头,继续微笑着说到,你刚刚提到过引擎,除了你说的两点区别,还有别的吗?

    我:接下来我来分点介绍

    上面刚刚介绍的,MyISAM不支持事务,表级锁,而InnoDB支持事务,支持行级锁。InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;InnoDB 是聚集索引,MyISAM 是非聚集索引。

    面试官:那你能说说什么是索引吗?

    我:索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据。在用法上索引大概分为以下几类:

    普通索引normal:仅仅加索查询;唯一索引unique:加索查询,列值唯一,可以有NULL。主键索引primary:加速查询,列值唯一,不可以为NULL,表中只有一个。组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并。全文索引full text:对文本的内容进行分词,进行搜索。

    面试官:那索引具体底层是什么样子的?(言外之意就是数据结构)

    我:索引的数据结构和具体存储引擎的实现有关,MySQL主要有两种结构:Hash索引和B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树。

    面试官:为什么采用B+树呢,和Hash索引比较如何?

    我:好的,接下来我来详细介绍下两者:

    Hash索引:Hash索引底层是哈希表(还不了解的点击这里直通车),哈希表是以一种Key-Value存储数据的结构,所以多个数据在存储关系上是没有顺序的,也就没办法进行范围查询,必须要全表扫描。哈希索引只适用于等值查询的场景。B+树索引:B+树更适合外部存储。由于内结点不存放真正的数据(只是存放其子树的最大或最小的关键字,作为索引),一个结点可以存储更多的关键字,每个结点能索引的范围更大更精确,也意味着B+树单次磁盘IO的信息量大于B树,I/O的次数相对减少。B+树也是更容易进行区间访问的,因为叶子节点维护了一个链表。

         如果不太了解数据结构的,可能理解这个问题比较吃力些,而且面试时也很容易由这里过渡到数据结构那块考察你,点击这里直通车(等待开通)。

    面试官:关于B+树的叶子节点,可以存放哪些东西?

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

    面试官:聚簇索引和非聚簇索引特点?

    我:聚簇索引和非聚簇索引,聚簇索引更快,因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询。当查询使用局促索引的时候,在对应的叶子结点上可以获取到整行的数据,不再需要回表查询,而非聚簇索引则需要回表查询。

    面试官:那非聚簇索引一定要回表搜索吗?

    我:不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询,即覆盖索引。

        覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。 当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。 如,表covering_index_sample中有一个普通索引 idx_key1_key2(key1,key2)。当我们通过SQL语句:select key2 from covering_index_sample where key1 = ‘keytest’;的时候,就可以通过覆盖索引查询,无需回表。

    面试官:在建立索引的时候,你一般考虑哪些因素?

    我:建立索引的时候一般要考虑字段的使用频率,经常作为查询条件的字段比较适合索引。当然也不能过度建立索引,因为索引也是占据内存的,而且修改表会导致索引更新,所以在建立索引的时候也要考虑表结构。     在一些场合使用联合索引是比较好的效果,比如我们可以建立一个(学校-班级-ID)的联合索引,这样会比建立三个索引效果好,但是如果我们只使用其中一个索引ID不会走联合索引,会导致全表扫描,所以要分业务情况。使用联合索引时需要注意顺序,尽量把区分度大的索引放在前面。

    面试官:为什么建立联合索引?(区分度大的索引放在前面)

    我:在联合索引使用中,如果想要命中索引需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。联合索引中有个最左匹配原则,当我们建立联合索引(A,B,C),实际上已经建立了(A)、(A,B)、(A,B,C)三个联合索引。     比如我们上面说的(学校-班级-ID)联合索引,b+树是按照从左到右的顺序来建立搜索树的,b+树优先比较学校来确定下一步的搜索方向,如果还未达到条件则继续执行搜索。如果只有学校字段,班级字段缺失,只能找到这个学校的所有字段,然后再匹配相应ID的学生,此种情况无法用到联合索引。

    面试官:如何判断创建的索引是否使用到,或者说如何分析Sql语句?

    我:我一般都是通过explain命令来查看语句的执行计划,使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。

    面试官:简单说一下Explain的字段?

    我:(下面是相关字段,没必要全说,说熟悉的即可)

    id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序select_type:查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询。table:指的就是当前执行的表。type:type所显示的是查询使用了哪种类型。查询性能从最好到最差依次是:system > const > eq_ref > ref > range > index > all,一般来说,得保证查询至少达到range级别,最好能达到ref。possible_keys 和 key:possible_keys 显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。key则表示实际使用的索引,如果为NULL,则没有使用索引。(可能原因包括没有建立索引或索引失效)。key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好。ref:显示索引的那一列被使用了,如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值。rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,也就是说,用的越少越好Extra:包含不适合在其他列中显式但十分重要的额外信息。

    面试官:什么情况下针对列创建了索引,查询的时候却没有使用?

    我:比如索引列参与了数学运算或者函数。字符串like时的左边是通配符,类似于"

    转载请注明原文地址:https://ipadbbs.8miu.com/read-1122.html
    最新回复(0)