Java程序员如何快速突击面试?这份阿里面试官的手稿应该能帮到你。

前言

首先说一下为什么要写这篇内容,今年的金三银四招聘旺季马上过去,我也不知道大家有没有拿到心仪的offer。反正我在我的私信列表里看到太多了跟我吐槽面试难,各种不顺利的。由于自己工作的问题也没时间一一回复,最近趁着有时间把自己觉得面试重要的一些点和题目给大家整理一下吧,希望能对各位有点帮助。内容分为多个板块,大家可以挑选自己感兴趣的不分看。

Mysql核心面试题

能说下myisam 和 innodb的区别吗?

  • myisam引擎是5.1版本之前的默认引擎,⽀持全⽂检索、压缩、空间函数等,但是不⽀持事务和⾏级锁,所以⼀般⽤于有⼤量查询少量插⼊的场景来使⽤,⽽且myisam不⽀持外键,并且索引和数据是分开存储的。
  • innodb是基于聚簇索引建⽴的,和myisam相反它⽀持事务、外键,并且通过MVCC来⽀持⾼并发,索引和数据存储在⼀起。

说下mysql的索引有哪些吧,聚簇和⾮聚簇索引⼜是什么?

引按照数据结构来说主要包含B+树和Hash索引。
假设我们有张表,结构如下:

create table user(
 id int(11) not null,
 age int(11) not null,
 primary key(id),
 key(age)
);

B+树是左⼩右⼤的顺序存储结构,节点只包含id索引列,⽽叶⼦节点包含索引列和数据,这种数据和索引在⼀起存储的索引⽅式叫做聚簇索引,⼀张表只能有⼀个聚簇索引。假设没有定义主键,InnoDB会选择⼀个唯⼀的⾮空索引代替,如果没有的话则会隐式定义⼀个主键作为聚簇索引。



这是主键聚簇索引存储的结构,那么⾮聚簇索引的结构是什么样⼦呢?⾮聚簇索引(⼆级索引)保存的是主键id值,这⼀点和myisam保存的是数据地址是不同的。



最终,我们⼀张图看看InnoDB和Myisam聚簇和⾮聚簇索引的区别

具体为大家整理了十几个板块,篇幅原因就不全做展示了,有需要的可以直接去文末获取完整版

那你知道什么是覆盖索引和回表吗?

覆盖索引指的是在⼀次查询中,如果⼀个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,⽽不再需要回表查询。
⽽要确定⼀个查询是否是覆盖索引,我们只需要explain sql语句看Extra的结果是否是“Using index”即可。
以上⾯的user表来举例,我们再增加⼀个name字段,然后做⼀些查询试试。

explain select * from user where age=1; //查询的name⽆法从索引数据获取
explain select id,age from user where age=1; //可以直接从索引获取

锁的类型有哪些呢

  • mysql锁分为共享锁和排他锁,也叫做读锁和写锁。
  • 读锁是共享的,可以通过lock in share mode实现,这时候只能读不能写。
  • 写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁和⾏锁两种。
  • 表锁会锁定整张表并且阻塞其他⽤户对该表的所有读写操作,⽐如alter修改表结构的时候会锁表。
  • ⾏锁⼜可以分为乐观锁和悲观锁,悲观锁可以通过for update实现,乐观锁则通过版本号实现。

你能说下事务的基本特性和隔离级别吗?

事务基本特性ACID分别是:

原⼦性指的是⼀个事务中的操作要么全部成功,要么全部失败。
⼀致性指的是数据库总是从⼀个⼀致性的状态转换到另外⼀个⼀致性的状态。⽐如A转账给B100块钱,假设中间sql执⾏过程中系统崩溃A也不会损失100块,因为事务没有提交,修改也就不会保存到数据库。
隔离性指的是⼀个事务的修改在最终提交前,对其他事务是不可⻅的。
持久性指的是⼀旦事务提交,所做的修改就会永久保存到数据库中。

⽽隔离性有4个隔离级别,分别是:

read uncommit 读未提交,可能会读到其他事务未提交的数据,也叫做脏读。
⽤户本来应该读取到id=1的⽤户age应该是10,结果读取到了其他事务还没有提交的事务,结果读取结果age=20,这就是脏读。



read commit 读已提交,两次读取结果不⼀致,叫做不可重复读。
不可重复读解决了脏读的问题,他只会读取已经提交的事务。
⽤户开启事务读取id=1⽤户,查询到age=10,再次读取发现结果=20,在同⼀个事务⾥同⼀个查询读取到不同的结果叫做不可重复读。



repeatable read 可重复复读,这是mysql的默认级别,就是每次读取结果都⼀样,但是有可能产⽣幻读。
serializable 串⾏,⼀般是不会使⽤的,他会给每⼀⾏读取的数据加锁,会导致⼤量超时和锁竞争的问题。

