java架构师面试题

menu

数据     2 jvm    3 redis    4 Mq      5 springcloud     6 系统解决方案    7 多线程     8 网络

 

数据库

为什么说B+比B树更适合实际应用中操作系统的文件索引和数据库索引?

1、B+的磁盘读写代价更低。

B+的内部结点并没有指向关键字具体信息的指针,因此其内部结点相对B树更小。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

2、B+-tree的查询效率更加稳定。

所有的记录都存储在叶子节点上面。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3、叶子节点都是有序链表存储的,更利于范围存储

 

MySQL InnoDB、Mysaim的特点?   

Innodb是聚集索引,支持事务,支持外键 ,支持行锁,必须有唯一索引,适合修改删除多的,回表查询指的是innodb里面非聚集索引的查询。

Myisam是非聚集索引,叶子节点保存指向数据的指针。不支持事务。

 

Mysql默认隔离级别

    可重复读(RR),由于之前mysql的binlog同步模式为statement,RC会产生数据不一致。当前binlog已经支持row的格式,可以选择RC+ROW,作为mysql的隔离级别和日记格式

 

数据库隔离级别是什么?

A原子性(undo log)、C一致性(AID来保证)、I隔离性(加锁、mvcc)、D持久性(redo log)

读已提交(脏读)、可重复读(读未提交)、序列化(幻读)

 

MySQL主备同步的基本原理。 当slave连接到master的时候,master机器会为slave开启binlog dump线程。当master 的 binlog发生变化的时候,binlog dump线程会通知slave,并将相应的binlog内容发送给slave

 

如何优化数据库性能(索引、分库分表、批量操作、分页算法、升级硬盘SSD、业务优化、主从部署)

 

SQL什么情况下不会使用索引( select *、索引列上有函数、字段类型错误、like%、or、not in、not exist、order by 不加where,加了limit)

exists和in   如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in

 

MVCC

InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读

通过readview 机制和 undo log 实现了读不被阻塞,而且在一定程度上实现RC和RR,并且RR级别在大部分时候可以解决幻读

 快照读(select ...)的幻读是用MVCC解决的,当前读(select ... for update; update; delete)的幻读是用间隙锁解决的。

 

数据库事务的几种粒度

表锁、行锁(通过索引来加锁)只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁

 

如何解决高并发减库存问题

如何从一张表中查出name字段不包含“XYZ”的所有行?

not in where name LIKE "%XYZ%"

 

mysql主从同步延时分析  1.业务的持久化层的实现采用分库架构,mysql服务可平行扩展,分散压力。

2.单个库读写分离,一主多从,主写从读,分散压力。这样从库压力比主库高,保护主库。

3.服务的基础架构在业务和mysql之间加入memcache或者redis的cache层。降低mysql的读压力。

4.使用比主库更好的硬件设备作为slave,mysql压力小,延迟自然会变小。

 

加锁原则

拿MySql的InnoDB引擎来说,对于insert、update、delete等操作。会自动给涉及的数据加排他锁;

对于一般的select语句,InnoDB不会加任何锁,事务可以通过以下语句给显示加共享锁或排他锁。

共享锁:SELECT ... LOCK IN SHARE MODE;

排他锁:SELECT ... FOR UPDATE;

 

索引的优缺点:优点,提高查询速度;缺点,更新数据时效率低,因为要同时更新索引

 

 

 

 

Spring Cloud

Eureka的源码分析服务注册和服务发现以及心跳机制和保护机制,对比eureka与zookeeper,什么是CAP原则?

服务注册,客户端启动后向服务端发送一个心跳,如果返回404,证明未注册,发起注册

服务发现  客户端定时拉去服务列表,保存在本地,使用时调用本地

心跳机制  客户端每30s会向服务端发送一个心跳,如果90s没有收到心跳,服务端会剔除客户端

自我保护机制  服务端在发现一定时间内剔除的节点过多,就会触发自我保护,不再剔除节点

 

Eureka支持AP  1、自我保护机制 2、server端低位平等,只要一个server存在就可以正常工作

Zookeeper支持CP  哨兵模式,需要选主,选举过程停止服务,中心化思想

Nacos

 

Ribbon源码分析和客服端负载均衡,客户端负载均衡?服务端负载均衡? Ribbon核心组件IRule以及重写IRule

客户端负载均衡,Ribbon实现IRule接口来自定义负载均衡策略

 

Fegin源码分析和声明式服务调用,Fegin负载均衡,Fegin如何与Hystrix结合使用? 有什么问题?

给feigin类写一个实现类fallback,里面定义一些方法,在feigin调用的时候,在类上引入fallback

 

Hystrix实现服务限流、降级,大型分布式项目服务雪崩如何解决? 服务熔断到底是什么?一线公司的解决方案

服务降级:返回一个友好提示,场景:程序运行异常、超时、服务熔断、服务限流

服务熔断:当失败达到一定阈值之后,停止服务调用,直接降级。等待一个时间窗口之后,继续重试

服务限流:采用线程池和信号量的方式对服务调用进行限流

 

Zuul统一网关详解、服务路由、过滤器使用等,从源头来拦截掉一些不良请求

路由 : 网关的基本模块,有ID,目标URI,一组断言和一组过滤器组成

断言:就是访问该路由的访问规则,可以用来匹配来自http请求的任何内容,例如headers或者参数

过滤器:这个就是我们平时说的过滤器,用来过滤一些请求的,gateway有自己默认的过滤器,我们也可以自定义过滤器,但是要实现两个接口,ordered和globalfilter

 

