网络知识 娱乐 用结构化思维吃透MySQL面试题——再也不怕连环问

用结构化思维吃透MySQL面试题——再也不怕连环问

面试经常碰到的两个问题:

1、很多人应该有过面试大厂的经历,不论你从事的是前端、后端还是大数据。即使你认为与你毫无关系,甚至一辈子可能也用不到Mysql, 但是面试官依然会问你关于MYSQL的问题呢?为什么?

2、面试官的各种连环问,让很多面试者瑟瑟发抖,辛辛苦苦死记硬背了几百道题,一着急,忘记的一干二净,叫天不应,叫地不灵~~只有绝望二字。为什么会这样?

目录

一、基础篇(考察你的基本功)

第1问:char 和 varchar 四连问

第2问:int(10) 和 bigint(10) 二连问

第3问:存储引擎七连问

二、进阶篇(考察你的结构化思维)

第1问:索引九连问

第2问:事务-五连问

第3问:锁-三连问

三、高级篇(考察解决问题的能力)

第1问:SQL优化经验七回答

第2问:假设生产系统一天10万条的增量,该怎么优化数据库


老王也面试过很多人,想从一个面试官的视角告诉你:面试官真正想考察你的有三点:

接下来,老王从MySQL知识的结构化视角来告诉你,面试MySQL你需要准备哪些内容。

一、基础篇(考察你的基本功)

WHY:   毫无疑问,基础知识的掌握是必须要会的,比如:create、insert、update、delete、select、drop、alter、show等。这些在企业生产当中会经常使用。

第1问:char 和 varchar 四连问

1.1 说说char和varchar的区别

 答:char是一种固定长度的类型;varchar是一种可变长度的类型。 

1.2 说说二者存储结构的区别

答:一般情况下,varchar比char更节省空间。

varchar类型用于存储可变长度字符串,实际存储的空间=字符串内容+字符串长度(需要使用1或2个额外字节记录字符串的长度)。

而char则是固定长度,一旦定义了字段类型char长度100,即使只存了50个字符,那么也是用了100个字符长度的存储空间(其中7个为空字符/无效字符)。 

1.3 说说二者性能区别

答:根据存储结构的特性,也导致了更新数据时,char的性能比varchar要更好。

char根据定义的字符串长度分配了足够的空间。

varchar在更新长度时,如果比原来长度更长,有可能导致分裂页出现碎片问题。

1.4 说说二者的应用场景

答:二者需要从存储结构层面去思考不同应用场景。

char适用场景:存储具有近似得长度的场景。比如: md5值、身份证、手机号这种长度比较短小得字符串。

varchar的适用场景: 字符串列得最大长度比平均长度大很多的场景;字符串很少被更新,容易产生存储碎片;使用多字节字符集存储字符串。varchar需要额外空间记录字符串长度,更适合经常更新得字符串,更新时不会出现页分裂得情况,避免出现存储碎片,获得更好的IO性能。

第2问:int(10) 和 bigint(10) 二连问

2.1 int(10) 和 bigint(10)能存储的数据大小一样吗

答:不一样。int 能存储四字节有符号整数。bigint 能存储八字节有符号整数。

2.2 int 和 bigint 如果没指定显示宽度。各自默认值是多少

答:如果没指定显示宽度, bigint 默认宽度是 20 ,int默认宽度 11。

第3问:存储引擎七连问

WHY:   存储引擎主要是针对数据库脚本进行解析、分析、优化和执行。那在生产环境中,选择不同的存储引擎,最终取决于实际业务需求以及未来发展情况。所以,在动手开发代码前,必须做到知己知彼,方能决胜千里之外。

3.1 Mysql 的存储引擎有哪些

答:MyISAM和InnoDB。

3.2 MyISAM和InnoDB有什么区别

答:MyISAM 是非事务的存储引擎。适合用于频繁查询的应用。表锁,不会出现死锁,适合小数据,小并发。成熟、稳定、易于管理,快速读取。一些功能不支持(事务等),表级锁。

InnoDB是支持事务的存储引擎。合于插入和更新操作比较多的应用,设计合理的话是行锁(最大区别就在锁的级别上),适合大数据,大并发。支持事务、外键等特性、数据行锁定。空间占用大,不支持全文索引。

3.3 MyISAM和InnoDB都支持外键吗

答:InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败。

3.4 分别说说MyISAM和InnoDB二者在索引方面的差异性

答:InnoDB是聚集索引。使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。

MyISAM是非聚集索引。也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。

总结:InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。

3.5 MyISAM和InnoDB二必须有唯一索引吗

答:InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键),而Myisam可以没有。

