首页 > 【Fake面经】玩吧 社招 Java高级工程师面经
头像
李衿
编辑于 2020-06-27 22:18
+ 关注

【Fake面经】玩吧 社招 Java高级工程师面经

问题地址link:https://www.nowcoder.com/discuss/440390?channel=2000&source_id=discuss_center_discuss_interview

以下是mock回答:

玩吧 6月12号

1.分布式锁有了解吗

有了解,在项目中使用了分布锁

- 为什么使用分布式锁:

在业务系统中,底层业务接口可能会被顶层的多个业务流程并发使用,比如在淘系订单的流程当中,若一个流程是“取消该订单”,其子流程可能包含了

1. 查询订单状态,由一个单号查出对应的订单全量信息
2. 根据单据信息判断是否能进行取消
3. 向商家下发取消命令
4. 更改单据状态为取消
5. 发送取消信息回告上游

这几个流程,而此时如果另外一个流程也使用了“更改单据”的底层业务接口,若不加分布式锁,则可能在2判断完毕之后进行取消时,被更改了单据状态,比如从未付款变成了付款,此时再向商家下发取消则可能造成消费者钱款的损失

抽象的来讲,分布式锁可以让多个互斥流程不并发产生

- 锁粒度如何控制:

分布式锁的粒度一般由编码同学进行控制,一般而言对于一个实体的操作我们会加上对这个实体的分布式锁,比如如果对一张单据加锁,则锁的key设定为单据号2

- 你们分布式锁的失败策略是什么:

我们采用的失败策略是被调用方直接抛出异常快速失败,但是这个异常是一个声明为可重试的异常,上游识别到可重试异常之后会传导到最上游,然后可以通过rpc框架/消息队列/调度系统进行多次重试

- 如何实现的分布式锁:

使用redis进行实现,在考虑一个分布式锁的时候,我们一般需要支持如下几点

1. 可重入,防止同个线程串行调用相同key的方法时产生线程内死锁
2. 每个锁存在持有者,防止A锁了之后,突然机器io hang住,锁过期,然后b认为没有锁,对相同资源进行加锁,然后A醒了,错误了解开了B上的锁
3. 支持定制锁的key,不同业务场景需要锁不同的key
4. 过期时间确定,是否需要考虑续约,在我们目前的场景中,一次rpc的调用的SLA要求是,99.99%维持在1500ms之内,而一次FGC大约是2-3s之间,所以设定了10s的锁时间内并没有考虑续约,因为续约新起线程也存在成本
5. 如何解锁,在加锁的时候使用了redis.nx.ex,而解锁的时候用的是阿里云提供的企业版原语cad,即compareAndDelete,在进行当前锁value的比较(即确认持有者身份,该value由uuid+线程id+自定义键组成)之后再进行删除,本质是一个lua脚本在redis上的原子性运行

2.数据库底层有了解吗

嗯,略微了解过一点

3.讲下聚簇索引

聚簇索引指的是在索引的叶子节点中存在了全量的行记录,而非聚簇索引的叶子节点只保存了指向主键ID的值

InnoDB中有且只有一个聚簇索引,若表定义了pk,则pk是聚簇索引,若没有,则第一个非空索引充当聚簇索引,否则innoDB使用隐藏的rowId作为聚簇索引(该id在mvcc中也会被使用到)

比如存在一个联合索引A,B,C,通过SELECT A,B,C from table where   A < 10;即会直接命中索引上的所有值,直接拉出

但是当SELECT A,B,C,D from table where   A < 10;时,该联合索引的字段不能满足查询条件的需求,就会指向聚簇索引进行回表查询导致额外的性能损耗

但是为了提供通用的业务能力,我们一般不把聚簇索引的使用作为设计数据库时的强要求

4.怎么解决慢查询

使用explain对慢查询日志进行分析

5.索引底层

持久化在磁盘上,在需要的时候load进内存页中进行使用,一般采用b+树或者hash索引,HASH的优势在于常量级的检索速度,但是不支持范围查找,并且对于局部性原理的适应性也比较差

6.b+树跟b树有啥区别

b+树的非叶子节点只保存指向下一层的指针,而b树的非叶子节点可以保存全量数据

