阿里云 实习面经 二面|讲解|0227

前面讲解过该同学的一面面经一面面经讲解,现在也分享了他的二面面经。 所以针对他的二面问题,继续给大家做讲解分析,参考回答和学习资料指引,期望对大家有所帮助~

本文也是**《热门面经讲解》专栏** 系列文章之一,大家可以点个关注,我会持续更新

原贴链接 alt alt

感谢这位同学的分享

1. Mysql相关:MyISAM和InnoDB的区别?

解析:: MyISAM虽然单独考察不多,但是与InnoDB的对比还是比较常考的。可以从一下几个方面来对比,面试的时候能回答个5-6个应该就可以了。

参考回答:

MyISAM和InnoDB是MySQL中两种常见的存储引擎,它们在多个方面存在显著的区别。以下是对两者的比较:

  1. 事务支持: InnoDB支持事务,具有提交、回滚和崩溃恢复能力,可以保护用户数据。它使用了一种称为“写前日志”(write-ahead logging,WAL)的技术,先写日志,再写数据,保证了事务的完整性。 MyISAM不支持事务,这意味着在MyISAM中进行的更改无法回滚,且如果发生崩溃,数据可能会损坏。
  2. 锁机制: InnoDB支持行级锁和表级锁,但默认使用行级锁。行级锁可以提高并发性能,因为它只锁定被访问的行,而不是整个表。 MyISAM只支持表级锁,这意味着当一个线程正在对一个表进行写操作时,其他线程无法对该表进行读或写操作。这可能会降低并发性能。
  3. 外键支持: InnoDB支持外键约束,这有助于维护数据的引用完整性和一致性。 MyISAM不支持外键约束。
  4. 数据存储和索引: InnoDB使用聚簇索引来组织数据,表数据和主键索引存储在一起。此外,InnoDB还支持全文索引和哈希索引。 MyISAM将数据和索引分开存储,数据文件(MYD)和索引文件(MYI)是分离的。MyISAM也支持全文索引,但不支持哈希索引。
  5. 数据恢复和备份: InnoDB提供了更好的数据恢复功能,如通过binlog进行点时间恢复(PITR)。此外,InnoDB还支持在线备份功能。 MyISAM的数据恢复相对较弱,通常依赖于物理备份和恢复。
  6. 性能: InnoDB在处理大量读写操作时通常表现更好,特别是在高并发环境下。这得益于其行级锁和事务支持。 MyISAM在处理只读查询时可能更快,特别是当数据量很大且内存充足时。然而,在写操作频繁的场景下,MyISAM的性能可能会下降。
  7. 存储空间: InnoDB的存储空间通常比MyISAM大,因为它需要额外的空间来存储事务日志、行级锁等信息。此外,InnoDB的数据文件大小是动态增长的。 MyISAM的存储空间相对较小,数据文件大小是固定的(可以通过配置修改)。但是,MyISAM不支持数据压缩功能。

学习指引: 系列文章 MySQL引擎篇:半道出家的InnoDB为何能替换官方的MyISAM?

2. Mysql相关:索引的底层数据结构?为什么选择这样的结构?(B+树)

解析:: 考察mysql InnoDB引擎的数据结构B+树,需要回答出来为什么要选用B+树,而不是B树,也不是二叉树。 为什么B+树比他们合适,比他们块。

参考回答:

MySQL 默认的存储引擎 InnoDB 采用的是 B+ 作为索引的数据结构,原因有:

1.B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O次数会更少。(为什么不选二叉树也有类似原因)

2.B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化;

3.B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。

学习指引: 系统学习 小林 coding|为什么 MySQL 采用 B+ 树作为索引?

3. Mysql相关:自己选择维度划分介绍有哪些索引类型?

解析:: 你知道索引有哪些吗?大家肯定都能说出一大堆,什么聚簇索引、主键索引、二级索引、普通索引、唯一索引、hash索引、B+树索引等等。但都全放一起都不是一个维度的。我们还需要做到能从不同维度来分类索引。

参考回答:

我们可以按照四个角度来分类索引。

  1. 按「数据结构」分类:B+tree索引、Hash索引、Full-text索引。
  2. 按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引)。
  3. 按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引。
  4. 按「字段个数」分类:单列索引、联合索引。

学习指引: MySQL的索引分类

4. Mysql相关:最左前缀匹配原则是什么?自己举例进行说明;索引下推是什么?

解析:: 首先大家需要理解最左匹配原则,还要能用自己话讲清楚。这种题被概念是没用的,你需要理解掌握,然后记忆几个关键词,面试的时候自己串讲。例如(联合索引;最左边的列;范围查询)

