【面经】CSDN Java开发实习生一面面经分享

一 MySQL索引的失效场景有哪些?

  1. 最左匹配原则未满足:在联合索引中,查询条件必须满足最左前缀,否则会导致索引失效。

  2. 使用了SELECT 查询:如果查询时使用了SELECT *,并且没有只查询必要的列,可能导致索引失效。

  3. OR操作使用不当:当WHERE子句包含OR操作,且两侧的查询列不命中相同的索引时,可能导致索引失效。

  4. 负向查询的使用:包括NOT、!=、<>、NOT IN、NOT LIKE等,可能导致索引失效,但并非绝对,需要根据MySQL优化器的判断。

  5. 隐式类型转换:当索引列与查询值的类型不匹配时,会发生隐式类型转换,可能导致索引失效。

  6. 索引列上使用内置函数:在索引列上使用内置函数或进行计算表达式,将导致索引失效。

  7. LIKE通配符使用不当:如果LIKE查询以%开头,将不能使用索引。 

二 如何保证消息的不丢失?

  1. 持久性存储:在消息系统中,一旦消息被接收,它应该立即被写入到持久性存储中,如硬盘。这样即使系统崩溃,消息也不会丢失。

  2. 确认机制:发送方在发送消息后,会等待接收方的确认信号。如果没有收到确认信号,发送方会重新发送消息。

  3. 备份和复制:通过在不同的服务器或地理位置上备份和复制消息,可以防止因硬件故障或自然灾害导致的消息丢失。

  4. 事务处理:在发送和接收消息的过程中使用事务处理,可以确保消息的一致性和完整性。

  5. 错误处理和恢复:当检测到错误或失败时,消息系统应该能够自动恢复并重试发送消息。

  6. 消息队列:使用消息队列可以缓冲消息,防止因系统繁忙或网络问题导致的消息丢失。

  7. 优先级和重试:为重要的消息设置高优先级,并在发送失败后进行重试,可以增加消息的可靠性。

  8. 加密和安全:通过加密和安全措施,可以防止消息在传输过程中被篡改或窃取。

  9. 监控和警报:通过监控系统的性能和状态,可以在出现问题时及时发现并处理,防止消息丢失。

三 线程池的参数有哪些,有什么作用?

        1.corePoolSize(核心线程数):线程池中保持活跃的线程数,即使它们处于空闲状态也不会被回收。这将允许迅速处理突增的任务需求,而无需等待新线程的创建。

        2.maximumPoolSize(最大线程数):线程池中允许存在的最大线程数。超出这个数量的任务将会被阻塞,直到有线程可用。

        3.keepAliveTime(线程保活时间):当线程数超过corePoolSize时,这是多余空闲线程在被终止之前等待新任务的最长时间。

        4.workQueue(任务队列):用于在执行任务前存放任务的队列。该队列有助于平滑突发任务,使得线程池不会立即创建大量线程。

        5.threadFactory(线程工厂):用于创建新线程的工厂。允许用户自定义线程的创建,如设置线程名、线程优先级等。

        6.handler(饱和策略):当线程池已满,无法接受更多任务时的处理策略,如调用者可以设置一个阻塞策略或弃用策略。

四 当一个任务到达线程池里面,它是如何调度的?

        1.提交任务:首先,任务通过execute()方法提交到线程池。

        2.核心线程处理:如果当前运行的线程数少于corePoolSize,则创建一个新的核心线程来处理任务。

        3.队列等待:如果运行的线程等于corePoolSize,任务将被添加到workQueue

        4.最大线程处理:如果workQueue已满,且运行的线程少于maximumPoolSize,将创建一个新的非核心线程来处理任务。

        5.饱和策略:如果运行的线程等于maximumPoolSize,根据handler的设定,采取相应措施,如阻塞、弃用任务或调用rejectedExecution()方法。

        6.线程保活与回收:如果线程数超过corePoolSize,且这些线程空闲时间超过keepAliveTime,则多余线程将被终止。

