转载

2020常见JAVA面试题整理(进阶)

1、jsp 9大内置对象

request :封装客户端的请求,其中包含来自GET或POST请求的参数;

response :封装服务器对客户端的响应;

pageContext :通过该对象可以获取其他对象;

session :封装用户会话的对象;

application :封装服务器运行环境的对象;

out :输出服务器响应的输出流对象;

config :Web应用的配置对象;

page :JSP页面本身(相当于Java程序中的this);

exception :封装页面抛出异常的对象。

2、jsp的4种作用域

page 代表与一个页面相关的对象和属性。

request 代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。

session 代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。

application 代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。

3、forward 和 redirect 的区别?

Redirect :重定向,服务器告诉浏览器请求的URI,由浏览器再次向新的地址发起请求

Forward :转发,由服务器内部执行跳转,共享request数据,并将执行结果告诉浏览器,浏览器并没有感知到地址发生变化。

4、SimpleDataFormat是线程安全的吗?如何更好的使用而避免风险?

simpleDateFormat不是线程安全的。参考下面线程安全的方法:

// 1. 在方法内部使用,没有线程安全问题
private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
public String getFormat(Date date){
    SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT);
    return dateFormat.format(date);
}
 
// 2. 每次使用的时候加锁      
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void getFormat(){
    synchronized (SIMPLE_DATE_FORMAT){
    SIMPLE_DATE_FORMAT.format(new Date());
    ….;
}
 
// 3. 使用ThreadLocal,每个线程都有自己的SimpleDateFormat对象,互不干扰
private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() {
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd");
    }
};
 
// 4. 使用DateTimeFormatter(This class is immutable and thread-safe.)
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(timeFormatter.format(LocalDateTime.now()));

5、有哪些设计模式?

工厂模式:Spring通过工厂模式创建bean

代理模式:Spring AOP通过动态代理增强原对象的功能

责任链模式:SpringMVC的过滤器

单例模式:spring构建的对象默认是单例

6、写个单例模式的代码?

public class SingletonClass { 
    private static final SingletonClass instance = new SingletonClass(); 
   
    public static SingletonClass getInstance() { 
        return instance; 
    } 
     
    private SingletonClass() {  
         
    }  
}

7、多线程如何避免死锁?

  • 加锁顺序(线程按照一定的顺序加锁)
  • 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
  • 死锁检测(jstack -l jvm_pid)

8、什么是XSS攻击,具体如何实现?

XSS又叫CSS (Cross Site Script) ,跨站脚本攻击。它指是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入Web里面的html代码会被执行,从而达到恶意用户的特殊目的。

  • 对输入内容的特定字符进行编码,例如表示 html标记的 < > 等符号。 
  • 对重要的 cookie设置 httpOnly, 防止客户端通过document.cookie读取 cookie,此 HTTP头由服务端设置。 
  • 将不可信的值输出 URL参数之前,进行 URLEncode操作,而对于从 URL参数中获取值一定要进行格式检测(比如你需要的时URL,就判读是否满足URL格式)。 
  • 不要使用 Eval来解析并运行不确定的数据或代码,对于 JSON解析请使用 JSON.parse() 方法。 
  • 后端接口也应该要做到关键字符过滤的问题。 

9、集群环境中,session如何实现共享?

  • 持久化session到数据库,即使用数据库来储存session。
  • 使用redis共享session。
  • 使用memcache同步session。
  • 通过脚本或守护进程在多台服务器之间同步session。
  • 使用NFS共享session。
  • nginx负载均衡策略。
  • Tomcat集群+redis实现session同步共享。

10、Java对象的生命周期

  1. 创建阶段(Created)
  2. 应用阶段(In Use)
  3. 不可见阶段(Invisible)
  4. 不可达阶段(Unreachable)
  5. 收集阶段(Collected)
  6. 终结阶段(Finalized)
  7. 对象空间重分配阶段(De-allocated)

11、如何调优JVM

-client -server模式

-Xmn、-Xms、-Xmx

监控:jps、jstat、jinfo、jmap、jhat、jstack…

12、hibernate和ibatis的区别?

自动化程度上:hibernate是全自动化的orm框架,提供了对象到数据库的完全映射和sql的内部自动生成,其对象映射是指pojo到整张数据表的映射。而ibatis则是半自动化的,其对象映射是指ibatis提供了sql语句的参数到pojo的映射,sql语句的返回到pojo的映射,而且sql语句并不自动生成,由开发者手动编写和维护。