参考回答:

最左匹配原则:

  1. 当MySQL使用联合索引进行查询时,会从索引的最左边开始匹配查询条件,如果查询条件中包含了索引左边的列,那么MySQL才可能会使用这个索引来优化查询。如果查询条件没有包含左边的列或者左边的列出现一些范围查询(>、<、between等),即使其他列都在索引中,MySQL也可能不会使用这个索引。

例子:

-- 基于上面的哪个(X、Y、Z)联合索引

//不符合最左前缀原则,最左侧的X字段未曾被使用
SELECT * FROM tb WHERE Y = "..." AND Z = "..."; 

// X出现范围查询,不能被使用
SELECT * FROM tb WHERE X > 10 AND Y = "..." AND Z = "..."; 

索引下推是什么?

直译:也就是将Server层筛选数据的工作,下推到引擎层处理。

解释:在没有索引下推的情况下,MySQL会先从索引中获取所有可能满足条件的数据行的主键,然后根据这些主键检索完整的数据行(即回表),最后再应用WHERE条件进行过滤。而有了索引下推,MySQL可以在扫描索引的同时,直接根据索引中的值来判断是否满足WHERE条件,从而避免不必要的回表操作。

学习指引: 一文搞懂最左匹配,索引覆盖,索引下推; MySQL索引应用篇:建立索引的正确姿势与使用索引的最佳指南!

5. Mysql相关:事务隔离级别?InnoDB引擎的默认隔离级别?InnoDB引擎如何解决幻读?

解析:: Mysql事务是重点考察知识,必须掌握。

参考回答:

事务隔离级别:读未提交、读已提交、可重复读和串行化。

InnoDB引擎的默认隔离级别:InnoDB存储引擎的默认事务隔离级别是“可重复读”(REPEATABLE READ)。在这个级别下,事务在开始时创建一个快照,事务内的查询都是基于这个快照进行的,因此可以确保在整个事务过程中读取到的数据是一致的,即使其他事务在此期间修改了数据。

InnoDB引擎如何解决幻读:幻读是指在同一个事务中多次执行相同的查询,但由于其他事务的插入操作,导致结果集不一致的现象。InnoDB引擎通过多版本并发控制(MVCC)和间隙锁(Gap Locks)来解决幻读问题。在可重复读隔离级别下,InnoDB使用MVCC来保持事务内数据的一致性视图。同时,InnoDB的间隙锁可以防止其他事务在已锁定范围的间隙中插入新的数据,从而确保在事务范围内不会出现新的、未被锁定的行,这样就解决了幻读问题。

学习指引: MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析

6. Mysql相关:你使用了哪些方式来对数据库查询进行优化?

解析:: 为了提高查询性能,可以从多个方面考虑;最直接的当然是,使用索引,优化sql语句,启用查询缓存; 还有一些需要在表设计阶段提前规划的措施:例如分库分表等

参考回答:

  1. 索引优化:前缀索引优化;覆盖索引优化;主键索引最好是自增的;防止索引失效;

  2. SQL查询优化:避免使用SELECT *,而是选择需要的列。避免在WHERE子句中使用函数或运算,这可能会导致索引失效。使用LIMIT分页查询时,注意性能问题,可以考虑使用索引覆盖扫描或其他分页技术。

  3. 启用查询缓存:启用MySQL的查询缓存功能,缓存频繁查询且结果不经常变动的查询结果。注意在数据变动频繁的场景下,查询缓存可能会成为性能瓶颈,需要谨慎使用。

  4. 分区和分表:对大表进行分区,将数据水平分割到不同的物理区域,提高查询性能。使用分表技术,如垂直分表或水平分表,将一个大表拆分成多个小表。

学习指引: SQL优化篇:如何成为一位写优质SQL语句的绝顶高手!

7. Spring相关:介绍下IoC和AOP?

解析:: 对于校招同学来说,如果你时间特别紧,实在没时间准备Spring,那你也需要把Spring常考的10个以内的面试题学会。 IoC和AOP就是其中之一。

参考回答:

IoC,即控制反转,是Spring框架的核心特性之一。在传统的程序设计中,我们直接在对象内部通过new关键字来创建依赖的对象,这种方式会导致代码之间高度耦合,不利于测试和维护。而IoC的思想是,将这些对象的创建权交给Spring容器来管理,由Spring容器负责创建对象并注入依赖。这样,对象与对象之间的依赖关系就交由Spring来管理了,实现了程序的解耦合。在项目中,我们可以通过Spring的配置文件或者注解来定义Bean,然后由Spring容器来创建和管理这些Bean。在Mybatis中,我们可以将SqlSessionFactory、Mapper等对象的创建和依赖关系交由Spring来管理。 AOP,即面向切面编程,是Spring提供的另一个重要特性。它允许开发者在不修改原有业务逻辑的情况下,增强或扩展程序的功能。AOP通过预定义的切点(例如方法执行前、方法执行后等)来织入切面逻辑,从而实现对原有功能的增强。在项目中,我们可以使用AOP来实现日志记录、事务管理、安全检查等功能。在Mybatis中,我们可以使用AOP来实现对数据库操作的日志记录、性能监控等功能。