那ACID靠什么保证的呢?

A原⼦性由undo log⽇志保证,它记录了需要回滚的⽇志信息,事务回滚时撤销已经执⾏成功的sqlC⼀致性⼀般由代码层⾯来保证
I隔离性由MVCC来保证
D持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复

Redi核心面试题

说说Redis基本数据类型有哪些吧

1. 字符串:

redis没有直接使⽤C语⾔传统的字符串表示,⽽是⾃⼰实现的叫做简单动态字符串SDS的抽象类型。C语⾔的字符串不记录⾃身的⻓度信息,⽽SDS则保存了⻓度信息,这样将获取字符串⻓度的时间由O(N)降低到了O(1),同时可以避免缓冲区溢出和减少修改字符串⻓度时所需的内存重分配次数。

2. 链表linkedlist:

redis链表是⼀个双向⽆环链表结构,很多发布订阅、慢查询、监视器功能都是使⽤到了链表来实现,每个链表的节点由⼀个listNode结构来表示,每个节点都有指向前置节点和后置节点的指针,同时表头节点的前置和后置节点都指向NULL。

3. 字典hashtable:

⽤于保存键值对的抽象数据结构。redis使⽤hash表作为底层实现,每个字典带有两个hash表,供平时使⽤和rehash时使⽤,hash表使⽤链地址法来解决键冲突,被分配到同⼀个索引位置的多个键值对会形成⼀个单向链表,在对hash表进⾏扩容或者缩容的时候,为了服务的可⽤性,rehash的过程不是⼀次性完成的,⽽是渐进式的。

4. 跳跃表skiplist:

跳跃表是有序集合的底层实现之⼀,redis中在实现有序集合键和集群节点的内部结构中都是⽤到了跳跃表。redis跳跃表由zskiplist和zskiplistNode组成,zskiplist⽤于保存跳跃表信息(表头、表尾节点、⻓度等),zskiplistNode⽤于表示表跳跃节点,每个跳跃表的层⾼都是1-32的随机数,在同⼀个跳跃表中,多个节点可以包含相同的分值,但是每个节点的成员对象必须是唯⼀的,节点按照分值⼤⼩排序,如果分值相同,则按照成员对象的⼤⼩排序。

5. 整数集合intset:

⽤于保存整数值的集合抽象数据结构,不会出现重复元素,底层实现为数组。

6. 压缩列表ziplist:压缩列表是为节约内存⽽开发的顺序性数据结构,他可以包含多个节点,每个节点可以保存⼀个字节数组或者整数值。

Redis为什么快呢?

redis的速度⾮常的快,单机的redis就可以⽀撑每秒10⼏万的并发,相对于mysql来说,性能是mysql的⼏⼗倍。速度快的原因主要有⼏点:

  1. 完全基于内存操作
  2. C语⾔实现,优化过的数据结构,基于⼏种基础的数据结构,redis做了⼤量的优化,性能极⾼
  3. 使⽤单线程,⽆上下⽂的切换成本
  4. 基于⾮阻塞的IO多路复⽤机制

那为什么Redis6.0之后⼜改⽤多线程呢?

redis使⽤多线程并⾮是完全摒弃单线程,redis还是使⽤单线程模型来处理客户端的请求,只是使⽤多线程来处理数据的读写和协议解析,执⾏命令还是使⽤单线程。
这样做的⽬的是因为redis的性能瓶颈在于⽹络IO⽽⾮CPU,使⽤多线程能提升IO读写的效率,从⽽整体提⾼redis的性能。

什么是缓存击穿、缓存穿透、缓存雪崩?

缓存击穿

缓存击穿的概念就是单个key并发访问过⾼,过期时导致所有请求直接打到db上,这个和热key的问题⽐较类似,只是说的点在于过期导致请求全部打到DB上⽽已。
解决⽅案:

  1. 加锁更新,⽐如请求查询A,发现缓存中没有,对A这个key加锁,同时去数据库查询数据,写⼊缓存,再返回给⽤户,这样后⾯的请求就可以从缓存中拿到数据了。
  2. 将过期时间组合写在value中,通过异步的⽅式不断的刷新过期时间,防⽌此类现象。


缓存穿透

缓存穿透是指查询不存在缓存中的数据,每次请求都会打到DB,就像缓存不存在⼀样。



针对这个问题,加⼀层布隆过滤器。布隆过滤器的原理是在你存⼊数据的时候,会通过散列函数将它映射为⼀个位数组中的K个点,同时把他们置为1。
这样当⽤户再次来查询A,⽽A在布隆过滤器值为0,直接返回,就不会产⽣击穿请求打到DB了。显然,使⽤布隆过滤器之后会有⼀个问题就是误判,因为它本身是⼀个数组,可能会有多个值落到同⼀个位置,那么理论上来说只要我们的数组⻓度够⻓,误判的概率就会越低,这种问题就根据实际情况来就好了。


