深擁

导航

面试总结一

1.微服务的优点:

  • 独立开发 – 所有微服务都可以根据各自的功能轻松开发
  • 独立部署 – 基于其服务,可以在任何应用程序中单独部署它们
  • 故障隔离 – 即使应用程序的一项服务不起作用,系统仍可继续运行
  • 混合技术堆栈 – 可以使用不同的语言和技术来构建同一应用程序的不同服务
  • 粒度缩放 – 单个组件可根据需要进行缩放,无需将所有组件缩放在一起
  • 微服务是松藕合的,无论是在开发阶段或部署阶段都是独立的。
  • 能够快速响应, 局部修改容易, 一个服务出现问题不会影响整个应用。
  • 易于和第三方应用系统集成, 支持使用不同的语言开发, 允许你利用融合最新技术。
  • 每个微服务都很小,足够内聚,足够小,代码容易理解。团队能够更关注自己的工作成果, 聚焦指定的业务功能或业务需求。
  • 开发简单、开发效率提高,一个服务可能就是专一的只干一件事, 能够被小团队单独开发,这个小团队可以是 2 到 5 人的开发人员组成。

2.微服务的缺点:

  • 微服务架构带来过多的运维操作, 可能需要团队具备一定的 DevOps 技巧.
  • 分布式系统可能复杂难以管理。因为分布部署跟踪问题难。当服务数量增加,管理复杂性增加

3.Redis的优点:

  • 响应快速

Redis 响应非常快,每秒可以执行大约 110 000 个写入操作,或者 81 000 个读操作,其速度远超数据库。如果存入一些常用的数据,就能有效提高系统的性能。

  • 支持 6 种数据类型

它们是字符串、哈希结构、列表、集合、可排序集合和基数。比如对于字符串可以存入一些 Java 基础数据类型,哈希可以存储对象,列表可以存储 List 对象等。这使得在应用中很容易根据自己的需要选择存储的数据类型,方便开发。

对于 Redis 而言,虽然只有 6 种数据类型,但是有两大好处:一方面可以满足存储各种数据结构体的需要;另外一方面数据类型少,使得规则就少,需要的判断和逻辑就少,这样读/写的速度就更快。

  • 操作都是原子的

所有 Redis 的操作都是原子的,从而确保当两个客户同时访问 Redis 服务器时,得到的是更新后的值(最新值)。在需要高并发的场合可以考虑使用 Redis 的事务,处理一些需要锁的业务。

  • MultiUtility 工具

Redis 可以在如缓存、消息传递队列中使用(Redis 支持“发布+订阅”的消息模式),在应用程序如 Web 应用程序会话、网站页面点击数等任何短暂的数据中使用。

4.Redis为什么快?

  • 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
  • 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
  • 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU;
  • 使用多路I/O复用模型,非阻塞IO;
  • 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

5.Redis的缺点:

  • 由于 Redis 是内存数据库,短时间内大量增加数据,可能导致内存不够用。
  • redis是单线程的,单台服务器无法充分利用多核服务器的CPU

6.Redis的过期策略和内存淘汰机制:

  • 1、定期删除+惰性删除

定期删除:指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除

惰性删除:在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了,如果过期了此时就会删除,不会给你返回任何东西

  • 2、如果大量过期key堆积在内存里,导致redis内存块耗尽了,怎么办?

内存淘汰机制:

redis.conf中配置:

1# maxmemory-policy noeviction  

noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。

allkeys-lru:在主键空间中,优先移除最近未使用的key。

volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。

allkeys-random:在主键空间中,随机移除某个key。

volatile-random:在设置了过期时间的键空间中,随机移除某个key。

volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。

7.数据库的特性:

  • 1、原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。
  • 2、一致性(Consistency):官网上事务一致性的概念是:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性。
  • 3、隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  • 4、持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

8.MySQL的事务隔离级别:

9.MySQL事务的并发问题

  • 1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
  •   2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
  •   3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
  •   小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

10.MySQL的补充

  • 1、事务隔离级别为读提交时,写数据只会锁住相应的行
  •   2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
  •   3、事务隔离级别为串行化时,读写数据都会锁住整张表
  •    4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

11.垃圾回收算法

(1)标记—清除算法是最基础的收集算法,它分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实就是前面的可达性分析算法中判定垃圾对象的标记过程。如果在被标记后直接对对象进行清除,会带来另一个新的问题——内存碎片化。如果下次有比较大的对象实例需要在堆上分配较大的内存空间时,可能会出现无法找到足够的连续内存而不得不再次触发垃圾回收。