五 Springboot的自动装配

        Spring Boot的自动装配是一个核心特性,它通过一系列的注解和配置来实现。具体如下:

        1.启动器依赖包:Spring Boot Starter是一系列预定义的依赖组合,使得开发者能够快速搭建特定类型的应用程序。例如,使用 spring-boot-starter-data-jpa 可以自动配置JPA和数据库相关依赖项。

        2.主类与注解:主类通常使用 @SpringBootApplication 注解标注。这个注解是一个组合注解,包含了 @EnableAutoConfiguration@SpringBootConfiguration 和 @ComponentScan

@EnableAutoConfiguration 使得Spring Boot根据添加的依赖自动配置相应的设置。

        3.自动配置原理:Spring Boot在启动时会扫描项目所引入的依赖,并读取 META-INF/spring.factories 文件中的配置信息,从而自动加载相应的自动配置类。通过 @Conditional 注解,自动配置类会根据特定的条件来决定是否加载配置。

        4.配置文件处理:Spring Boot会自动读取项目中的 application.properties 或 application.yml 配置文件,并按照预设的规则应用这些配置。

        5.组件扫描:@ComponentScan 注解启用组件扫描功能,自动扫描和注册使用 @Component@Service@Repository@Controller 等注解标注的类为Spring Bean。

        综上所述,Spring Boot的自动装配极大地简化了项目的配置和开发过程。其背后的机制涉及了依赖管理、注解处理、条件装配等多个环节,使得开发者能够专注于业务逻辑的开发,而不需要关心繁琐的配置细节。

六 为什么可以调用定义的mapper接口的抽象方法去实现数据库的功能?

        由于MyBatis的映射机制和动态代理(Dynamic Proxy)技术。在MyBatis中,Mapper接口与XML映射文件是相辅相成的。Mapper接口定义了与数据库交互的抽象方法,而XML映射文件则具体描述了这些方法如何映射到数据库的操作上,包括SQL语句的编写、参数的设置、以及结果集的映射等。

        MyBatis在启动时或首次使用Mapper接口时,会利用Java的动态代理机制为Mapper接口创建一个动态代理对象。这个动态代理对象会拦截对Mapper接口中所有方法的调用,并根据Mapper接口方法的名字和参数信息,在XML映射文件中找到对应的SQL语句,然后执行这个SQL语句,并将结果映射成方法返回类型的对象。

        当通过动态代理对象调用Mapper接口中的方法时,MyBatis会解析方法名(可能还包括参数信息)来定位XML映射文件中的SQL语句;替换SQL语句中的参数占位符为实际的值;执行SQL语句。将数据库返回的结果集映射成Java对象(可能是POJO、Map、List等);将映射后的结果返回给调用者。

        这种机制带来了以下几个显著的优点:

  • 解耦:Mapper接口与具体的SQL实现(XML映射文件)分离,提高了代码的解耦度,使得代码更加清晰、易于维护。
  • 类型安全:Mapper接口定义了方法的参数和返回类型,这有助于在编译时期就捕获一些潜在的错误。
  • 灵活性:通过XML映射文件,可以灵活地编写SQL语句,包括复杂的动态SQL,而不必担心Java代码中的SQL注入等问题。