在索引的设计理念当中,核心目标是减小对磁盘的扫描,而B+树减少了非叶子节点的存储信息,在减少内存压力的同时,也减轻了对于磁盘的扫描次数,一次读取磁盘就可以load进更多的节点信息和数据,然后将所有的详细数据放到叶子节点当中,再最后确保只要扫一次就能拿到数据

7.你说b+树能范围查询,怎么实现的

在b+树的叶子节点中,是一串连在一起的链表,范围查询只需要查询到从头开始的最小那个元素,然后依据这个链表开始遍历,拿到范围内的数据并返回即可,而b树需要进行中序遍历

8.b+树的高度怎么计算

一般最多3-4层,取决于规定好的树的出度

9.b树和b+树的叶子节点和非叶子节点都存什么

B:存在索引以及全量行信息

B+:叶子:全量行信息,非叶子:索引以及下一层的指针

10.mysql最多能存多少数据

弱智问题,目前我们的原则是单库1T单表500w,具体要视经验和业务场景以及数据的ttl而定,水平拆分的话可以无上限

11.Mysql默认的事务隔离级别是什么,有什么缺点

RR:Read Repeatable,在当前读(即非select读)的场景下会发生幻读

12.什么是幻读

在事务A开始的时候发现存在记录1,3,随后A提交了update between 13的语句,然后B插入了记录2,在两者commit之后,A会发现记录2并没有被update

13.怎么解决幻读

加间隙锁,锁住一个range内的数据

14.Mysql的log有哪些,分别用来解决什么问题

binlog:对于数据库内增删改查的事件进行记录,用作主备同步,数据广播,故障恢复等功能

redoLog:通过对物理数据块的备份来提供容灾的恢复能力

undoLog:支持事务的rollback机制以及mvcc,实际是提供维护所有记录的版本控制链来进行的

15.怎么看mysql有没有执行索引

explain,看type里是否使用了index

16.explain的时候最关心哪些字段,分别是什么含义

type:访问类型

key:使用的什么索引

extra:是否存在filesort或者回表的情况

row&filter:判断这行sql扫描数据量以及优化效果

17.讲下hashmap底层结构,put操作怎么找到位置的,&运算等价于什么运算?为什么不是线程安全的,1.8是头插还是尾插?怎么保证线程安全

拉链结构,数据+链表or红黑树,put操作先找到hashcode对应的数据位置,然后append到其链表当中

不是线程安全的原因是因为在头插的时候扩容可能引起链表成环

1.8改为尾插后不会引起成环问题,但也不意味着其线程安全了,线程间的可见性还是无法保障

18.concurrenthashmap底层,1.7怎么扩容的,1.8怎么保证线程安全

1.7中使用分段加锁,1.8 volatile+synchronized来优化和保证

19.Synchronized怎么实现的

在class中存在一个monitor对象,syn会对这个值进行加锁

20.作用于方法时锁的是什么,静态方法锁的是什么,怎么实现可重入的

锁对象,静态方法锁class,重入通过记录和判断是否是当前线程来实现

21.CAS是什么,有什么缺点

compare and set,比较并设置,通过自旋来设置值

基于发生锁冲突的情况概率不高的假设,使用CAS如果OK的话直接跳过加锁阶段来获取当前锁

缺点是多次自旋的时候消耗CPU以及ABA问题

22.ABA怎么发生的,怎么解决ABA问题

值从A变到B再变回A,看似没有变化,但是实际已经被改变两次了,不是原有的值,如果有对原有值的依赖操作的话就会产生问题

解决方案:加版本号,做成乐观锁

23.ReetrantLock有用过吗,怎么实现重入的

AQS当中维护了当前的抢占线程以及抢占次数,每次如果抢占线程为当前线程则允许重入,当当前线程把所有抢占次数release了这个锁才算release

24.Volatile解决了什么问题,一般用在哪里

通过禁止重排序的方式解决了内存可见性问题,一般用在线程通信当中

25. Jdk1.6对锁做了哪些优化

实现锁膨胀

偏向锁:只在Monitor中标志是否在被同步方法调用

轻量级锁:发生竞争后先进行自旋,此时锁升级为轻量级

重量级锁:自旋获取未遂或有第三个线程竞争则升级成重量级锁,会多额外的线程切换开销

26. Spring的AOP的实现方式

优先GClib,再使用jdk动态代理

27. 什么是动态代理