该算法有如下缺点:

  • 标记和清除过程的效率都不高

  • 标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不触发另一次垃圾收集动作

(2)复制算法是针对标记—清除算法的缺点,在其基础上进行改进而得到的,它将可用内存按容量分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另外一块内存上面,然后再把已使用过的内存空间一次清理掉。复制算法有如下优点:

  • 每次只对一块内存进行回收,运行高效

  • 只需移动栈顶指针,按顺序分配内存即可,实现简单

  • 内存回收时不用考虑内存碎片的出现

它的缺点是:可一次性分配的最大内存缩小了一半

(3)复制算法比较适合于新生代,在老年代中,对象存活率比较高,如果执行较多的复制操作,效率将会变低,所以老年代一般会选用其他算法,如标记—整理算法。该算法标记的过程与标记—清除算法中的标记过程一样,但对标记后出的垃圾对象的处理情况有所不同,它不是直接对可回收对象进行清理,而是让所有的对象都向一端移动,然后直接清理掉端边界以外的内存

 

12.JVM里包含什么

方法区,java堆(堆内存),java栈(栈内存),本地方法栈,程序计数器

13.悲观锁乐观锁

  • 1、 乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。
  • 2、 悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像 synchronized,不管三七二十一,直接上了锁就操作资源了。

14.hashmap底层

首先从它的底层结构来说,它的底层结构在1.7和1.8版有点不一样。在1.7的时候,它的底层数据结构是一个数组加上一个单链表;然而到1.8的时候它的底层结构就变成了一个数组加上一个单链表或者说是红黑树的方式,当单链表和红黑树之间的转换,他的单链表的长度大于等于8,并且他的hash桶大于等于64的时候,他会将单链表转换成红黑树形式存储;它在红黑树的结点数量如果要是小于等于6的时候,他会重新再转成一个单链表,这是它底层结构的一个变化。

红黑树-----平衡二叉树

15.es底层-----前缀树

16.es倒排索引

传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。

而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。

有了倒排索引,就能实现 o(1)时间复杂度的效率检索文章了,极大的提高了检索效率。

倒排索引,相反于一篇文章包含了哪些词,它从词出发,记载了这个词在哪些文档中出现过,由两部分组成——词典和倒排表。

17.spring

18.动态代理

动态代理就是为了解决静态代理不灵活的缺陷而产生的。静态代理是固定的,一旦确定了代码,如果委托类新增一个方法,而这个方法又需要增强,那么就必须在代理类里重写一个带增强的方法。而动态代理可以灵活替换代理方法,动态就是体现在这里。

动态代理的动态, 就是可以动态的切换真实实现类, 也就是说可以一个代理类(相同的代码, 相同的增强操作)应对一堆不确定的真实实现类.

19.面向切面编程

我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点;这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。

20.多线程的实现

  • 1. 继承Thread类
  • 2.实现Runnable接口
  • 3.实现Callable接口
  • 4.线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。

21.run方法和start方法的区别

start与run方法的主要区别在于当程序调用start方法一个新线程将会被创建,并且在run方法中的代码将会在新线程上运行,然而在你直接调用run方法的时候,程序并不会创建新线程,run方法内部的代码将在当前线程上运行。大多数情况下调用run方法是一个bug或者变成失误。因为调用者的初衷是调用start方法去开启一个新的线程,这个错误可以被很多静态代码覆盖工具检测出来,比如与fingbugs. 如果你想要运行需要消耗大量时间的任务,你最好使用start方法,否则在你调用run方法的时候,你的主线程将会被卡住。另外一个区别在于,一但一个线程被启动,你不能重复调用该thread对象的start方法,调用已经启动线程的start方法将会报IllegalStateException异常,  而你却可以重复调用run方法

 22.Runnable 和 Callable 的区别

  • Callable 规定(重写)的方法是 call(),Runnable 规定(重写)的方法是 run()。
  • Callable 的任务执行后可返回值,而 Runnable 的任务是不能返回值的。
  • Call 方法可以抛出异常,run 方法不可以。
  • 运行 Callable 任务可以拿到一个 Future 对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过 Future 对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果

posted on 2020-09-22 20:51  深擁  阅读(211)  评论(0编辑  收藏  举报