字节 已oc

[一面]

1.Web

1.1session 和 cookie 是什么,有什么区别

  • 1.session的常见实现要借助cookie来发送sessionID
  • 2.在绝大部分开发环境中, session = cookie + 其他东西根据实际需求而产生的东西
  • 本来 session 是一个抽象概念,开发者为了实现中断和继续等操作,将 user agent 和 server 之间一对一的交互,抽象为“会话”,进而衍生出“会话状态”,也就是 session 的概念。
  • 而 cookie 是一个实际存在的东西,http 协议中定义在 header 中的字段。可以认为是 session 的一种后端无状态实现。
  • 而我们今天常说的 “session”,是为了绕开 cookie 的各种限制,通常借助 cookie 本身和后端存储实现的,一种更高级的会话状态实现。
  • 所以 cookie 和 session,你可以认为是同一层次的概念,也可以认为是不同层次的概念。具体到实现,session 因为 session id 的存在,通常要借助 cookie 实现,但这并非必要,只能说是通用性较好的一种实现方案。

    1.2在浏览器输入一个网址会发生什么

2.JVM

2.1Java 内存模型

什么是 Java 内存模型、Java 内存模型的作用以及 Java 中内存模型做了什么事情
1.什么是java内存模型
Java 内存模型(Java Memory Model,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了 Java 程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
2.Java 内存模型的作用
JMM 是一种规范,是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。

  • 目的是保证并发编程场景中的原子性、可见性和有序性。
    • 原子性,是指在一个操作中,CPU 不可以在中途暂停然后再调度,即不被中断操作,要不执行完成,要不就不执行。
    • 可见性,是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
    • 有序性,即程序执行的顺序按照代码的先后顺序执行。

3.Java 中内存模型做了什么事情

  • 原子性

在 Java 中,为了保证原子性,提供了两个高级的字节码指令 Monitorenter 和 Monitorexit。在 Synchronized 的实现原理文章中,介绍过,这两个字节码,在 Java 中对应的关键字就是 Synchronized。因此,在 Java 中可以使用 Synchronized 来保证方法和代码块内的操作是原子性的。

  • 可见性

Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。
Java 中的 Volatile 关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存。被其修饰的变量在每次使用之前都从主内存刷新。因此,可以使用 Volatile 来保证多线程操作时变量的可见性。除了Volatile,Java 中的 Synchronized 和 Final 两个关键字也可以实现可见性。只不过实现方式不同,这里不再展开了。

  • 有序性

在 Java 中,可以使用 Synchronized 和 Volatile 来保证多线程之间操作的有序性。实现方式有所区别:Volatile 关键字会禁止指令重排。Synchronized 关键字保证同一时刻只允许一条线程操作。

但是 Synchronized 是比较影响性能的,虽然编译器提供了很多锁优化技术,但是也不建议过度使用。

2.1 GC 原理

1.是什么?
GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc()Runtime.getRuntime().gc()
2.哪些内存需要回收?
哪些内存需要回收是垃圾回收机制第一个要考虑的问题,所谓“要回收的垃圾”无非就是那些不可能再被任何途径使用的对象。那么如何找到这些对象?

  • 选取GCRoots对象
    • 引用计数法:这种算法不能解决对象之间相互引用的情况,所以这种方法不靠谱
    • 可达性分析法:这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。

那么问题又来了,如何选取GCRoots对象呢?在Java语言中,可以作为GCRoots的对象包括下面几种:

  1. 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
  2. 方法区中的类静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JNI(Native方法)引用的对象。
    • 方法区的垃圾回收

1、方法区的垃圾回收主要回收两部分内容:

  • 废弃常量
  • 无用的类

2、既然进行垃圾回收,就需要判断哪些是废弃常量,哪些是无用的类?

  • 如何判断废弃常量呢?以字面量回收为例,如果一个字符串“abc”已经进入常量池,但是当前系统没有任何一个String对象引用了叫做“abc”的字面量,那么,如果发生垃圾回收并且有必要时,“abc”就会被系统移出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
  • 如何判断无用的类呢?需要满足以下三个条件:
    1. 该类的所有实例都已经被回收,即Java堆中不存在该类的任何实例。
    2. 加载该类的ClassLoader已经被回收。
    3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

      2.3 线程属于哪块区域

      运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。
      image.png
  • 概括地说来:JVM初始运行的时候都会分配好Method Area(方法区)和Heap(堆),而JVM 每遇到一个线程,就为其分配一个Program Counter Register(程序计数器), VM Stack(虚拟机栈)和Native Method Stack (本地方法栈),当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。
  • 每当有线程被创建的时候,JVM就需要为其在内存中分配虚拟机栈和本地方法栈来记录调用方法的内容,分配程序计数器记录指令执行的位置,这样的内存消耗就是创建线程的内存代价。
  • 内存作为有限的资源,如果JVM创建了过多的线程,必然会导致资源的耗尽。因此,使用Executor架构复用线程可以节省内存资源,是十分必要的
  • JVM的并发是通过线程切换并分配时间片执行来实现的。 在任何一个时刻, 一个处理器内核只会执行一条线程中的指令。因此, 为了线程切换后能恢复到正确的执行位置, JVM需要先保存被挂起线程的上下文环境:将线程执行位置保存到程序计数器中,将调用方法的信息保存在栈中;同时将待执行线程的程序计数器和栈中的信息写入到处理器中,完成线程的上下文切换。维护线程隔离数据区中的内容在处理器中的导入导出,就是线程切换的性能代价。

3.HashMap

哈希表:相比上述几种数据结构,在哈希表中进行添加,删除,查找等操作,性能十分之高,不考虑哈希冲突的情况下(后面会探讨下哈希冲突的情况),仅需一次定位即可完成,时间复杂度为O(1),接下来我们就来看看哈希表是如何实现达到惊艳的常数阶O(1)的。
哈希表的主干就是数组。比如我们要新增或查找某个元素,我们通过把当前元素的关键字 通过某个函数映射到数组中的某个位置,通过数组下标一次定位就可完成操作。这个函数可以简单描述为:存储位置 = f(关键字),这个函数f一般称为哈希函数,这个函数的设计好坏会直接影响到哈希表的优劣。

  • 简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的

    3.1 是否线程安全

  • 当我们多个线程同时插入数据库的时候就会出现线程不安全,putVal()是put()方法中调用的,我们可以看到红框框起来的部分,

  • 由于put()和putVal()代码没有同步,插入一个value的时候会进行判空处理,在多线程的时候,如果正好2个线程都检查到对应位置是空的,都会插进去的话,先插进去的就会被后插进去的节点覆盖,而不是都挂在后面。就会出现数据错误,导致线程不安全

[

](https://blog.csdn.net/chunlaiqingke/article/details/89818185)

3.2 如何实现线程安全的操作;

在多线程条件下,容易导致死循环,具体表现为CPU使用率100%。因此多线程环境下保证 HashMap 的线程安全性,主要有如下几种方法:
1.替换成Hashtable,Hashtable通过对整个表上锁实现线程安全,因此效率比较低
2.使用Collections类的synchronizedMap方法包装一下。方法如下:
public static Map synchronizedMap(Map m) 返回由指定映射支持的同步(线程安全的)映射
3.使用ConcurrentHashMap,它使用分段锁来保证线程安全

3.3 为什么使用红黑树红黑树特性;

为什么使用红黑树
红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。[这里指到叶子节点的路径]

3.4 平衡搜索二叉树的区别;时间复杂度

·

4.网络

4.1 TCP/IP模型

image.png
关于四层模型的介绍,网上可谓汗牛充栋,概念何其艰深,比如:

  • 应用层:决定了向用户提供应用服务时通信的活动
    • 来到这里,关于网络的问题基本上就结束了,因为终端已经拿到了别人发过来的信息了,至于怎么处理信息,就是会话层,表示层,应用层的问题了。不同的应用程序有着不同的通信协议(Email——SMTP,Web——Http,文件传输——Ftp等),这些通信协议都工作在应用层。
  • 传输层:对上层应用层提供处于网络连接中的两台计算机之间的数据传输
    • 找到对端后,怎样保证对端完整收到信息?这就是传输层要解决的问题了,这里就有传送速度的调节,传送信息的验证,传完信息的确认等等问题,对应的TCP和UDP也是两种不同的实现思路。
  • 网络层:处理在网络上流动的数据包
    • 但随着入网设备越来越多,就需要被管理了,所以网络层就来解决网络管理的问题。IP层是整个网络分层里承上启下的核心,他主要解决两个问题,一个是编号,一个是找人。
  • 链路层:处理连接网络的硬件部分。
    • 知道介质了,怎样把信息传给设备?这就是数据链路层要解决的问题了。这里需要解决信息的封装问题,信息传送机制问题。所以这一层的所有协议设计都是围绕着怎么传信息展开。

4.2 OSI模型

image.png

5.MySQL

5.1 用过索引吗;怎么样确定使用到了索引索引类型

1. 查询条件在索引列上使用函数操作,或者运算的情况
例如以下case是不走索引的:

  1. explain select * from student where abs(age) =18;
  2. explain select * from student where age + 1=18;

2. 查询条件字符串和数字之间的隐式转换
例如:name与age分别做字符串/数字(88)的隐式转换;
以下case走索引情况:

  1. explain select * from student where name =’88’;
  2. explain select * from student where age='88';
  3. explain select * from student where age =88;

以下case不走索引情况:

  1. explain select * from student where name=88;

3. 特殊修饰符 %%, Or 将不走索引

  1. explain select * from student where name like'%name%' ;
  2. explain select * from student where name ='name' or age = 18;

4. 索引优化器选择最优的索引

  • 这一点最重要,索引到底用不用,不是列加了索引就一定会用,而是根据索引优化器来决定。
  • 显然,一个索引上不同的值越多,这个索引的区分度就越好。我们把一个索引上不同的值的个数,称之为“索引基数”。也就是说,基数越大,索引的区分度就越好,执行查询的行数就越少。

MySQL目前主要有以下几种索引类型
1.普通索引
2.唯一索引
3.主键索引(用过)
4.组合索引(被考过)联合索引使用,按照建索引的顺序字段来比较使用,参照左前缀原则
5.全文索引

5.2 为什么用B+树;和B树有什么区别

B+树其实和B树是非常相似的,我们首先看看相同点

  • 根节点至少一个元素
  • 非根节点元素范围:m/2 <= k <= m-1

不同点

  • B+树有两种类型的节点:内部结点(也称索引结点)和叶子结点。内部节点就是非叶子节点,内部节点不存储数据,只存储索引,数据都存储在叶子节点。
  • 内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
  • 每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接。
  • 父节点存有右孩子的第一个元素的索引。

B+树相对于B树有一些自己的优势,可以归结为下面几点。

  • 单一节点存储的元素更多,使得查询的IO次数更少,所以也就使得它更适合做为数据库MySQL的底层数据结构了。
  • 所有的查询都要查找到叶子节点,查询性能是稳定的,而B树,每个节点都可以查找到数据,所以不稳定。
  • 所有的叶子节点形成了一个有序链表,更加便于查找。

参考:https://segmentfault.com/a/1190000020416577
https://blog.csdn.net/a519640026/article/details/106940115

6.锁

6.1 有用过锁吗

6.2 lock 和 synchronized 区别

1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

6.3 乐观锁和悲观锁

  • 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度。在Java和数据库中都有此概念对应的实际应用。
  • 先说概念。对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。
  • 而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。
  • 乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。

image.png

7.Java volatile 作用;原理

神奇的volatile关键字解决了神奇的失效数据问题。

8.设计模式 手写单例模式

为什么要有单例模式
实际编程应用场景中,有一些对象其实我们只需要一个,比如线程池对象、缓存、系统全局配置对象等。这样可以就保证一个在全局使用的类不被频繁地创建与销毁,节省系统资源。
实现单例模式的几个要点

  1. 首先要确保全局只有一个类的实例。要保证这一点,至少类的构造器要私有化。
  2. 单例的类只能自己创建自己的实例。因为,构造器私有了,但是还要有一个实例,只能自己创建咯!
  3. 单例类必须能够提供自己的唯一实例给其他类就是要有一个公共的方法能返回该单例类的唯一实例。

[二面]

1.其他

1.1 自我介绍
1.2 觉得自己相比一年前进步了多少

2.项目

2.1 使用的JDK 版本;

14.0.1 2020年版

2.2 1.6、1.7、1.8 都有什么区别

JDK1.6 :

  • 有JDBC4.0更新、Complier API、WebSevice支持的加强等更新。
  • Scripting(开启 JS的支持,算是比较有用的)

JDK1.7

  • 异常处理(捕获多个异常) try-with-resources
  • Path接口、DirectoryStream、Files、WatchService(重要接口更新)

JDK1.8

  • 在语言、编译器、类库、开发工具以及Java虚拟机等方面都带来了不少新特性。
  • Lambda表达式可以说是Java 8最大的卖点,她将函数式编程引入了Java。Lambda允许把函数作为一个方法的参数,或者把代码看成数据。


3.HashMap

3.1 HashMap 有没有线程安全的版本;如何实现

  • JDK1.7:因为使用头插法,在 B 还指向 A 的情况下,把 A 头插到 B 前面,成环,下次访问A或B会造成死循环,空耗CPU资源。

HashMap中存在的线程安全问题:
1.HashMap在读取Hash槽首元素的时候读取的是工作内存中引用所指向的对象,并发情况下,其他线程修改的值并不能被及时读取到。
2.HashMap在插入新元素的时候,主要会进行两次判断:
2.1 第一次是根据键的hash判断当前hash槽是否被占用,如果没有就放入当前插入对象。并发情况下,如果A线程判断该槽未被占用,在执行写入操作时时间片耗尽。此时线程B也执行获取hash(恰巧和A线程的对象发生hash碰撞)判断该槽未被占用,继而直接插入该对象。然后A线程被CPU重新调度继续执行写入操作,就会将线程B的数据覆盖。(注:此处也有可见性问题)
2.2 第二次是同一个hash槽内,因为HashMap特性是保持key值唯一,所以会判断当前欲插入key是否存在,存在就会覆盖。与上文类似,并发情况下,如果线程A判断最后一个节点仍未发现重复key那么会把以当前对象构建节点挂在链表或者红黑树上,如果线程B在A判断操作和写操作之间,进行了判断和写操作,也会发生数据覆盖。
3.除此之外扩容也会发生类似的并发问题。还有size的问题,感觉其实高并发情况下这个size的准确性可以让步性能。
针对2.1问题采用无锁化同步机制,即CAS(需配合volatile),如果设置成功,跳出循环返回,如果失败继续下次循环重新判断。
针对2.2问题采用synchronized加锁,因为hash槽维度已保证线程安全,故而此处只需保证槽内数据线程安全即可,所以此处加锁的对象是链表首节点或者红黑树。这样就保证了线程安全性。扩容的时候类似。
[

](https://blog.csdn.net/weixin_31437143/article/details/114933414)

3.2 和HashTable的区别是什么

共同点:都是双列集合,底层都是哈希算法
区别:
1.HashMap是线程不安全的,效率高,JDK1.2版本
Hashtable是线程安全的,效率低,JDK1.0版本 都有synchronized
2.HashMap可以存储null键和null值
Hashtable不可以存储null键和null值
3.继承的父类不同
4.对外提供的接口不同 Hashtable比HashMap多提供了elments() 和contains() 两个方法。
5.遍历方式的内部实现上不同
6.初始容量大小和每次扩充容量大小的不同

4.MySQL

4.1 索引数据结构

4.2 聚簇索引和非聚簇索引;怎么分配;为什么这样分配;

  • 这两种索引内部都是B+树,聚集索引的叶子节点存放着一整行的数据。

聚簇索引(聚集索引)
聚簇索引就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分,每张表只能拥有一个聚簇索引。
Innodb通过主键聚集数据,如果没有定义主键,innodb会选择非空的唯一索引代替。如果没有这样的索引,innodb会隐式的定义一个主键来作为聚簇索引。
辅助索引(非聚簇索引)
在InnoDB中除主键索引外其他都是非聚簇索引,所以也叫非主键索引。非主键索引的叶节点并不是存储一行的值,而是存储具体行的主键值。不满足聚簇的定义。
聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找。辅助索引叶子节点存储的不再是行的物理位置,而是主键值。通过辅助索引首先找到的是主键值,再通过主键值找到数据行的数据页,再通过数据页中的Page Directory找到数据行。
image.png
对比点

  • 聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
  • Innodb聚簇索引和MyIsam非聚簇索引的比较说明

    4.3 B+树叶子节点链表一定是放的是key和值吗??

    4.4 有做过数据库优化吗

    1.SQL调优

  • SQL的调优就可以解决大部分问题了,当然也不排除SQL执行环节的调优。

2.排除缓存干扰

  • 为什么缓存会失效,而且是经常失效
  • 只要我们一对表进行更新,那这个表所有的缓存都会被清空
  • 8.0之下的版本,记得排除缓存的干扰

3.explain

  • 如果你发现explain的结果预估的rows值跟实际情况差距比较大
  • 用analyze table tablename 就可以重新统计索引信息
  • 还有一个方法就是force index强制走正确的索引,或者优化SQL,最后实在不行,可以新建索引,或者删掉错误的索引。

4.覆盖索引

  • 由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

5.联合索引

  • 名称和库存的联合索引,这样名称查出来就可以看到库存了,不需要查出id之后去回表再查询库存了
  • 联合索引在我们开发过程中也是常见的,但是并不是可以一直建立的,大家要思考索引占据的空间。

6.最左匹配原则

  • 最好能利用到现有的SQL最大化利用
  • 很多时候我们索引可能没建对,那你调整一下顺序,可能就可以优化到整个SQL了。

7.索引下推

  • 官方帮我们优化的东西,索引下推

4.5 int 存到数据库里面一般你都用什么类型

1、二进制数据类型
Binary、Varbinary、Image
2、字符数据类型
Char,Varchar和 Text
3、Unicode数据类型
包括Nchar,Nvarchar和Ntext
4、日期和时间数据类型
包括Datetime, Smalldatetime, Date, TimeStamp
5、数字数据类型
数字数据类型包括正数和负数、小数和整数
6、货币数据类型
表示正的或者负的货币数量。
7、特殊数据类型
特殊的数据类型有3种,即Timestamp、Bit 和 Uniqueidentifier。

4.6 varchar(35)的含义;中文在varchar中占几个字符;编码有哪些

  • varchar(35)的含义:这个字符串可以35个字符。不管这个字符是汉字还是字母
  • 常用汉字占3个字节、符号也是
  • image.png

    5.Redis

    5.1 为什么使用Redis

    为什么使用redis?主要从两个角度去考虑:性能和并发。当然,redis还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件(如zookpeer等)代替,并不是非要使用redis。因此,这个问题主要从性能和并发两个角度去答。
    1.性能
    如下图所示,我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。
    2.并发
    如下图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。

5.2 优点;

Redis特点

  • Redis是一个高性能(支持并发11万读8万写)的key-value存储系统。
  • 支持丰富的存储value类型,包括string(字符串)、list(链表)、set(集合)、zset(sorted set —有序集合)和hash(哈希类型)。
  • Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。
  • 虽然运行在内存,同时支持持久化。提供两种方式:1保存到数据文件.rdb安全性高,但对效率有影响。2保存对数据有影响的操作命令到.aof中,保存操作命令的频率可配置,比较灵活,效率高。
  • 支持订阅发布功能(subscribe/publish)
  • Redis 很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Python,Ruby,Erlang,PHP客户端,使用很方便。

[

](https://blog.csdn.net/qq_34777600/article/details/81502116)

5.3 如何应对缓存穿透和缓存雪崩问题

缓存穿透: 即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
解决方案:
(一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
(二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
(三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

缓存雪崩: 即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
解决方案:
(一)给缓存的失效时间,加上一个随机值,避免集体失效。
(二)使用互斥锁,但是该方案吞吐量明显下降了。
(三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点
I 从缓存A读数据库,有则直接返回
II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
III 更新线程同时更新缓存A和缓存B。
[

](https://blog.csdn.net/qq_34777600/article/details/81502116)

6.算法

编辑距离

[三面]

1.网络

1.1 TCP 和 UDP 的区别

1. 对比
image.png
2. 总结

  • TCP向上层提供面向连接的可靠服务 ,UDP向上层提供无连接不可靠服务。
  • 虽然 UDP 并没有 TCP 传输来的准确,但是也能在很多实时性要求高的地方有所作为
  • 对数据准确性要求高,速度可以相对较慢的,可以选用TCP

1.2 三次握手和四次挥手

为什么是三次握手?不是两次、四次?
1.三次握手才可以阻止重复历史连接的初始化(主要原因)
如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:

  • 如果是历史连接(序列号过期或超时),则第三次握手发送的报文是 RST 报文,以此中止历史连接;
  • 如果不是历史连接,则第三次发送的报文是 ACK 报文,通信双方就会成功建立连接;

2.三次握手才可以同步双方的初始序列号

  • 序列号的作用
    • 接收方可以去除重复的数据;
    • 接收方可以根据数据包的序列号按序接收;
    • 可以标识发送出去的数据包中, 哪些是已经被对方收到的;
  • 四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。

3.三次握手才可以避免资源浪费
即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求SYN报文,而造成重复分配资源。
java后台开发面经整理 - 图8
为什么挥手需要四次?
再来回顾下四次挥手双方发 FIN 包的过程,就能理解为什么需要四次了。

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,从而比三次握手导致多了一次。

1.3 https 建立连接的过程

java后台开发面经整理 - 图9
1 客户端访问https连接
2-3 服务端发送证书(公钥)给客户端
4-5 客户端验证服务端的证书
6-7 服务端接收加密信息,解密得到客户端提供的随机字符串
8 客户端验证服务端返回的握手信息,完成握手

1.4 状态码

常见的HTTP状态码

1.5 重定向和转发区别

2.MySQL

2.1 索引的知识

在MySQL中主要就是Hash索引和B+Tree。

2.2 事务ACID 特性

1.原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。
2、一致性(Consistency)一致性是指事务使得系统从一个一致性的状态转换到另一个一致性的状态。
3、隔离性(Isolation)一个事务所做的修改在最终提交以前对其他事务是不可见的。
4、持久性(Durability)一旦事务提交,则所做的修改会永久保存在数据库中。

2.3 事务并发的问题解决方法

  1. 第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。
  2. 脏读(dirty read):如果第二个事务查询到第一个事务还未提交的更新数据,形成脏读。
  3. 虚读(phantom read):一个事务执行两次查询,第二次查询比第一次多出或少一些数据,造成两次结果不一致。只是另一个事务在这两次查询中间插入或者删除了数据造成的。
  4. 不可重复读(unrepeated read):一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。
  5. 第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。

并发控制:
1.数据库系统采用不同的锁类型来实现以上四种隔离级别,具体的实现过程对用户是透明的。用户应该关心的是如何选择合适的隔离级别。
2.对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读,而且具有较好的并发性能。
3.每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。JDBC数据库连接使用数据库系统默认的隔离级别。
4.在Hibernate的配置文件中可以显示地设置隔离级别。每一种隔离级别对应着一个正整数。
5.需要注意的是,在受管理环境中,如果Hibernate使用的数据库连接来自于应用服务器提供的数据源,Hibernate不会改变这些连接的事务隔离级别。在这种情况下,应该通过修改应用服务器的数据源配置来修改隔离级别。
6.当数据库系统采用Red Committed隔离级别时,会导致不可重复读和第二类丢失更新的并发问题,在可能出现这种问题的场合。可以在应用程序中采用悲观锁或乐观锁来避免这类问题。

  • 悲观锁:为它每次发送的SQL语句都会加上”for update”用于告诉数据库锁定相关数据,大大限制了并发性
  • Hibernate为乐观锁提供了3中实现:1. 基于version2. 基于timestamp3. 为遗留项目添加添加乐观锁 Hibernate为乐观锁提供了3中实现:

3.Java

3.1 String 创建对象;intern 方法

3.2 == 和 equals 区别

  • “==”和”!=”比较的是”栈”中的内容,而equals()比较的是”堆”中的内容.

(1)==可用于基本类型和引用类型:当用于基本类型时候,是比较值是否相同;当用于引用类型的时候,是比较对象是否相同。
(2)对于String a = “a”; Integer b = 1;这种类型的特有对象创建方式,==的时候值是相同的。
(3).基本类型没有equals方法,equals只比较值(对象中的内容)是否相同(相同返回true)。
(4)一个类如果没有定义equals方法,它将默认继承Object中的equals方法,返回值与==方法相同

3.3 线程池操作,参数

1、java中为什么要使用多线程使用多线程,可以把一些大任务分解成多个小任务来执行,多个小任务之间互不影像,同时进行,这样,充分利用了cpu资源。
2、java中简单的实现多线程的方式
继承Thread类,重写run方法;

3.4 用的设计模式

java后台开发面经整理 - 图10

3.5 Lambda

Lambda原理:在Java8中每一个表达式必须有一个函数式接口与之对应。

什么函数式接口?简单的说就是只包含一个抽象方法的普通接口

4.JVM

Java 内存模型
垃圾回收机制

5.算法

1.输入一个数组,返回数组内数字能组成的最大数
2.输入每个课程之间的关系,比如1->2,表示学习课程2需要先学习课程1,每轮学习课程 项目不限,问最快几轮学习完之类的蛇形或者说之字形遍历二叉树