框架灵活性:自动化程度上的区别,直接影响了框架灵活性的区别。比如,在场景A中,要查询表的部分字段,在场景B中,要查询表的所有字段,hibernate由于只有一个配置文件,要么就部分字段lazy load,要么就全部查询出来,而且默认是(全部查询的),就无法适用2个场景,灵活性差。但要是ibatis,则可以通过不同场景使用不同sql语句来满足,并且不同的场景下,也可以有自己的sql语句的参数pojo映射和返回结果pojo映射,互不影响,这就灵活的适应了不同场景的要求。

性能上的区别:在海量数据和数据库性能要求苛刻的场合,往往是通过高度优化的sql语句来实现的,而这种情况下,hibernate则无法实现,但ibatis则可能通过sql的优化,来进一步提升数据库代码的性能。

学习门槛和成本:ibatis比hibernate更容易上手,而且hibernate也更复杂一些。

13、MYSQL常用优化?

  1. 数据库表结构设计
  2. 数据库字段设计, 选取最适用的字段属性, 尽量把字段设置为NOT NULL
  3. explain分析
  4. 更好的索引类型, 合理利用索引
  5. SQL查询语句优化
  • 使用连接(JOIN)来代替子查询
  • 分段查询
  • 尽量避免全表扫描
  • 避免在where子句中对字段进行null值判断
  • 不建议使用%前缀模糊查询
  • 避免在where子句中对字段进行表达式操作
  • 尽量避免在 where 子句中使用!=或<>操作符
  • 尽量避免在 where 子句中使用or 来连接条件
  • in 和 not in 也要慎用,否则会导致全表扫描
  • 尽量避免在where子句中对字段进行函数操作
  • 很多时候用 exists 代替 in 是一个好的选择
  • 不要使用 select * from t ,用具体的字段列表代替“*”
  • 避免频繁创建和删除临时表,以减少系统表资源的消耗
  • 避免隐式类型转换
  • 对于联合索引来说,要遵守最左前缀法则
  • 必要时可以使用force index来强制查询走某个索引
  • 注意范围查询语句
  • 尽量使用inner join,避免left join
  • 当只需要一条数据的时候,使用limit 1
  • 尽量用union all代替union

14、inner join和left join的区别?