学习指引: **************** & ****************

8. Spring相关:在Spring中连接数据库进行数据插入操作时有哪些需要注意的地方?事务传播行为有哪些?你在项目中基于什么考虑使用的?

解析:: Spring 重点面试题之一;Spring 事务相关。如果没时间系统准备,那就看我推荐资料的面试题。

参考回答:

在Spring中连接数据库进行数据插入操作时有哪些需要注意的地方?

在Spring中连接数据库进行数据插入操作时,需要注意合理配置数据源、确保数据库连接池资源的管理与释放、编写SQL语句时要防止SQL注入攻击、处理异常时要确保事务的一致性,并且应该利用Spring提供的事务管理功能来保证操作的原子性、一致性和隔离性。

事务传播行为有哪些?

事务传播行为定义了当一个事务方法被另一个事务方法调用时,当前方法应该如何处理事务。Spring支持的事务传播行为包括:

  • PROPAGATION_REQUIRED(如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务)、
  • PROPAGATION_SUPPORTS(如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行)、
  • PROPAGATION_MANDATORY(如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常)、
  • PROPAGATION_REQUIRES_NEW(创建一个新的事务,如果当前存在事务,则将当前事务挂起)、
  • PROPAGATION_NOT_SUPPORTED(以非事务方式执行操作,如果当前存在事务,则将当前事务挂起)、
  • PROPAGATION_NEVER(以非事务方式执行,如果当前存在事务,则抛出异常)、
  • PROPAGATION_NESTED(如果当前存在事务,则嵌套事务执行;如果当前没有事务,则创建新的事务)。

你在项目中基于什么考虑使用的?

在项目中,选择使用特定的事务传播行为是基于业务需求和操作复杂性的考虑。例如,对于需要保证多个操作原子性的场景,会选择PROPAGATION_REQUIRED来确保所有操作在同一个事务中完成。而在需要独立处理某个操作,不受其他事务影响时,可能会选择PROPAGATION_REQUIRES_NEW来创建一个新的事务。同时,也会根据性能优化的需要,选择合适的事务传播行为来减少事务的开销和资源的占用。总之,选择事务传播行为时需要综合考虑业务需求、数据一致性、系统性能和资源利用等多个方面。

学习指引: ****************

9. Spring相关:bean的生命周期?有哪些注入方式?

解析:

八股文,整体流程大家可以理解记忆,但是里面的细节点,说实话是背了忘忘了背。 其实能说个大概面试官一般不会深究。

参考回答: alt

学习指引: *********************

10. 并发相关:线程池有哪些核心参数?各自的作用?项目中如何使用的?

解析:: 线程池是Java并发考察的重点,据我去年看过的面经统计,线程池甚至是最常考的, 必须掌握,滚瓜烂熟。

参考回答:

  • corePoolSize(核心线程数):这是线程池的基本大小,即线程池中始终保持的线程数量,不受空闲时间的影响。当有新任务提交时,如果线程池中的线程数小于corePoolSize,即便存在空闲线程,线程池也会创建一个新线程来执行任务。

  • maximumPoolSize(最大线程数):线程池允许创建的最大线程数量。当线程池中的线程数超过corePoolSize,但小于maximumPoolSize时,只有在队列满的情况下,才会创建新的线程来处理任务。

  • keepAliveTime(空闲线程存活时间):当线程池中的线程数超过corePoolSize时,多余的线程在空闲时间超过keepAliveTime后会被销毁,直到线程池中的线程数不超过corePoolSize。这有助于在系统空闲时节省资源。

  • unit(时间单位):这是keepAliveTime的时间单位,如秒、毫秒等。它定义了如何解释keepAliveTime的值。

  • workQueue(工作队列):这是一个用于存储等待执行的任务的队列。当线程池中的线程数达到corePoolSize,且这些线程都在执行任务时,新提交的任务会被放入workQueue中等待执行。

  • threadFactory(线程工厂):这是一个用于创建新线程的对象。通过自定义线程工厂,我们可以控制新线程的创建方式,例如设置线程的名字、是否是守护线程等。

  • handler(拒绝策略):当所有线程都在繁忙,且workQueue也已满时,新提交的任务会被拒绝。此时,就需要一个拒绝策略来处理这种情况。常见的拒绝策略有抛出异常、丢弃当前任务、丢弃队列中最旧的任务或者由调用线程直接执行该任务等。

