大家都应该知道今年春招特别的猛,因为裁员够猛,一线互联网公司都在忙着换人抢人,我们公司也不例外。上周公司招了一位有10年工作经验的32岁技术大佬,面试过程相当激烈。。。

上周公司招了一位有10年工作经验的32岁技术大佬,由于CTO大佬不在, 心情忐忑的我被抓了壮丁去面这位大佬。本以为结果毫无悬念,但过程让我很是吃鲸!

在业务的缘故下,我主要问了MySQL相关的内容,先把所有问题抛出来,你们会怎么答,然后再猜一下大佬的回答。

面试题

1、谈一谈MySQL有多少中索引?各自的特点和不同?
2、MySQL有多少种锁? 什么力度?
3、知道间隙锁吗? 怎么实现的?  有什么作用?
4、谈谈平时做的关于MySQL索引优化的事情?
5、讲下explain。
6、分析一下MySQL的语句执行过程。
7、讲一下MySQL的三种日志以及各自的作用。
8、知道binlog吧?讲讲数据的格式。
9、有几种隔离模式,可重复读下,新增和修改的不同?
10、select * from  table , 具体是怎么知道查询哪些字段的,怎么实现的?

在大佬海阔天空的一番自我介绍后,我问了理论层的问题(想着来个简单的,能够让面试的氛围显得更为融洽,顺便掩盖自己是菜鸡的本质)——

理论层

我 :

在我们的业务中经常用到MySQL,能谈一谈MySQL有多少种索引?各自的特点和不同?

大佬:

有四种索引,分别是:全文索引,HASH索引,BTREE,RTREE。

全文索引 :全文索引目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。

Hash 索引:Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以Hash索引的查询效率要远高于B-Tree索引。

B-Tree索引:B-Tree索引就是一种将索引值按一定的算法,存入一个树形的数据结构中。

R-Tree:R-Tree在MySQL很少使用,仅支持geometry数据类型。各自的特点不同我总结了一下的特点:

(1)对于B-Tree这种MySQL默认的索引方式,具有普遍的适用性。

(2)由于FULLTEXT对中文支持不是很好,在没有插件的情况下,最好不要使用。

(3)对于一些搜索引擎级别的应用来说,FULLTEXT同样不是一个好的处理方法,MySQL的全文索引建立的文件还是比较大的,而且效率不是很高,即便是使用了中文分词插件,对中文分词支持也只是一般。

(4)因为hash表在处理较小数据量时具有的优势,所以hash索引很适合做缓存。

我听完感觉大佬不亏是大佬,于是我就又问了另外了一个问题 ——

我:

那么MySQL有多少种锁?什么粒度?

大佬:

按照对数据操作的锁粒度来分:行级锁、表级锁、页级锁、间隙锁

行级锁:行级锁是MySQL中锁定粒度最细的一种锁。表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突,其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁。

表级锁:表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。

页级锁:页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此,采取了折中的页级锁,一次锁定相邻的一组记录。BDB 支持页级锁。

间隙锁:锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。该锁只会在隔离级别是RR或者以上的级别内存在。间隙锁的目的是为了让其他事务无法在间隙中新增数据。

我看大佬对MySQL 的理论很熟练,就想了解下大佬的实际操作怎么样,精彩的部分来了,这也是很多小伙伴面试中卡壳的点

实战层

我:

简单分析一下MySQL的语句执行过程。

大佬:

客户端发送一条查询给服务器;

服务器先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果。否则进入下一阶段;

服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划;

MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;

将结果返回给客户端。

我:

那么,其中MySQL的日志又是怎样的?

大佬:

MySQL中有六种日志文件,分别是:

重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog)、错误日志(errorlog)、 慢查询日志(slow query log)、一般查询日志(general log) 、中继日志(relay log)。

我:

既然提到了 那么提问一下,讲讲binlog数据的格式。

大佬:

binlog是二进制日志,它记录了数据库上的所有改变,并以二进制的形式保存在磁盘中;

它可以用来查看数据库的变更历史、数据库增量备份和恢复、MySQL的复制(主从数据库的复制)。

binlog有三种格式:Statement、Row以及Mixed。

分别是

–基于SQL语句的复制(Statement-based replication,SBR) 。

– 基于行的复制(Row-based replication,RBR)。 

– 混合模式复制(mixed-based replication,MBR)。

问到这里于是我想顺藤摸瓜想要了解下大佬的水平究竟如何我和大佬的区别在哪里 ?(绝不是为了自己能够更加的优秀)

于是我问到了这里——

我:

能大概的说说binlog的Statement、Row以及Mixed这三种模式么,以及在业务中的有什么优点和缺点?

大佬: 

从实战角度来分析三者的优点和缺点。

Statement:

每一条会修改数据的SQL都会记录到master的binlog中,slave在复制的时候SQL进程会解析成和原来master端执行多相同的SQL再执行。

优点:

相对于Row模式来说 不需要记录每一行数据的变化减少了binlog日志量,节省了I/O以及存储资源,提高性能。因为他只需要记录在master上所执行的语句的细节以及执行语句的上下文信息。

缺点:

在Statement模式下,由于他是记录的执行语句,所以,为了让这些语句在slave端也能正确执行,那么他还必须记录每条语句在执行的时候的一些相关信息,也就是上下文信息,以保证所有语句在slave端被执行的时候能够得到和在master端执行时候相同的结果。

另外就是,由于MySQL现在发展比较快,很多的新功能不断的加入,使MySQL的复制遇到了不小的挑战,自然复制的时候涉及到越复杂的内容,bug也就越容易出现。在Statement中,目前已经发现不少情况会造成MySQL的复制出现问题,主要是修改数据的时候使用了某些特定的函数或者功能的时候会出现,比如:sleep()函数在有些版本中就不能被正确复制,在存储过程中使用了last_insert_id()函数,可能会使slave和master上得到不一致的id等等。由于Row是基于每一行来记录的变化,所以不会出现,类似的问题

Row:

Row模式下日志中会记录成每一行数据被修改的形式,然后在slave端再对相同的数据进行修改,只记录要修改的数据,只有value,不会有SQL多表关联的情况。

优点:

在Row模式下,bin-log中可以不记录执行的SQL语句的上下文相关的信息,仅仅只需要记录那一条记录被修改了,修改成什么样了,所以Row的日志内容会非常清楚的记录下每一行数据修改的细节,非常容易理解。而且不会出现某些特定情况下的存储过程和function,以及trigger的调用和出发无法被正确复制问题。

缺点:

在Row模式下,所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容。

Mixed:

实际上就是前两种模式的结合。在Mixed模式下,MySQL会根据执行的每一条具体的SQL语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。新版本中的Statment还是和以前一样,仅仅记录执行的语句。而新版本的MySQL中对Row模式也被做了优化,并不是所有的修改都会以Row模式来记录,比如遇到表结构变更的时候就会以Statement模式来记录,如果SQL语句确实就是 update或者delete等修改数据的语句,那么还是会记录所有行的变更。

听完大佬的讲述我恍然大悟 (不是因为我是菜鸡哈,实在是因为大佬讲得很好 )

听到这里,我明显的看到了自己和大佬的差距,于是就拿了点简单的问题来问——

我:

select * from  table , 具体是怎么知道查询哪些字段的,怎么实现的?

大佬:

MySQL拿到一条命令,会去解析命令、优化查询,然后去存储引擎执行查找,Select*数据库会解析更多的 对象,字段,权限,属性相关。

首先 数据库需要知道*等于什么,查数据字典,这在分析阶段就会增大开销实际上,ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。

紧接着我就问了一点 常见的问题 ——

我:

您在工作中如果遇到了数据库异常,这时您会怎么做 ?   

大佬:

一般数据库出现异常的原因:

1、数据库请求过多,导致资源消耗过度,导致数据库操作缓慢;

2、数据库查询太慢导致用户不断点击,使数据操作更慢,用户更是不断点击操作,最终导致数据库操作更慢

3、数据库操作导致事物提交时间太长

4、数据库误操作导致数据库出现异常

那么针对这些不同的原因进行优化:

1、查询数据库操作进程查看数据库操作情况;用show processlist;显示操作信息,发现过多查询使用kill 进程杀死进程先解决数据库卡死的情况

2、重新启动项目释放数据库连接

3、优化SQl语句,提高数据库操作性能

4、分库分表进行负载均衡,读写分离

5、数据库参数进行优化提高数据库操作性能

到这里的时候,我已经大概了解到了大佬的水平和层次 ,更是清楚地看到了自己和大佬的差距。

 其实我问的问题并不是很难,也不是太深入,但是细节性的点都很重要的,大佬的回答,几乎可以说是标准的答案了。10年水平不是盖的,对于大多数的技术都有很深入的理解,对比下来,感觉自己还是欠缺很多,希望能在这里跟大家一起追求更高层次的技术。  

那么最后,面对上面的这些问题,你现在知道怎么回答了么?

本篇文章里的面试题都有收录在我自己整理的面经里,大家感兴趣的可以扫下方名片加我,分享给大家