七 请你聊聊动态代理?

        动态代理是一种在程序运行时动态创建对象的方法,它允许开发者在不修改代码的情况下增强或控制对原有对象的访问。动态代理是在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术,这意味着代理类是在运行时动态生成的,而不是在编译时确定的。这种技术主要用于在方法执行前后添加额外的处理逻辑,如日志记录、权限验证等。

        动态代理的实现方式:

        基于接口的动态代理:使用Java自带的java.lang.reflect.Proxy类,通过指定接口和InvocationHandler来实现代理对象的创建。适用于目标对象实现接口的情况,是JDK官方提供的实现方式。

        基于类的动态代理:使用第三方库如CGLib,通过net.sf.cglib.proxy.Enhancer类来创建代理对象。适用于目标对象不实现接口,但需要代理的情况,它通过生成目标类的子类来实现代理。

        动态代理的应用场景:

        Spring AOP:Spring框架中的面向切面编程(AOP)大量使用了动态代理技术,以实现对方法的前置处理、后置处理等。

        事务管理:在数据库操作中,通过动态代理可以实现事务的自动管理,确保数据的一致性和完整性。

        缓存处理:在访问耗时资源之前,通过代理对象检查是否有缓存,可以大大提高系统性能。

        日志记录:在每个方法调用前后,自动记录日志信息,有助于系统监控和维护。

        动态代理的优势:

        代码解耦:被代理类与代理类之间的耦合度降低,提高了代码的可维护性。

        功能增强:可以在不改变原有业务逻辑的情况下,增加或修改对象的行为。

        性能优化:由于代理对象的方法调用可以被拦截,因此可以在这些方法中加入性能优化的逻辑。

八 分布式锁除了redis还有哪些实现的方式?

        除了Redis,分布式锁还可以通过数据库、Zookeeper等技术实现。在大型系统中,为了保证数据的一致性和系统的稳定性,分布式锁是一种常用的技术手段。分布式锁可以在多个进程或者线程之间同步对共享资源的访问,避免数据竞争和不一致问题。具体使用哪种方式取决于系统的特定需求、性能考量以及可靠性要求。

        1.基于数据库实现的分布式锁

        乐观锁:乐观锁主要通过数据的 version 字段(版本号)来实现。每次读取数据时,同时读取 version 字段值,当数据进行更新操作时,会再次检查 version 字段的值是否在读取之后未被其他事务修改。如果是,则提交事务并更新 version 值;否则,中止事务或重试。

        唯一索引锁:通过在数据库中创建一个具有唯一索引的表来实现。例如,创建一个名为 method_lock 的表,包含方法名等字段,并在方法名字段上创建唯一索引。执行某个方法时,使用该方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应行数据释放锁。这种方法的优点是实现简单,缺点是可能影响数据库性能并且不具备非阻塞特性。

        2.基于Zookeeper实现的分布式锁

        Zookeeper节点锁:Zookeeper通过其层次化节点结构实现分布式锁。每个客户端试图创建同一节点,成功的客户端获得锁,其他客户端等待通知。当锁被释放时,等待的客户端之一将被通知并获得机会创建节点。Zookeeper的优点在于它高可用且能有效地避免分布式环境下的“脑裂”问题。Zookeeper的锁机制具备良好的可靠性和容错性,但需要合理地管理会话以避免锁无法释放的问题。

        3.基于文件系统实现的分布式锁

        文件锁:在文件系统中创建一个锁文件作为占有信号。进程尝试创建锁文件,如果成功则持有锁,操作完成后删除该文件释放锁。Unix和Linux系统通常使用/tmp目录存储这种类型的锁文件。文件锁实现简单,但仅限于单个系统内的多进程访问控制,不适用于真正的分布式环境。

        4.基于开源框架Redisson实现的分布式锁

        Redisson:Redisson是一个在Redis基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了基于Redis的分布式锁实现,还增加了如看门狗(Watchdog)自动续期机制等高级功能,有效解决了因锁提前过期而导致的数据不一致问题。Redisson通过Netty实现与Redis的非阻塞通信,提高了性能及可靠性。

        5.基于ETCD实现的分布式锁

        etcd租约TTL锁:etcd是一种高度一致的键值存储系统,常用于保存关键元数据信息并实现分布式锁。通过etcd的租约(Lease)机制,可以实现自动过期的锁,避免了死锁的风险。具体实现是通过在etcd中创建一个键,同时设置一个TTL(Time to Live),在TTL时间内锁没有被释放则自动失效。

        6.基于Consul实现的分布式锁

        Consul锁:Consul是一种服务发现和配置工具,也支持分布式一致性数据存储。借助Consul的API,可以实现分布式锁的功能。具体做法是在Consul的KV存储中创建一个键作为锁,利用Consul的Session机制来保证锁的正确释放和避免并发问题。

  • 25
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值