不需要硬编码的形式,可以直接在运行期对部分对象进行代理,增强其行为

JDK动态代理可以通过实现InvocationHanlder进行实现,只能代理接口,本质是生成一个实现了所有interface的proxy然后把对象注入进来走反射进行调用,性能稍低(反射开销在于权限检查以及和入参拆解)

GCLIB通过重写对象生成子对象进行,缺点是无法继承final对象以及生成class会加重meta space区域的内存负担

28. Spring怎么解决循环依赖的

不知道

29. 类加载机制是什么,讲下双亲委派

加载、验证、解析、初始化

每个类由其线程上下文的加载器进行加载,可以进行手动设置,这样会破坏双亲委派机制

双亲委培:默认的类加载器都会调用父类的方法进行类的加载,这样可以保证类加载进来的时候如果没有特殊处理的话会交给一个统一的加载器进行加载,来统一实现该类

30. 讲下redis穿透

redis中缓存着数据库中的值,但是部分请求会直接请求不存在的值,此时会直接绕过redis对数据库进行访问,导致数据库压力上升,可以使用布隆过滤器进行拦截

31. 讲下布隆过滤器的实现机制

实质是一种hash,每个缓存值hash可以映射到二进制数组上,多个缓存值可以映射到同一个数组的项中,那么如果需要查询值A,若其hash值在这个数组的二进制位上存在,则认为它可能存在,可以进行数据库查询,若其hash值在数组的二进制位上不存在,则其必然不存在,直接返回失败即可

32. Redis为什么会这么快

- Redis由多路复用程序+事件分发器+事件处理器构成,其IO事件处理在单线程下运行,通过多路复用支持多个client的并发请求,但是执行的时候onebyone的执行,不存在任何的事务性竞争和线程上下文切换等开销
- 整个数据库放在内存中,在启动后不依赖持久化在磁盘的数据,IO开销对rt基本无影响
- 仅支持简单数据结构,减少序列化相关的cpu影响

33. 讲下redis击穿

在redis的某个热点key失效的时候,该热点流量会瞬间打到mysql上面,导致MySQL压力增大

解决方案:

1. 设置永不过期
2. 在server端对缓存进行访问的时候选择性的去给这个缓存续约

附赠:缓存雪崩

在设置缓存的时候,多个热点key的失效时间相同,则在失效的时候全部热点流量达到mysql

解决方案:过期时间设置的随机一点

34. 垃圾回收器讲下

CMS:并行回收

Serial:串行回收

Parnew:多线程版本的Serial

G1:分块回收

35. CMS怎么进行垃圾回收的

1. 初始化标记(存在STW):标记老年代的根对象,包括新生代中引用到的和GCRoot所关联的
2. 并发标记:标记跟对象所引用的所有对象
3. 预清理:将修改过的对象锁引用的对象重新标记
4. 最终标记(STW):将最终的dirty对象进行引用
5. 并发清理:将该清理的对象进行清理
6. 重设:准备下一轮GC

CMS好处:

1. 只在标记阶段产生STW
2. 并发清理效率快
3. 通过预清理来减少最终清理的stw损耗

CMS坏处:

1. 还是存在stw
2. 并发清理会使得cpu负担加重
3. 基于标记清理的方***使得内存产生碎片

36. 讲下垃圾回收算法

GCROOT:

1. 系统类加载器和初始化类加载器加载进来的对象
2. JNI当中的代码
3. 线程中引用的对象(包含虚拟机线程和本地方法线程)
4. monitor

标记清理:标记后将自由空间进行释放,当前对象均保留在原位,会产生内存碎片

标记整理:标记后将存活对象进行对齐整理,腾出更多的整块空间,耗cpu

37. 讲下对分布式的理解

CAP,BASE,LAMPORT LOCK,RAFT等理论性原理,不多做赘述

38. 有一个转盘,分3块,每块的中奖概率一样,怎么实现

直接random3,可考虑做成伪随机

39. 做个朋友圈,需要注意哪些点

点赞的表如何设计

40. 有看书吗?看过哪些书

41. 给自己打个分


更多模拟面试

全部评论

(3) 回帖
加载中...
话题 回帖

推荐话题

相关热帖

历年真题 真题热练榜 24小时
技术(软件)/信息技术类
查看全部

近期精华帖

热门推荐