3.6 说说MyISAM和InnoDB二者在存储表具体行数的区别

答:InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快(注意不能加有任何WHERE条件)。

3.7 那么为什么InnoDB没有表行数这个变量呢

答: 因为InnoDB的事务特性,在同一时刻表中的行数对于不同的事务而言是不一样的,因此count统计会计算对于当前事务而言可以统计到的行数,而不是将总行数储存起来方便快速查询。InnoDB会尝试遍历一个尽可能小的索引除非优化器提示使用别的索引。如果二级索引不存在,InnoDB还会尝试去遍历其他聚簇索引。

如果索引并没有完全处于InnoDB维护的缓冲区(Buffer Pool)中,count操作会比较费时。可以建立一个记录总行数的表并让你的程序在INSERT/DELETE时更新对应的数据。和上面提到的问题一样,如果此时存在多个事务的话这种方案也不太好用。如果得到大致的行数值已经足够满足需求可以尝试SHOW TABLE STATUS。


二、进阶篇(考察你的结构化思维)

第1问:索引九连问

WHY:   我们在学习Mysql的时候,可能经常听到的一句话,便是:Mysql一定要学会建立索引?索引很重要吗?这是毋庸置疑的,但是他会有多重要呢?任何没有实际业务的实战经验,我们便会对其不以为然。因为,没有切身的体验,我们根本不知道没有索引的情况下,你所面临的问题有多严重。

1.1 什么是索引

 答:索引是一种数据结构,可以帮助我们快速的进行数据的查找. 

1.2 说一说索引的数据结构

答:索引的数据结构和具体存储引擎的实现有关, 在MySQL中使用较多的索引有Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。 

1.3 Hash索引和B+树所有有什么区别或者说优劣

答:首先要知道Hash索引和B+树索引的底层实现原理:

hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.B+树底层实现是多路平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。

hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询.

因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询.而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围.

hash索引不支持使用索引进行排序, 原理同上。

hash索引不支持模糊查询以及多列索引的最左前缀匹配.原理也是因为hash函数的不可预测.AAAA和AAAAB的索引没有相关性.

hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询.

hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差.而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低.

因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度.而不需要使用hash索引.

1.4 B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据,什么是聚簇索引

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

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

1.5 非聚簇索引一定会回表查询吗

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

举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age < 20的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询.

1.6 在建立索引的时候,都有哪些需要考虑的因素呢

答:建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合.如果需要建立联合索引的话,还需要考虑联合索引中的顺序.此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力.这些都和实际的表结构以及查询方式有关. 

1.7 联合索引是什么?为什么需要注意联合索引中的顺序

答:MySQL可以使用多个字段同时建立一个索引,叫做联合索引.在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。

具体原因为: MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。

当进行查询时,此时索引仅仅按照name严格有序,因此必须首先使用name字段进行等值查询,之后对于匹配到的列而言,其按照age字段严格有序,此时可以使用age字段用做索引查找,,,以此类推.因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面.此外可以根据特例的查询或者表结构进行单独的调整。

1.8 创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因

答:MySQL提供了explain命令来查看语句的执行计划,MySQL在执行某个语句之前,会将该语句过一遍查询优化器,之后会拿到对语句的分析,也就是执行计划,其中包含了许多信息. 可以通过其中和索引有关的信息来分析是否命中了索引,例如possilbe_key,key,key_len等字段,分别说明了此语句可能会使用的索引,实际使用的索引以及使用的索引长度。

1.9 那么在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢

答:使用不等于查询;列参与了数学运算或者函数;在字符串like时左边是通配符.类似于'%a';
当mysql分析全表扫描比使用索引快的时候不使用索引;当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引。

第2问:事务-五连问

在关系型数据库中,事务的重要性不言而喻,只要对数据库稍有了解的人都知道事务具有 ACID 四个基本属性。而且在实际企业应用开发过程当中,会真实碰到事物的各种问题。由于事务使用不当,很可能会导致公司经济损失。

2.1 什么是事务

答:事物是独立的工作单元,在这个独立工作单元中所有操作要么全部成功,要么全部失败。也就是说如果有任何一条语句因为崩溃或者其它原因导致执行失败,那么未执行的语句都不会再执行,已经执行的语句会进行回滚操作,这个过程被称之为事物。比如经典的银行存钱取钱问题。

2.2 说说MySQL事务四大特性

答:一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。

一致性(Consistency):指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。

隔离性(Isolation):多个事务并发访问时,事务之间是相互隔离的,即一个事务不影响其它事务运行效果。简言之,就是事务之间是井水不犯河水的。

持久性(Durability):表示事务完成以后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。

2.3 事物并发会出现什么问题

答:总共有3种情况。