缓存雪崩

当某⼀时刻发⽣⼤规模的缓存失效的情况,⽐如你的缓存服务宕机了,会有⼤量的请求进来直接打到DB上,这样可能导致整个系统的崩溃,称为雪崩。雪崩和击穿、热key的问题不太⼀样的是,他是指⼤规模的缓存都过期失效了。



针对雪崩⼏个解决⽅案:

  1. 针对不同key设置不同的过期时间,避免同时过期
  2. 限流,如果redis宕机,可以限流,避免同时刻⼤量请求打崩DB
  3. ⼆级缓存,同热key的⽅案。

Spring核心面试题

说说Spring ⾥⽤到了哪些设计模式?

  • 单例模式 :Spring 中的 Bean 默认情况下都是单例的。⽆需多说。
  • ⼯⼚模式 :⼯⼚模式主要是通过 BeanFactory 和 ApplicationContext 来⽣产 Bean 对象。
  • 代理模式 :最常⻅的 AOP 的实现⽅式就是通过代理来实现,Spring主要是使⽤ JDK 动态代理和 CGLIB代理。
  • 模板⽅法模式 :主要是⼀些对数据库操作的类⽤到,⽐如 JdbcTemplate、JpaTemplate,因为查询数据库的建⽴连接、执⾏查询、关闭连接⼏个过程,⾮常适⽤于模板⽅法。

谈谈你对IOC 和 AOP 的理解?他们的实现原理是什么?

IOC 叫做控制反转,指的是通过Spring来管理对象的创建、配置和⽣命周期,这样相当于把控制权交给了Spring,不需要⼈⼯来管理对象之间复杂的依赖关系,这样做的好处就是解耦。在Spring⾥⾯,主要提供了 BeanFactory 和 ApplicationContext 两种 IOC 容器,通过他们来实现对 Bean 的管理。

AOP 叫做⾯向切⾯编程,他是⼀个编程范式,⽬的就是提⾼代码的模块性。Srping AOP 基于动态代理的⽅式实现,如果是实现了接⼝的话就会使⽤ JDK 动态代理,反之则使⽤ CGLIB 代理,Spring中 AOP的应⽤主要体现在 事务、⽇志、异常处理等⽅⾯,通过在代码的前后做⼀些增强处理,可以实现对业务逻辑的隔离,提⾼代码的模块化能⼒,同时也是解耦。Spring主要提供了 Aspect 切⾯、JoinPoint 连接点、PointCut 切⼊点、Advice 增强等实现⽅式。

JDK 动态代理和 CGLIB 代理有什么区别?

JDK 动态代理主要是针对类实现了某个接⼝,AOP 则会使⽤ JDK 动态代理。他基于反射的机制实现,⽣成⼀个实现同样接⼝的⼀个代理类,然后通过重写⽅法的⽅式,实现对代码的增强。

⽽如果某个类没有实现接⼝,AOP 则会使⽤ CGLIB 代理。他的底层原理是基于 asm 第三⽅框架,通过修改字节码⽣成成成⼀个⼦类,然后重写⽗类的⽅法,实现对代码的增强。

FactoryBean 和 BeanFactory有什么区别?

BeanFactory 是 Bean 的⼯⼚, ApplicationContext 的⽗类,IOC 容器的核⼼,负责⽣产和管理 Bean对象。

FactoryBean 是 Bean,可以通过实现 FactoryBean 接⼝定制实例化 Bean 的逻辑,通过代理⼀个Bean对
象,对⽅法前后做⼀些操作。

Spring事务传播机制有哪些?

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建⼀个新事务,如果当前存在事务,就加⼊该事务,这也是通常我们的默认选择。
  • PROPAGATION_REQUIRES_NEW:创建新事务,⽆论当前存不存在事务,都创建新事务。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执⾏。如果当前没有事务,则按REQUIRED属性执⾏。
  • PROPAGATION_NOT_SUPPORTED:以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常。
  • PROPAGATION_MANDATORY:⽀持当前事务,如果当前存在事务,就加⼊该事务,如果当前不存在事务,就抛出异常。
  • PROPAGATION_SUPPORTS:⽀持当前事务,如果当前存在事务,就加⼊该事务,如果当前不存在事务,就以⾮事务执⾏。‘

最后

本文就先写到这里,面试中常问的一些题目我都有整理的,有需要完整PDF文档的可以关注公众号前程有光获取

你可能感兴趣的:(Java程序员如何快速突击面试?这份阿里面试官的手稿应该能帮到你。)