left join (左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 

right join (右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录

inner join (等值连接) 只返回两个表中联结字段相等的行

15、索引的工作原理?

索引的目的在于提高查询效率,索引就像是书的目录,是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。

索引的原理在于使用空间换时间,索引会将建立的索引KEY存储在N叉树(BTree)。BTree适合在磁盘存储设备上组织动态查找表,这些数据结构以某种方式引用(指向)数据。

16、数据库索引包含哪些?

唯一索引:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

主键索引:是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。

组合索引:指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。

普通索引:最基本的索引,它没有任何限制,用于加速查询。

全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。

17、Mysql怎么分表,以及分表后如果想按条件分页查询怎么办?

MySQL垂直分区

如果把业务切割得足够独立,那把不同业务的数据放到不同的数据库服务器将是一个不错的方案,而且万一其中一个业务崩溃了也不会影响其他业务的正常进行,并且也起到了负载分流的作用,大大提升了数据库的吞吐能力。

MySQL水平分片(Sharding)

按一定规则(按哈希/按时间/按区间)分组,并把该组的数据存储到一个数据库分片中,这样随着数据量的增加,只要简单地配置一台服务器即可。

如何来确定某个用户所在的shard呢,可以建一张用户和shard对应的数据表,每次请求先从这张表找用户的shard id,再从对应shard中查询相关数据。

18、分表之后想让一个id多个表是自增的,效率实现?

  • 数据库自增id,通过设置数据库 sequence 或者表的自增字段步长来进行水平伸缩
  • uuid
  • 获取系统当前时间
  • snowflake算法
  • 通过redis/ memcache生成

19、数据库事务的特性?

原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。

一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。

隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。

20、数据库事务的隔离级别?

Read uncommitted:最低级别,任何情况都无法保证

Read committed:可避免脏读的发生

Repeatable read:可避免脏读、不可重复读的发生

Serializable:可避免脏读、不可重复读、幻读的发生

21、数据库的锁:行锁,表锁;乐观锁,悲观锁?

乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁。相当于对于同一把门,它拥有多个钥匙一样。就像这样,你家有一个大门,大门的钥匙有好几把,你有一把,你女朋友有一把,你们都可能通过这把钥匙进入你们家,进去啪啪啪啥的,一下理解了哈,没错,这个就是所谓的共享锁。

排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。与共享锁类型,在需要执行的语句后面加上for update就可以了。

行锁由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。

表锁和行锁相对应,给这个表加上锁。

22、分布式事务的处理方案?

两阶段提交(2PC/3PC)

补偿事务(TCC)

MQ 事务消息,最终一致性方案

23、SpringMVC的原理?

2020常见JAVA面试题整理(进阶)

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用处理器映射器HandlerMapping。
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
  4. DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
  5. 执行处理器Handler(Controller,也叫页面控制器)。
  6. Handler执行完成返回ModelAndView
  7. HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
  11. DispatcherServlet响应用户。

24、Spring中BeanFactory和ApplicationContext的联系和区别?

BeanFactory 是Spring中最基本、通用的工厂,这里的工厂,其不仅仅是构造实例,它可以创建并管理各种类的对象,即是Spring IOC容器体系结构的基本接口,其与其子接口便构成了Spring IOC容器的体系结构(如下图:IOC容器主要接口关系图)。所以不要把它理解成常规意义的简单工厂,也可以把它称为Sring容器。

ApplicationContext 也是一个Spring容器,它是由BeanFactory接口派生而来,因而提供BeanFactory所有的功能,此外它还扩展了BeanFactory的功能,提供了更多的高级功能。

所以联系上它们本身都是Spring提供的接口,它们都和其子接口构成了Spring IOC的容器,我们一般都称他们为Spring容器。其中BeanFactory是Spring框架的基础设施,面向Spring本身,其提供基本的功能,如:getBean(),;ApplicationContext是建立在它之上,面向使用者,扩展了更多面向应用的功能,更易于创建实际应用,实际也基本使用它。

区别上:由上述可知他们提供的功能是有不同的,体系结构、用途等有所不同。另外他们在初始化时有一个重大的区别:BeanFactroy初始化容器时,并未初始化Bean,直到第一次在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化(延迟加载),这样的话,我们就不能在初始化容器时发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,就一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。

25、Spring注入的几种方式?

  • 构造方法注入
  • setter注入
  • 基于注解的注入

26、Spring如何实现事物管理的?

在 Spring 中管理事务会涉及到这几个属性,事务隔离级别、是否只读、事务的传播行为,说到事务的传播行为,指的就是不同的业务方法之间相互调用时,应该如何管理事务。

Spring的事务管理机制实现的原理,就是通过这样一个动态代理对所有需要事务管理的Bean进行加载,并根据配置在invoke方法中对当前调用的 方法名进行判定,并在method.invoke方法前后为其加上合适的事务管理代码,这样就实现了Spring式的事务管理。

27、Spring的AOP底层是怎么实现的?

spring的AOP底层是由 JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)实现。

28、SpringIOC、spring AOP的理解?

IOC (Inversion of Control,控制反转)是spring的核心,贯穿始终。所谓IOC,对spring框架来说,就是由spring来控制对象的生命周期和对象间的依赖关系。

传统模式:对象间的依赖关系由对象自己控制,代码耦合度高。

IOC开发模式:对象间的依赖关系由spring容器来控制,解耦。

AOP 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

主要功能是:日志记录、性能统计、安全控制、事务处理、异常处理等等。

29、SpringMVC、SpringBoot、SpringCloud的区别?

spring和springMvc:

spring
springMvc

springMvc和springBoot:

springMvc
springBoot

springBoot和springCloud:

springboot
springcloud

30、Dubbo的工作流程?

  1. 服务容器 Container 负责启动加载运行服务提供者Provider。根据Provider配置的文件根据协议发布服务 , 完成服务的初始化。
  2. Provider 在启动时,根据配置中的Registry地址连接Registry,将Provider的服务信息发布到Registry,在Registry注册自己提供的服务。
  3. Consumer 在启动时,根据消费者XML配置文件中的服务引用信息,连接到Registry,向Registry订阅自己所需的服务。
  4. Registry 根据服务订阅关系,返回Provider地址列表给Consumer,如果有变更,Registry会推送最新的服务地址信息给Consumer。
  5. Consumer 调用远程服务时,会根据路由策略,先从缓存的Provider地址列表中选择一台进行,跨进程调用服务,假如调用失败,再重新选另一台调用。
  6. 服务 ProviderConsumer ,会在内存中记录调用次数和调用时间,每分钟发送一次统计数据到Monitor。

31、dubbo的集群容错策略?

Failover Cluster:失败重试

当服务消费方调用服务提供者失败后自动切换到其他服务提供者服务器进行重试。这通常用于读操作或者具有幂等的写操作,需要注意的是重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

Failfast Cluster:快速失败

当服务消费方调用服务提供者失败后,立即报错,也就是只调用一次。通常这种模式用于非幂等性的写操作。

Failsafe Cluster:失败安全

当服务消费者调用服务出现异常时,直接忽略异常。这种模式通常用于写入审计日志等操作。

Failback Cluster:失败自动恢复

当服务消费端用服务出现异常后,在后台记录失败的请求,并按照一定的策略后期再进行重试。这种模式通常用于消息通知操作。

Forking Cluster:并行调用

当消费方调用一个接口方法后,Dubbo Client会并行调用多个服务提供者的服务,只要一个成功即返回。这种模式通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

Broadcast Cluster:广播调用

当消费者调用一个接口方法后,Dubbo Client会逐个调用所有服务提供者,任意一台调用异常则这次调用就标志失败。这种模式通常用于通知所有提供者更新缓存或日志等本地资源信息。

32、dubbo服务负载均衡策略?

Random LoadBalance:随机策略

按照概率设置权重,比较均匀,并且可以动态调节提供者的权重。

RoundRobin LoadBalance:轮询策略

轮询,按公约后的权重设置轮询比率。会存在执行比较慢的服务提供者堆积请求的情况,比如一个机器执行的非常慢,但是机器没有挂调用(如果挂了,那么当前机器会从Zookeeper的服务列表删除),当很多新的请求到达该机器后,由于之前的请求还没有处理完毕,会导致新的请求被堆积,久而久之,所有消费者调用这台机器上的请求都被阻塞。

LeastActive LoadBalance:最少活跃调用数

如果每个提供者的活跃数相同,则随机选择一个。在每个服务提供者里面维护者一个活跃数计数器,用来记录当前同时处理请求的个数,也就是并发处理任务的个数。所以如果这个值越小说明当前服务提供者处理的速度很快或者当前机器的负载比较低,所以路由选择时候就选择该活跃度最小的机器。如果一个服务提供者处理速度很慢,由于堆积,那么同时处理的请求就比较多,也就是活跃调用数目越大,这也使得慢的提供者收到更少请求,因为越慢的提供者的活跃度越来越大。

ConsistentHash LoadBalance:一致性Hash策略

一致性Hash,可以保证相同参数的请求总是发到同一提供者,当某一台提供者挂了时,原本发往该提供者的请求,基于虚拟节点,平摊到其他提供者,不会引起剧烈变动。

33、RocketMQ集群的工作流程?

  1. 启动Namesrv,Namesrv起来后监听端口,等待Broker、Produer、Consumer连上来,相当于一个路由控制中心。
  2. Broker启动,跟所有的Namesrv保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有topic信息。注册成功后,namesrv集群中就有Topic跟Broker的映射关系。
  3. 收发消息前,先创建topic,创建topic时需要指定该topic要存储在哪些Broker上。也可以在发送消息时自动创建Topic。
  4. Producer发送消息,启动时先跟Namesrv集群中的其中一台建立长连接,并从Namesrv中获取当前发送的Topic存在哪些Broker上,然后跟对应的Broker建长连接,直接向Broker发消息。
  5. Consumer跟Producer类似。跟其中一台Namesrv建立长连接,获取当前订阅Topic存在哪些Broker,然后直接跟Broker建立连接通道,开始消费消息。

34、为什么要使用mq以及优缺点?

优点:

  • 异步:如果平均一个业务操作要耗时500ms,那么十个操作将近5秒才能执行完成,如果采用mq,发送消息到mq后直接返回用户,再由消息队列去处理这些业务逻辑,那么请求将<500ms,用户几乎无感知。
  • 解耦:假设现在有三个系统,A->B->C,采用mq的话只需要单独发三个请求到每个系统,即使以后再增加D系统也是非常轻松解决。
  • 削峰填谷:如果一时间请求的用户量非常大,已经超出了系统所有承受的最大并发访问量,这时候我们就可以用mq来削弱峰值,解决服务器宕机。

缺点:

  • 系统可用性降低:多了一个风险因素,mq可能会挂掉,造成数据打丢失。
  • 系统复杂度提高:需要一定的学习成本,加入mq之后,还要考虑消息丢失、重复消息、消费顺序等一系列的问题,并要解决这些问题。
  • 数据一致性问题:mq成功发送完消息给多个子系统,需要保证数据的幂等性

35、国内主流mq的对比?

国内采用的MQ有: ActiveMQKafkaRabbitMQRocketMQ ,但现在 ActiveMQ 用的越来越少了。主要做其他三种的调研。

kafka RabbitMQ RocketMQ
开发语言 Scala开发 Erlang开发 java开发
性能 吞吐量:吞吐量所有MQ里最优秀,QPS十万级、性能毫秒级、支持集群部署 吞吐量比较低,QPS几万级、性能u秒级、主从架构 吞吐量高,QPS十万级、性能毫秒级、支持集群部署
功能 功能单一 功能单一 支持各种高级功能,比如说延迟消息、事务消息、消息回溯、死信队列、消息积压等等
缺点 丢数据, 因为数据先写入磁盘缓冲区,未直接落盘。机器故障会造成数据丢失 Erlang小众语言开发,吞吐量低,集群扩展麻烦 官方文档相对简单可能是RocketMQ目前唯一的缺点
应用场景 适当丢失数据没有关系、吞吐量要求高、不需要太多的高级功能的场景,比如大数据场景 中小公司对并发和吞吐量要求不高的场景 适当丢失数据没有关系、吞吐量要求高、不需要太多的高级功能的场景,比如大数据场景

36、Zookeeper和Redis如何选举Leader?

zookeeper默认是采用投票数大于半数则胜出的逻辑。

选举状态

  • LOOKING,竞选状态。
  • FOLLOWING,随从状态,同步leader状态,参与投票。
  • OBSERVING,观察状态,同步leader状态,不参与投票。
  • LEADING,领导者状态。

目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:

  1. 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking。
  2. 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
  3. 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
  4. 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
  5. 服务器5启动,后面的逻辑同服务器4成为小弟。

当redis集群的主节点故障时,Sentinel集群将从剩余的从节点中选举一个新的主节点。

选举过程

  1. 主观下线,Sentinel集群的每一个Sentinel节点会定时对redis集群的所有节点发心跳包检测节点是否正常。如果一个节点在down-after-milliseconds时间内没有回复Sentinel节点的心跳包,则该redis节点被该Sentinel节点主观下线。
  2. 客观下线,当节点被一个Sentinel节点记为主观下线时,并不意味着该节点肯定故障了,还需要Sentinel集群的其他Sentinel节点共同判断为主观下线才行,该Sentinel节点会询问其他Sentinel节点,如果Sentinel集群中超过quorum数量的Sentinel节点认为该redis节点主观下线,则该redis客观下线,如果客观下线的redis节点是从节点或者是Sentinel节点,则操作到此为止,没有后续的操作了;如果客观下线的redis节点为主节点,则开始故障转移,从从节点中选举一个节点升级为主节点。
  3. Sentinel集群选举Leader,如果需要从redis集群选举一个节点为主节点,首先需要从Sentinel集群中选举一个Sentinel节点作为Leader。每一个Sentinel节点都可以成为Leader,当一个Sentinel节点确认redis集群的主节点主观下线后,会请求其他Sentinel节点要求将自己选举为Leader。被请求的Sentinel节点如果没有同意过其他Sentinel节点的选举请求,则同意该请求(选举票数+1),否则不同意。如果一个Sentinel节点获得的选举票数达到Leader最低票数(quorum和Sentinel节点数/2+1的最大值),则该Sentinel节点选举为Leader;否则重新进行选举。
  4. Sentinel Leader决定新主节点,当Sentinel集群选举出Sentinel Leader后,由Sentinel Leader从redis从节点中选择一个redis节点作为主节点:
  5. 过滤故障的节点
  6. 选择优先级slave-priority最大的从节点作为主节点,如不存在则继续
  7. 选择复制偏移量(数据写入量的字节,记录写了多少数据。主服务器会把偏移量同步给从服务器,当主从的偏移量一致,则数据是完全同步)最大的从节点作为主节点,如不存在则继续
  8. 选择runid(redis每次启动的时候生成随机的runid作为redis的标识)最小的从节点作为主节点

37、memcached和redis的区别?

  1. Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等。 
  2. Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。 
  3. 虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘 
  4. 过期策略–memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10 
  5. 分布式–设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从 
  6. 存储数据安全–memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化) 
  7. 灾难恢复–memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复 
  8. Redis支持数据的备份,即master-slave模式的数据备份。

