面试总结-619
前言
昨天也进行了一次 Java开发 的线上面试。
嗯,自我介绍有的时候确实也很重要,就像我今天面试的时候自我介绍是瞅着简历说的,把自己的项目归纳总结说明的时候总是觉得措辞不太准确,就磕磕巴巴,可能是会让面试官觉得吞吞吐吐是不是项目有水分。
至于我回答了什么,我就不在这里放了,还是把网上的标准答案扣下来,一边抄的时候,我也一边巩固,好让自己的记忆也更加深刻一点。
面试题
1. SQL 在MySQL中的执行过程是怎样的?
这个问题,对于某些没有看过MySQL相关实现原理的朋友们来说,可能是超纲了,毕竟我们用MySQL可能只用他的存储和查询,对于他执行过程可能并不关心。但是对于面试来说,可不能说一句“我不知道”就能轻松了事。
毕竟这也许会关乎面试的结果。
那么实际上执行过程是怎样的呢?
受限,一条SQL在MySQL中的执行过程分为两个部分。
第一个部分是在MySQL中的Server层。
- 首先会经过 连接器 (管理与客户端链接、权限验证)
- 随后会进入 分析器 ,分析SQL的词法和语法,了解这条SQL想要做什么事情,如果SQL为无效的SQL(词法、语法不正确、字段不存在等),会返回错误
- 其次,会进入 优化器 ,该优化器会为本条SQL的执行选择索引和生成执行计划
第二部分是在MySQL中的存储层。
- 首先写undolog存储回滚段指针和事务ID(通过指针找到undo记录,可判断记录可见性,可用于回滚/奔溃恢复/MVCC
- 开始执行生成的执行计划
- 写入redolog
- 写入binlog(select不会)
- 提交事务
- 刷redolog,处于commit-prepare阶段
- 刷binlog(select不会),处于commit-commited阶段(为了让redolog和binlog日志的逻辑一致,使用了两段式提交)
参考文章地址: 深入理解MySQL执行过程及执行顺序
2. String、String Builder、String Buffer的区别
这个问题属于比较常见的Java基础的问题。
三者都经常用于做字符串的处理,是比较常用类。
String
内部使用一个字节数组来表示字符串内容。String
是不可变的。他通过在类名前加了 final
来表示该类无法继承,自然也不会有人能通过正常方法修改内部的字节数组了。该类的一些操作方法返回的 String
类,均没有对该类内部做任何修改操作。
StringBuilder
是一个字符串的操作类。内部使用字节数组来为字符串做操作。可以用于字符串拼接、合并、删减等一些操作。他并没有在代码层面上做任何多并发处理,因此在多线程操作时可能会出现多线程一致性问题,建议在线程的局部内调用。
StringBuffer
同样也是一个字符串的操作类。能力与 StringBuilder
差不多,但是因为在许多操作方法签名上添加了 synchronized
标志,这也导致在,在多线程的情况下,对同一个 StringBuffer
做字符串的操作,会进入对象的监视器锁。因此该类也是多线程安全的。但也因为多个锁的消耗,可能在线程局部内使用时性能不如 StringBuilder
。
参考文章地址: 【面试题精讲】String、StringBuffer、StringBuilder 的区别?
3. JVM内存溢出有遇到吗?什么时候会出现?
- 堆溢出
java.lang.OutOfMemoryError: Java heap space
原因有:
- 应用中可能存在大对象分配
- 内存泄漏,多次GC后仍无法得到足够大的内存容纳新对象
解决方案有:
- 检查最大数组分配
- 通过jmap把内存dump下来,使用mat分析
- 使用-Xmx加大最大堆内存
- 元空间溢出
java.lang.OutOfMemoryError: PermGen spacejava.lang.OutOfMemoryError: Metaspace
原因有:
- 运行期间动态生成了太多的类,导致类信息过多
- 方法栈溢出
java.lang.OutOfMemoryError : unable to create new native Thread
原因:
- 创建过多的线程导致系统无法生成新的线程
解决方案:
- 使用jstack查看线程数是否过多
- 通过-Xss降低线程栈的容量,可以多创建线程
- 检查内存和系统的限制
- 非常规溢出 - 分配了超大数组
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
原因:
一般是由于不合理的大尺寸的数组分配请求导致的。在数组的内存分配之前,JVM会检查数组是否在平台可以寻址,否则会抛出错误。
解决方案:
- 尽量避免分配不合理的大尺寸数组。
- 本地方法栈
java.lang.OutOfMemoryError: stack_trace_with_native_method
原因:
- 一般发生在本地方法内部。
- swap溢出
java.lang.OutOfMemoryError: Out of swap space
原因:
- swap分区不足
- 其他进程消耗了所有的内存
解决方案:
- 加大swap或内存大小
- 检查其他进程的内存消耗
参考文章: 1篇文章搞清楚8种JVM内存溢出(OOM)的原因和解决方法
4. 接口调用太慢了,如何解决和定位
接口调用过慢有几种方向可以排查。
- 个别接口慢(排查具体内容)
- 所有接口慢(可能是服务器问题)
如果是服务器问题,检查服务器网络、CPU、内存使用率等相关参数,检查服务器的当前状态是否异常。
如果是个别接口慢,则需要看是否是如下几个问题。
- 数据库查询慢。是否是SQL查询涉及有问题,需要优化SQL查询或者优化表结构(比如加索引)、大数据(分库分表)
- 子系统调用过慢。查询与子系统之间的通信是否不畅,或子系统是否需要优化执行过程。
- 是否代码中有死锁。可用jps+stack、jconsole、jvisualvm 来查看是否有死锁。
参考文章: Java后端接口响应慢的排查方法及解决方案
5. 你们登录JWT怎么用的,为什么要这么用?JWT主要是用来做什么?
JWT
, 全称 JSON Web Token
,是用于网络中授权和数据交换的主要方式之一。一般是前后端分离项目中,后端用于验证前端的身份。
JWT
的内容由三部分组成。
- 头
- 数据载荷
- 签名
其中头和数据载荷可以通过Base64解密获取其明文字符串,但签名无法获取其原字符串。
签名部分可以认证整段的 JWT
内容是否有效,如果这段中任何一处修改了,都将造成 JWT
失效,因此,就算能看到数据载荷的明文,JWT
也是无法篡改其内部内容的,毕竟修改了之后,这段 JWT
内容就会完全失效。
JWT
由生成者发布,一般来说,是在登录时服务端颁发给客户端的身份凭证,这个凭证加入在客户端被篡改内容,服务端解析时就会发现凭证无效。在很多的前后端分离项目中,被当成时重要的身份认证方式之一。
客户端借由登录接口,获取到了自己的身份认证后,在请求头上添加这个身份标识,以此来请求所有其他的资源。服务端通过在过滤器中验证、解析请求中的 JWT
,借此知道当前 JWT
是否有效、当前请求人的信息。
JWT
涉及初衷是为了避免客户端随意篡改身份认证的内容以及释放后端存储SESSION的压力,JWT
保证了自己内容的正确性,好比保证的钥匙本身不会被篡改,这家只能用这把钥匙打开,没错,一切十分正常、正确,除了遇到意外的情况—— 钥匙被别人偷了, JWT
被别有用心的人偷取,他使用该钥匙来开我们的门。
如果碰到这种情况,门或许能做的事情就有限了,因为门只认钥匙,不认开锁的人是谁,是谁持有钥匙。
尽管我们对发生的这种情况难以解决,但是至少还能做到预防这种情况,如下:
- 在颁发
JWT
时,指定JWT
的有效时间,时间指定仅几小时或者几分钟,另外提供接口对JWT
进行刷新获取新的JWT
,参考OAuth
。 - 在颁发
JWT
时,将客户端的统一标识塞进JWT
的载荷里, 后端在验证JWT
时主动验证 请求的客户端的统一标识 是否一致。统一标识可以是客户端的ip,可以是客户端用某种算法生成的唯一ID。
当然这些手段仅仅只是用来预防,也不能完全成功预防 JWT
被偷取的情况,只能加大偷取的成本,如果不在乎SESSION的存储压力,或许可以用分布式的SESSION方式来解决传统中SESSION存储无法共享和占用空间的问题。
参考文章: JWT 官网
6. Spring Cloud 注册中心 如何判断服务的状态?如果服务状态异常,Spring Cloud会如何处理请求?
服务注册中心维护一个注册表来存储已注册的服务实例的信息和状态。
在服务实例注册到注册中心后,实例会每隔一段时间向注册中心发送心跳信息,已更新自己在注册中心的状态。
如果实例在一段时间内没有向注册中心发送心跳信息,注册中心会将该实例从注册表中踢除。
如果服务状态异常,造成的服务不可响应、服务响应超时,会触发熔断机制,服务返回降级后的自定义默认数据,服务也会一时间进入熔断
的状态,调用时优先返回设置的默认返回值。如果调用失败率到达一定阈值,会被标记为 短路
。熔断
之后,一定时间后会进行重试,如果正常,则取消服务的 熔断
状态。
7. Spring Cloud 用过哪些组件?
主要组件分为如下:
- 注册中心
- 配置中心
- 网关
- 负载均衡
- 服务调用
- 熔断降级
对应 Spring Cloud Netflix 如下:
- Eureka
- Spring Cloud Config
- Spring Cloud Gateway
- Ribbon
- Feign
- HyStrix(可与Feign协同使用,帮助Feign实现服务降级)
8. 你知道Redis怎么刷盘的吗?
关于Redis的刷盘策略。有两种说法。
一种是Redis的持久化策略,另一种是特指AOF的刷盘策略。
Redis持久化策略包括三种:
- RDB快照形式
- AOF追加文件形式
- RDB和AOF混合模式
RDB快照介绍
在一定间隔时间内,将Redis中的内存中的数据以二进制的形式保存在硬盘上。持久化策略使用配置文件或命令来触发。
RDB在配置文件中配置可以如下:
1 |
|
优缺点如下:
优点:
- 二进制文件,占用空间小,传输速度快
- 恢复数据快
- 持久化对服务器性能影响小,大部分工作由子进程完成
缺点:
- 不能实时保存数据,如果故障、恢复数据时可能会丢失某些数据
- 生成过程会占用较多内存和CPU
AOF追加文件形式持久化介绍
将Redis服务器执行的每条写命令记录到一个文本文件——AOF文件。
AOF有三种持久化策略:
- no 写入缓存,什么时候刷盘由Redis决定
- everysec 每隔一秒刷一次盘
- always 写入缓存同时写入磁盘
AOF追加形式的持久化优缺点如下:
优点:
- 近乎实时的记录Redis中内存的数据,数据丢失概率比RDB快照形式少
- AOF文件可以方便查看和编辑,直接由redis客户端执行
- AOF会自动进行重写,以免AOF文件过大,重写不影响服务器正常服务(重写是新建一个文件,并将当前数据使用set记录)
缺点:
- AOF文件通常比RDB快照大
- AOF恢复速度比RDB慢,因为要重新执行所有的命令
- AOF写入时可能出现数据不一致,需要使用redis-check-aof工具来修复
AOF和RDB混合形式的持久化介绍
可以使用AOF保证数据不丢失,作为恢复的第一选择,同时使用RDB当成冷备份数据。当AOF文件损坏时,可以及时使用RDB恢复数据。
如果要开启混合需要在配置文件中配置属性
aof-use-rdb-preamble yes
三种持久化策略都有各自的优缺点。我们可以根据场景选择。
- 数据完整性要求不高,可使用RDB,或AOF设置每秒一次
- 需要数据尽可能不丢失,可使用AOF,设置每次操作都同步
- 数据完整性和性能都由要求,则可混合形式的持久化
- 如果对存储和性能比较敏感的,可使用RDB
参考文章: Redis持久化及建议
写在后面
本次就把昨日面试的一些关心的内容记录了下面。
当然我还有太多太多的知识点可能漏掉了,需要查漏补缺。