脏读: 读取了另一个事物没有提交的数据。

不可重复读: 事物读取同一个数据,返回结果先后不一致问题。

幻读:事物按照范围查询,两次返回结果不同。

2.4 说说脏读、不可重复读和幻读的区别

答:脏读读取的是另一个事物没有提交的数据,而不可重复读读取的是另一个事物已经提交的数据。幻读和不可重复读都是读取了另一条已经提交的事务(这点与脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

2. 5 针对脏读、不可重复读和幻读这三个问题,有哪些隔离级别呢

答:在 MySQL中定义了四种隔离级别。

        未提交读 (Read Uncommitted):两个事物同时运行,有一个事物修改了数据,但未提交,另一个事物是可以读取到没有提交的数据。这种情况被称之为脏读。
        提交读(Read committed):一个事物在未提交之前,所做的任何操作其它事物不可见。这种隔离级别也被称之为不可重复读。因为会存在俩次同样的查询,返回的数据可能会得到不一样的结果。
        可重复读(Repeatable Read):这种隔离级别解决了脏读问题,但是还是存在幻读问题,这种隔离界别在 MySQL 的 innodb 引擎中是默认级别。MySQL 在解决幻读问题使用间隙锁来解决幻读问题。
        可串行化 (Serializable):这种级别是最高的,强制事物进行串行执行,解决了可重复读的幻读问题。

对于隔离级别,级别越高并发就越低,而级别越低会引发脏读、不可重复读、幻读的问题。因此在 MySQL 中使用可重复读(Repeatable Read)作为默认级别。

第3问:锁-三连问

WHY:   锁在 MySQL 中是是非常重要的一部分,锁对 MySQL 数据访问并发有着举足轻重的作用。

3.1 MySQL锁有哪些分类

答:行锁、表锁、页锁。

3.1 三种锁有哪些分类

答:行锁:Innodb 存储引擎;表锁:Myisam、MEMORY 存储引擎;页锁:BDB 存储引擎。

3.3  说说这三类锁优缺点

答:从并发性对效率的影响角度来说。

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最 低,并发度也最高。

页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表 锁和行锁之间,并发度一般。

三、高级篇(考察解决问题的能力)

WHY:  如果我们仅知道理论,但是无法灵活使用这些知识,在实际生产当时也是毫无意义的,所以这个也是面试官会问的最重要的一个问题:实际解决问题的能力。

第1问:SQL优化经验七回答

1.1 如何选择正确的存储引擎

答:以 MySQL为例,包括有两个存储引擎 MyISAM 和 InnoDB,每个引擎都有利有弊。
MyISAM 适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。

InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。但是它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。

1.2 有哪些优化字段的数据类型技巧

答:记住一个原则,越小的列会越快。如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用 INT 来做主键,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间,使用 DATE 要比 DATETIME 好得多。当然,你也需要留够足够的扩展空间。

1.3 为搜索字段添加索引

答:索引并不一定就是给主键或是唯一的字段。如果在你的表中,有某个字段你总要会经常用来做搜索,那么最好是为其建立索引,除非你要搜索的字段是大的文本字段,那应该建立全文索引。

1.4 避免使用Select *

答:避免使用Select *从数据库里读出越多的数据,那么查询就会变得越慢。并且,如果你的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。即使你要查询数据表的所有字段,也尽量不要用*通配符,善用内置提供的字段排除定义也许能给带来更多的便利。

1.5 使用 ENUM 而不是 VARCHAR

答:ENUM 类型是非常快和紧凑的。在实际上,其保存的是 TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。例如,性别、民族、部门和状态之类的这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是 VARCHAR。

1.6 尽可能的使用 NOT NULL

答:除非你有一个很特别的原因去使用 NULL 值,你应该总是让你的字段保持 NOT NULL。 NULL其实需要额外的空间,并且,在你进行比较的时候,你的程序会更复杂。 当然,这里并不是说你就不能使用NULL了,现实情况是很复杂的,依然会有些情况下,你需要使用NULL值。

1.7 固定长度的表会更快

答:如果表中的所有字段都是“固定长度”的,整个表会被认为是 “static” 或 “fixed-length”。 例如,表中没有如下类型的字段: VARCHAR,TEXT,BLOB。只要你包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另一种方法来处理。

固定长度的表会提高性能,因为MySQL搜寻得会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。

并且,固定长度的表也更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论你用不用,他都是要分配那么多的空间。 

第2问:假设生产系统一天10万条的增量,该怎么优化数据库

设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。
选择合适的表字段数据类型和存储引擎,适当的添加索引。
Mysql库主从读写分离。添加缓存机制,比如memcached,apc等。