分布式链路跟踪详解,串联调用链,,让Bug无处可藏,如何厘清微服务之间的依赖关系?如何跟踪业务流的处理顺序?

 

Redis

2、 持久化:RDB、AOF

3、 缓存击穿:1、查询之后将数据刷新到redis中 2、避免一些热点数据在同一时间失效、

      缓存雪崩:同时失效或者redis挂掉。1、分散过期时间,2、通过hystrix进行访问降级或者限流 3、redis进行分布式部署

      缓存穿透:1、增加查询限制条件  2、在特殊值加载到缓存里面  3布隆过滤器

5、 使用场景:1、会话缓存 2、热点数据缓存 3、搜索历史记录 4、分布式锁

6、单线程,I/O多路复用,  通过数组的方式同时处理多个I/O

7、高可用方案:主从、哨兵、cluster(hash槽)https://blog.csdn.net/u014209205/article/details/82113258

8、redis如何实现多写入,通过事务中添加watch来检测,要修改的key是否已经被其他客户端修改

9、redis脑裂:出现了两个master节点,clientA继续往旧的master写入,旧master降级成为slave节点,会进行全量同步,会把clientA写入的数据清空。解决方案:参数配置,min-slave-to-write 2、min-slaves-max-lag 5:至少2个slave对master的同步复制延迟不能超过5秒,如果达不到要求,master停止接受请求

10、redis主从节点是长连接

11、redis如何判断某个节点是否正常工作?通过心跳检测机制,主节点10秒发送一次心跳检测从节点 ,从节点1秒发送一次心跳给主节点同步自己的复制偏移量

12、主从如何判断全量同步还是增量同步?从节点slave到主节点之后,如果是首次直接全量同步,如果存在偏移量,比较偏移量和主节点的缓冲区大小,如果比缓存区小,那么可以进行增量同步,不然进行全量同步

13、redis不同数据类型的作用

  • String:缓存,限流,计数器,分布式锁,分布式Session
  • Hash:储存用户信息,用户主页访问量,组合查询
  • List:微博关注人时间轴列表,简单队列
  • Set:赞,踩,标签,好友关系
  • Zset:排行榜

14.Redis为什么这么快?官方使用的基准测试结果表明,单线程的Redis可以达到10W/S的吞吐量。

a.基于内存操作:Redis的所有数据都存在内存中,因此所有的运算都是内存级别的,所以它的性能比较高。

b.数据结构简单:Redis的数据结构比较简单,是为Redis专门设计的,而这些简单的数据结构的查找和操作的时间复杂度都是O(1)。

c.多路复用和非阻塞IO:Redis使用IO多路复用功能来监听多个socket连接的客户端,这样就可以使用一个线程来处理多个情况,从而减少线程切换带来的开销,同时也避免了IO阻塞操作,从而大大提高了Redis的性能。

d.避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生。

Redis在4.0之前单线程依然快的原因:基于内存操作、数据结构简单、IO多路复用和非阻塞IO、避免了不必要的线程上下文切换。并且在Redis4.0开始支持多线程,主要体现在大数据的异步删除方面,例如:unlink key、flushdb async、flushall async等。而Redis6.0的多线程则增加了对IO读写的并发能力,用于更好的提升Redis的性能

 

 

多线程

1、notify唤醒哪一个线程?

notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。

notifyAll会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll方法。

2、如何停止一个线程的运行

stop()、interrupt() 设置一个标志位,在run方法内部进行判断。

https://www.nowcoder.com/discuss/740580?source_id=discuss_experience_nctrack&channel=-1

3、线程如何同、进程如何同步

 

 

Mq

1、Mq如何保证消息不丢失  : 1、生产者:采用confirm机制(同步、异步)    2、mq本身:消息持久化,可以结合confirm机制   3、消费者:关闭自动的ack

2、消息队列机制  https://blog.csdn.net/h2604396739/article/details/81136527

3、消息堆积:会阻塞生产者,必须提高消费端能力,还可以增加队列容量,

 

 

网络

tcp如何保证传输可靠性,tcp的报文头包含什么

https: 1、非对称+对称:先用非对称浏览器将对称秘钥传给服务器,之后服务器和浏览器通过对称加密传输    2、防止公钥被篡改,采用证书

 

JVM

java对象内存分布

1,对象头  2,实例数据    3,对齐填充字节

对象头:1,Mark Word    2,指向类的指针   3,数组长度(只有数组对象才有)

mark word :锁、分代年龄、gc标记

 JVM一般是这样使用锁和Mark Word的:

 1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。

 2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。

 3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。

 4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。

 5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。

 6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。

 7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

 

 2,指向类的指针

该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。Java对象的类数据保存在方法区。

 3,数组长度

只有数组对象保存了这部分数据。该数据在32位和64位JVM中长度都是32bit。

 

GCRoots的对象包括下面几种

(1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。

(2). 方法区中的类静态属性引用的对象。

(3). 方法区中常量引用的对象。

(4). 本地方法栈中JNI(Native方法)引用的对象。

sychronized底层实现逻辑、

volatile 内存屏障

 

系统方案

防重放:1、增加时间戳,请求超时后端不处理   2、请求携带uuid

大文件下载

token机制、spring注解原理、、cpu彪高处理、数据库慢排查方式、索引失效、CDN刷新不一致

 

 

posted @ 2022-01-02 14:57  chencaijie  阅读(2543)  评论(0编辑  收藏  举报