38、Redis常见性能问题和解决方案?

  1. Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
  2. 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
  3. 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
  4. 尽量避免在压力很大的主库上增加从库
  5. 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…

这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

39、Memcache与Redis的比较?

  1. memcache把数据存在内存之中,断电后会挂掉;Redis部分数据持久化在硬盘上,断电不会丢失。
  2. memcache存的是key-value对,redis支持更多的数据结构和数据类型
  3. memcache可以使用一致性hash做分布式,redis可以做主从同步
  4. redis单线程,只使用1个cpu

40、如何实现Redis的分片?

使用一致性哈希对数据进行映射

实现方式:客户端分片(每个客户端对应一个分片)、代理协助分片、查询路由分片;

使用redis集群(依赖zookeeper);

41、简单介绍红黑树与二叉树的区别?

红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单。

平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知。

42、单链表和双链表的区别?

占用空间不同

  • 单链表只存储指向下一个结点的指针
  • 双链表既有指向下一个结点的指针,也有指向上一个结点的指针

适用情况不同

  • 单向链表更适合元素的增加与删除
  • 双向链表更适合元素的查询工作

读取方向不同

  • 单链表只能单向读取
  • 双链表可以双向读取

43、使用随机算法产生一个数,要求把1-1000W之间这些数全部生成。