在实际生产环境中,我们可能更倾向于直接使用ThreadPoolExecutor类来创建线程池,因为这样可以更精细地控制线程池的参数和行为。

学习指引: Java 线程池详解

11. 并发相关:进程、线程和协程的区别和作用?

解析:

常规八股文,不管是并发,还是操作系统,都有可能问到。

参考回答:

  1. 进程:进程是操作系统分配资源的基本单位,它拥有独立的内存空间和系统资源。每个进程都拥有自己的地址空间、堆、栈以及其他的系统资源。进程之间的通信需要通过进程间通信(IPC)的方式来进行。进程适合处理重量级的任务,但由于进程切换时需要保存和恢复大量的上下文信息,因此进程切换的开销相对较大。
  2. 线程:线程是操作系统调度的基本单位,它是进程的一个执行单元。一个进程可以包含多个线程,这些线程共享进程的地址空间和部分资源(如堆),但每个线程拥有自己独立的栈。线程之间的切换开销相对较小,因为它们共享进程的内存空间,不需要像进程那样在切换时保存和恢复大量的上下文信息。线程适合处理IO密集型任务,可以通过多线程并发执行来提高程序的执行效率。
  3. 协程:协程是一种用户态的轻量级线程,它完全由程序控制,而不是由操作系统内核来调度。协程拥有自己的寄存器上下文和栈,但它们的栈是独立的,不与其他协程共享。协程之间的切换开销非常小,因为它们只涉及到少量的上下文切换。协程适合处理大量并发的、IO密集型的任务,可以通过协程的异步特性来提高程序的执行效率。

学习指引: 进程、线程基础知识

12.并发相关:volatile关键字的作用?在保证线程安全的情况下实现单例对象的获取?

解析:: Java并发相关的常规且重要的面试题。不要背,理解记忆,然后用自己的话来描述。结合volatile,经常延伸考察“双重检查锁定”方式实现单例模式;

参考回答:

作用:保证变量的可见性。当一个变量被声明成volatile,JMM会确保所有线程看到这个变量的值是一致的。

双重检查锁定代码示例

public class Singleton {  
    private volatile static Singleton instance = null;  
  
    private Singleton() {  
        // 私有构造函数,防止其他类实例化此单例  
    }  
  
