Java开发框架和中间件面试题(10)

目录

104.怎么保证缓存和数据库数据的一致性?

105.什么是缓存穿透,什么是缓存雪崩?怎么解决?

106.如何对数据库进行优化?

107.使用索引时有哪些原则?

108.存储过程如何进行优化?

109.说说如何对Tomcat进行优化?

110.BIO、NIO和AIO的区别?

111.Netty的特点?

112.Netty的线程模型?

113.如何进行JVM性能调优?

115.GC调优策略有哪些?

116.介绍下单点登录?

117.RabbitMQ的使用场景有哪些?

118.RabbitMQ有哪些重要的角色?有哪些重要的组件?

119.RabbitMQ中vhost的作用是什么?

120.介绍下RabbitMQ的架构?

121.RabbitMQ中的交换机类型有哪些?

122.除了ReetrantLock,你还接触过JUC并发包中的哪些并发API?

123.你了解哪些负载均衡算法、策略?

[124.如何设计符合幂等性的高质量Restful API ?](#124.如何设计符合幂等性的高质量Restful API ?)

[125.如何理解Restful API 的幂等性?](#125.如何理解Restful API 的幂等性?)

126.jsp和servlet有什么区别?

127.forward和redirect的区别?

128.jsp有哪几个作用域?

129.session和cookie有什么区别?

130.如果客户端禁止cookie能实现session还能用吗?


104.怎么保证缓存和数据库数据的一致性?

1.淘汰缓存在;如果是较为复杂的数据时,进行缓存的更新操作就会变得异常复杂,因此一般推荐选择淘汰缓存,而不是更新缓存。

2.选择先淘汰缓存,再更新数据库,加入先更新数据库在淘汰缓存,如果淘汰缓存失败,那么后面的请求就会得到脏数据,直至缓存过期。加入先淘汰缓存再更新数据库,如果更新数据库失败,只会产生一次缓存穿透,相比较而言,后者对业务则没有本质上的影响。

3.延时双删除策略,如下场景:同时有一个请求A进行更新操作,另一个请求B进行查询操作。我们按照如下步骤执行:

(1)请求A进行写操作,删除缓存

(2)请求B查询发现缓存不存在

(3)请求B去数据库查询得到旧值

(4)请求B将旧值写入缓存

(5)请求A将新值写入数据库,次数便出现了数据不一致问题,此时我们可以采用延时双删策略的已解决。

java 复制代码
public  void  write(String key,Object data){
  redisUtils.del(key);
  db.update(data);
  Thread.Sleep(100);
  redisUtils.del(key);
}

这么做,可以将1秒内所造成的缓存脏数据,再次删除。这个时间设定可以根据业务场景进行一个调节。

4.数据库读写分离的场景

假如有如下场景:两个请求,一个请求A进行更新操作,另一个请求B进行查询操作。我们按照如下步骤执行:

1.请求A进行写操作,删除缓存

2.请求A将数据写入数据库了

3.请求B查询缓存发现,缓存没有值

4.请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值。

5.请求B将旧值写入缓存

6.数据库完成主从同步,从库变为新值,依旧采用延时双删策略解决此问题。

105.什么是缓存穿透,什么是缓存雪崩?怎么解决?

1.缓存穿透:一般的缓存系统,都是按照key去缓存查询,如果不存在对用的value,就应该去后端系统去查找(比如DB数据库)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

2.怎么解决?

对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert之后清理缓存。对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的bitmap中,查询时通过该bitmap过滤。

3.缓存雪崩:当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,会给后端系统带来大量的压力,导致系统崩溃。

4.如何解决?在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待;做二级缓存;不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀。

106.如何对数据库进行优化?

1.选取适合的字段属性

为了获取更好的性能,可以将表中字段的宽度设的尽可能小。

尽量把字段设置成not null

执行查询的时候,数据库不用去比较null值

对某些省份或者性别字段,将他们定义为enum类型

enum类型被当做数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快很多。

2.使用join链接代替子查询

3.使用联合union来代替手动创建的临时表union用法中,两个select语句的字段类型要匹配,而且字段个数要相同。

5.锁定表:尽管事务时维护数据库完整性的一个非常好的方法,但却因为他的独占性,有时候会影响数据库的性能,尤其是在大应用中。由于在事务执行的过程中,数据库会被锁定,因此其他用户只能暂时等待直到数据库结束。有的时候可以用锁定表的方法获得更好的性能。

共享锁:

其他用户只能看,不能修改lock table person in share mode;对于通过lock table命令主动添加的锁来说,如果要释放他们,只需发出rollback命令即可。

6.使用外键:锁定表的方法可以维护数据的完整性,但是他却不能保证数据的关联性,这个时候可以使用外键。

7.使用索引:索引时提高数据库查询速度的常用方法,尤其是查询语句中包含max(),min(),order by这些命令的时候,性能提高更为显著。一般来说索引应该建在常用于join,where,order by的字段上。尽量不要对数据库中含有大量重复的值得字段建立索引。

8.优化的查询语句:在索引的字段上尽量不要使用函数进行操作。尽量不要使用like关键字和通配符,这样做法很简单,但却是以牺牲性能为代价的。避免在查询中进行自动类型转换,因为类型转换也会使索引失效。

107.使用索引时有哪些原则?

常见的索引原则有:

1.选择唯一性索引

唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。

2.为经常需要排序,分组和联合操作的字段建立索引;

3.为常作为查询条件的字段建立索引。

4.限制索引的数据:越多的索引,会使更新表变得很浪费时间。

5.尽量使用数据量少的索引。

6.如果索引的值很长,那么查询的速度会受到影响。

7.尽量使用前缀来索引。

8.如果索引字段的值很长,最好使用值的前缀来索引。

9.删除不再使用或者很少使用的索引。

10.最左前缀匹配原则,非常重要的原则。

11.尽量选择区分度高的列作为索引。

12.索引列不能参与计算,保持列干净:但函数的查询不参与索引。

13.尽量的扩展索引,不要新建索引。

108.存储过程如何进行优化?

存储过程是一组为了完成特定功能的SQL语句集,存储在数据库中,经过一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行他。存储过程是数据库中的一个重要对象。

存储过程优化思路:

1.尽量利用一些SQL语句来替代一些小循环,例如聚合函数,求平均函数等。

2.中间结果存放于临时表,加索引。

3.少使用游标。SQL是个集合语言,对于集合运算具有较高性能。而cursors是过程运算。比如一个100万行的数据进行查询。游标需要读取100万次,而不使用游标只需要少量几次读取。

4.事务越短越好。SqlServer支持并发操作。如果事务过多过长,或者隔离级别过高,都会造成并发操作的阻塞,死锁。导致查询极慢,CPU占用率极低。

5.使用try catch处理错误异常

6.查找语句尽量不要放在循环内。

109.说说如何对Tomcat进行优化?

Tomcat作为Web服务器,他的处理性能直接关系到用户体验,下面是几种常见的优化措施。

1.去掉对web.xml的监视,把jsp提前编辑成Servlet。有抚育物理内存的情况,加大Tomcat使用的JVM的内存。

2.服务器资源

服务器所能提供CPU,内存,硬盘的性能对处理能力有决定性影响。

2.1对于高并发情况下会有大量的运算,那么CPU的速度会直接影响到处理速度。

2.3硬盘主要问题就是读写性能,当大量文件进行读写时,硬盘极容易称为性能瓶颈。最好的办法还是利用下面提到的缓存。

3.利用缓存和压缩

对于静态页面最好是能够缓存起来,这样就不必每次从磁盘上读。这里我们采用了Nginx作为缓存服务器,将图片,css,js文件都进行了缓存,有效的减少了后端Tomcat的访问。

另外,为了能加快网络传输速度,开启gzip压缩也是必不可少的。但考虑带Tomcat已经需要处理很多东西了,所以把这个压缩的工作就交给前端的Nginx来完成。

除了文本可以用gzip压缩,其实很多图片也可以用图像处理工具预先进项压缩,找到一个平衡点可以让画质损失很小而文件可以减小很多。曾静我就见过一个图片从300多kb压缩到几十kb,自己几乎看不出来区别。

4.采用集群

单个服务器性能总是有限的,最好的办法自然是实现横向扩展,那么组建Tomcat集群是有效提升性能的手段。我们还是采用了Nginx来作为请求分流的服务器,后端多个Tomcat共享session来协同工作。

5.优化Tomcat参数

这里以Tomcat7的参数配置为例,需要修改conf/server.xml文件,主要是优化连接配置,关闭客户端dnd查询。

110.BIO、NIO和AIO的区别?

1.基本概念

BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。

伪异步IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源。

NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程处理。

AIO:一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

2.常规区别

BIO是面向流的,NIO是面向缓冲区的;

BIO的各种流是阻塞的。

而NIO是非阻塞的。

BIO的Stream是单向的,而NIO的channel是双向的。NIO特点:事件驱动模型,单线程处理多任务,非阻塞I/O,I/O读写不再阻塞,而是返回0,基于block的传输比基于流的传输更高效,更高级的IO函数zero copy,IO多路复用大大提高了Java网络应用的可伸缩性和实用性。

基于Reactor线程模型。在Reactor模式中,事件分发器等待某个事件或者可应用或者某个操作的状态发生,时间分发器就把这个事件事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。

如在Reactor中实现读:注册读就绪事件和相应的事件处理器,事件分发器等待事件,事件到来,激活分发器,分发器调用事件对应的处理器,事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

111.Netty的特点?

一个高性能,异步事件驱动的NIO框架,他提供了对TCP,UDP和文件传输的支持使用更高效的socket底层,对epoll空轮询引起的CPU占用飙升在内部进行了处理,避免了直接使用NIO的陷阱,简化了NIO的处理方式。

采用多种decoder/encoder支持,对TCP粘包/分包进行自动化处理,可使用接受/处理线程池,提高连接效率,对重连,心跳检测的简单支持。

可以配置IO线程数,TCP参数,TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存池的方式循环利用ByteBuf通过引用计数器及时申请释放不再引用对象,降低了GC频率使用单线程串行化的方式,高效的Reactor线程模型大量使用了volitale,使用了CAS和原子类,线程安全类的使用,读写锁的使用。

112.Netty的线程模型?

Netty通过Reacor模型基于多路复用器接收并处理用户请求,内部实现了了两个线程池,boss线程池和work线程池。其中boss线程池的线程负责处理请求的accept,当接收到accept事件的请求时,把对应的socket封装到一个NIOSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。

单线程模型:所有I/O操作都有一个线程完成,即多路复用,事件分发和处理都是在一个Reactor线程上完成的。既要接受客户端的连接请求,向服务器端发起连接,又要发送/读取请求或者应答/响应消息。

一个NIO线程同时处理成百上千的链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载,大并发的应用场景不合适。

多线程模型:有一个NIO线程(Acceptor只负责监听服务器,接受客户端的TCP连接请求,NIO线程池负责网络IO的操作,即消息的读取,解码,编码和发送。

1个NIO线程可以同时处理N条链路,但是一个链路只对应1个NIO线程,这是为了防止发生并发操作问题。但在并发百万客户端连接或者需要安全认证时,一个Acceptor线程可能会存在性能不足问题。

主从多线程模型:Acceptor线程用于绑定监听端口,接收客户端连接,将SocketChannel从主线程池的Reactor线程的多路复用器上移除,用于处理I/O的读写等操作,从而保证mainReactor只负责接入认证,握手等操作。

113.如何进行JVM性能调优?

1.监控GC的状态,使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分GC执行时间,觉得是否进行优化。

举一个例子:系统崩溃前的一些现象:

每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4,5s

FullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC

年老代的内存越来越大并且每次FullGC后年老代没有内存被释放之后系统就会无法响应新的请求,逐渐到达OutOfMemoryError的临界值,这个时候就需要分析JVM内存快照dump。

2.生成堆的dump文件。

通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。

3.分析dump文件打开这个3g的堆信息文件,显然一般的Window系统没有那么大内存,必须借助高配置的Linux,几种工具打开该文件:

Visual VM

IBM HeapAnalyzer

JDK自带的Hprof工具

Mat(Eclipse专门的静态内存分析工具)推荐使用

备注:文件太大,建议使用Eclipse专门的静态内存分析工具Mat打开分析。

4.分析结果,判断是需要优化,如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化,如果GC事件超过1到3秒,或者频繁GC,则必须优化。

注:如果满足下面的指标,则一般不需要进行GC:

Minor GC执行时间不到50毫秒;

Minor GC执行不频繁,约10秒一次;

FullGC执行时间不到1秒;

FullGC执行频率不算频繁,不低于10分钟一次;

5.调整GC类型和内存分配,如果内存分配过大或者过下,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找一台或者几台机器进行beat,然后比较优化过的机器和没有优化的机器性能对比,并有针对性的做出最后选择。

6.不读拿的分析和调整,通过不断地试验和试错,分析并找到最合适的参数,如果找到了最合适的参数,则将这些参数应用到所有的服务器。

114.JVM调优参数有哪些?

115.GC调优策略有哪些?
116.介绍下单点登录?
117.RabbitMQ的使用场景有哪些?

1.解决一步问题,例如用户注册,发送邮件和短信反馈注册成功,可以使用RabbitMQ消息队列,用户无需等待反馈。

2.服务间解耦,订单系统和库存系统,中间加入RabbitMQ消息队列,当库存系统出现问题时,订单系统依旧能正常使用,降低服务间耦合度。

3.秒杀系统,利用RabbitMQ的最大值,实现秒杀系统。

118.RabbitMQ有哪些重要的角色?有哪些重要的组件?

1.RabbitMQ有哪些重要角色?客户端,RabbitMQ服务端。

2.有哪些重要组件?

2.1connectionFactory(连接管理器)应用程序与RabbitMQ之间建立连接的管理器。

2.2Channel(信道)消息推送使用的信道。

2.3RoutingKey(路由键)用于把生产者的数据分配到交换机上。

2.4Exchange(绑定键)用于把交换机的消息绑定到队列上

2.6Queue(队列)用于存储生产者消息。

119.RabbitMQ中vhost的作用是什么?

vhost可以理解为mini版的RabbitMQ,其内容均含有独立的交换机,绑定,队列,最重要的是拥有独立的权限系统,可以做到vhost范围内的用户控制。

用RabbitMQ全局考虑,不同的应用可以跑在不同的vhost上,作为不同权限隔离的手段。

120.介绍下RabbitMQ的架构?
121.RabbitMQ中的交换机类型有哪些?
122.除了ReetrantLock,你还接触过JUC并发包中的哪些并发API?
123.你了解哪些负载均衡算法、策略?
124.如何设计符合幂等性的高质量Restful API ?
125.如何理解Restful API 的幂等性?
126.jsp和servlet有什么区别?
127.forward和redirect的区别?
128.jsp有哪几个作用域?
129.session和cookie有什么区别?
130.如果客户端禁止cookie能实现session还能用吗?
相关推荐
Swift社区2 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht2 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht2 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20243 小时前
Swift 数组
开发语言
吾日三省吾码3 小时前
JVM 性能调优
java
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc4 小时前
《Python基础》之字符串格式化输出
开发语言·python
弗拉唐4 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi775 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器