public static void main(String[] args) {
    int size = 10000000;
    System.out.println("执行a方案:");
    a(size);
    System.out.println("执行b方案:");
    b(size);
}
public static void a(int size){
    long time = System.currentTimeMillis();
    Set<Integer> result = new HashSet<>(size);
    Random random = new Random();
    while (result.size() < size) {
        int i = random.nextInt(size);
        result.add(i);
    }
    System.out.println("执行耗时 : " + (System.currentTimeMillis() - time) / 1000f + " 秒 ");
}
public static void b(int size){
    long time = System.currentTimeMillis();
    int[] list = new int[size];
    for(int i = 0; i < size; i++){
        list[i] = i;
    }
    Random random = new Random();
    int[] result = new int[size];
    for(int i = 0; i < size; i++){
        int index = random.nextInt(size - i);
        result[i] = list[index];
        list[index] = list[size - i - 1];
    }
    System.out.println("执行耗时 : " + (System.currentTimeMillis() - time) / 1000f + " 秒 ");
}

2020常见JAVA面试题整理(进阶)

44、两个有序数组的合并排序。

public static void main(String[] args) {
    int[] a = {2,5,6};
    int[] b = {3,7,9};
    int[] c = mergeSorted(a, b);
    System.out.println("排序后:" + Arrays.toString(c));
}
public static int[] mergeSorted(int[] a, int[] b){
    int an = 0;
    int bn = 0;
    int[] c = new int[a.length + b.length];
    for (int i = 0; i < c.length; i++) {
        if (an < a.length && bn < b.length) {
            if (a[an] > b[bn]) {
                c[i] = b[bn];
                bn++;
            } else {
                c[i] = a[an];
                an++;
            }
        } else if (an < a.length) {
            c[i] = a[an];
            an++;
        } else if (bn < b.length) {
            c[i] = b[bn];
            bn++;
        }
    }
    return c;
}

2020常见JAVA面试题整理(进阶)

45、常用的linux下的命令。

cd 命令用来改变所在目录

ls 命令用来查看目录的内容

cp 命令用来拷贝文件cp

mv 命令把文件移动到另一个目录

mkdir 命令创建目录

tar 命令用于压缩解压

vi/vim 是使用vi编辑器的命令

cat 这个命令常用来显示文件内容

wget 是从远程下载的工具

ifconfig 命令用来查看和配置网络设备

ps 命令显示运行的进程

kill 命令用于终止进程

free 命令用于显示Linux系统内存

top 命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况

原文  https://segmentfault.com/a/1190000022599892
正文到此结束
Loading...