    public static Singleton getInstance() {  
        if (instance == null) { // 第一次检查  
            synchronized (Singleton.class) {  
                if (instance == null) { // 第二次检查  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}

学习指引:

书籍:《并发编程艺术》|第3章 Java内存模型|3.8 双重检查锁定与延迟初始化

13.并发相关:synchronized关键字的作用?底层实现原理?和ReentrantLock的区别?锁升级及实现原理?

解析:: Java并发相关的常规且重要的面试题。不要背,理解记忆,然后用自己的话来描述。

参考回答:

作用:synchronized是Java中的一个关键字,主要用于给对象加锁,保证同一时刻只有一个线程可以执行某个方法或某个代码块,从而实现线程同步。

synchronized的底层实现原理:是基于Monitor(监视器)的。在Java中,每个对象都与一个Monitor关联,Monitor包含一个互斥锁和一个等待队列。当一个线程进入synchronized代码块时,会尝试获取对象的Monitor锁,如果锁被其他线程持有,则该线程会被放入等待队列中阻塞。如果成功获取锁,线程执行同步代码,并在退出时释放锁,同时可能会唤醒等待队列中的其他线程。这个过程由JVM内部机制管理,确保多线程访问时的同步和互斥。

和ReentrantLock的区别?

  • 底层实现不同:synchronized 是依赖于 JVM 实现的,具体实现并没有直接暴露给我们;ReentrantLock 是 JDK 层面实现的,所以我们可以通过查看它的源代码,来看它是如何实现的。
  • ReentrantLock 比 synchronized 增加了一些高级功能: 等待可中断;可实现公平锁;可实现选择性通知(锁可以绑定多个条件Condition)

锁升级及实现原理?:锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。

学习指引:

**************************

************************

14.Redis相关:有哪些持久化方式?AOF和RDB的底层实现原理?在项目中用的是哪种?基于什么考虑使用的?

解析:

Redis持久化属于重要面试点,经常考察,需要重点掌握

参考回答:

哪些持久化的方式/底层原理:

  1. AOF持久化:AOF以日志的形式记录服务器所接收到的所有写操作命令,并在服务器启动时,通过重新执行这些命令来重建数据集。
  2. RDB持久化:RDB持久化是在指定的时间间隔内,将内存中的数据集快照写入磁盘,也就是Snapshot快照。它是一个二进制文件,可以通过配置设置自动做快照持久化的频率。 2.混合持久化:将 RDB 和 AOF 合体使用,这个方法是在 Redis 4.0 提出的,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。

在项目中使用的持久化方式及考虑: 在项目中,选择哪种持久化方式取决于具体的应用场景和需求。

  • 如果数据丢失的代价非常大,或者需要尽可能减少数据丢失的风险,通常会选择AOF持久化,因为它提供了更高的数据安全性。但是,AOF可能会导致日志文件过大,需要定期处理和维护。
  • 如果数据集非常大且对性能要求较高,可能会选择RDB持久化,因为RDB文件更加紧凑,恢复速度更快。但是,RDB是定时持久化,可能会导致一定时间内的数据丢失。

学习指引: 小林coding|图解redis|持久化篇

15.Redis相关:引入哨兵集之后,主从故障的转移过程?

解析:: redis高可用面试题,校招考察频率相对redis其他部分来说略低。但是还是推荐掌握。大家看完推荐资料后理解记忆,然后用自己的话来描述过程。

参考回答:

转移过程是这样的:哨兵集群持续监控主节点,一旦检测到主节点故障,就会启动故障转移流程。在这个过程中,哨兵会选择一个新的从节点升级为主节点,并更新其他从节点的复制目标,确保数据一致性。同时,客户端也会根据哨兵的指引,切换到新的主节点进行数据操作。这样,Redis系统能够在主节点故障时,快速自动地恢复服务。

学习指引: Redis主从复制是怎么实现的?

16.Redis相关:Redis是单线程吗?采用单线程为什么速度快?

解析:: redis 基础且重点的考题之一,需要熟练掌握。

参考回答:

有如下几个原因:

  • Redis 的大部分操作都在内存中完成,并且采用了高效的数据结构,因此 Redis 瓶颈可能是机器的内存或者网络带宽,而并非 CPU,既然 CPU 不是瓶颈,那么自然就采用单线程的解决方案了;
  • Redis 采用单线程模型可以避免了多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销,而且也不会导致死锁问题。
  • Redis 采用了 I/O 多路复用机制处理大量的客户端 Socket 请求,IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听 Socket 和已连接 Socket。内核会一直监听这些 Socket 上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。

学习指引: 面试题解析:Redis 采用单线程为什么还这么快?

17.Redis相关:Redis阻塞的原因有哪些?出现阻塞后的排查以及解决方式?

解析:: 可以从内在原因和外在原因进行分析。 参考回答:

Redis阻塞的原因主要有:

  1. 内在原因:
  • 数据集中过期:当大量key同时过期时,Redis的主动过期任务会在主线程中执行,导致阻塞。

  • CPU饱和:如果Redis的并发量达到极限,或者CPU资源被其他进程过度占用,会导致Redis性能下降甚至阻塞。

  1. 外在原因:
  • 数据结构或API使用不合理:例如,对大对象执行复杂的命令,如hgetall或del操作大hash对象,可能导致阻塞。
  • CPU竞争:进程竞争或绑定CPU可能导致Redis阻塞。
  • 网络问题:网络闪断、Redis连接拒绝或连接溢出等问题都可能导致阻塞。

出现阻塞后的排查方式:

  • 使用Redis的慢查询日志功能,记录执行时间超过阈值的命令。
  • 使用INFO命令查看Redis的运行状态,如内存使用情况、CPU占用率等。
  • 使用redis-cli的--stat选项实时查看Redis的运行情况。

解决方式:

  • 优化数据结构和命令使用:避免使用复杂的命令操作大对象,可以考虑将大对象拆分为多个小对象。
  • 合理配置Redis参数:如设置合适的过期策略、调整连接池大小等。
  • 分布式部署:通过主从复制、哨兵模式或集群模式实现数据的分布式存储和处理,提高系统的整体性能和可用性。

学习指引: redis阻塞原因以及处理方案

关联阅读:该同学的一面面经讲解

阿里云 实习面经(已OC) 一面面经|讲解

本文也是**《热门面经讲解》专栏系列文章之一,大家可以点个关注**,我会持续更新

#面经##实习##暑期实习##牛客在线求职答疑中心##阿里云#
热门面经讲解 文章被收录于专栏

挑选近期热门真实后端面经进行讲解分析,给出:个人分析+参考回答+学习资料指引。

全部评论

相关推荐

25 79 评论
分享
牛客网
牛客企业服务