提交 91bac466 编写于 作者: W wizardforcel

2022-06-07 20:18:58

上级 93bb2765
此差异已折叠。
此差异已折叠。
# InterviewTop
<div align="left"> <a href = "https://www.zhihu.com/people/yi-zhi-zi-dong-bian-ma-ji"><img src="https://img.shields.io/badge/Zhihu-知乎-blue" width = "80px" hight = "50px"/></a><span style="font-size:12px">&nbsp@后端技术小牛说</span>&nbsp&nbsp&nbsp&nbsp
<a href = "https://mp.weixin.qq.com/s/PXuHPGjAjlQrlFdi4m1_4w"><img src="https://img.shields.io/badge/WX-公众号-green" width = "80px" hight = "50px"/></a><span style="font-size:12px">&nbsp@后端技术小牛说</span>
&nbsp&nbsp&nbsp&nbsp
<a href = "https://github.com/autoencoder-github/interviewtop"><img src="https://img.shields.io/badge/GitHub-仓库-red" width = "80px" hight = "50px"/></a><span style="font-size:12px">&nbsp @interviewtop</span>
</div>
### **❤️致各位题友的一封信(使用仓库前必读)**
小牛是非科班出身的微软程序员一枚。
在面试国内各大互联网企业时,发现大部分针对计算机的面试题都差不多。
于是小牛灵机一动跟几个BAT工程师们根据自己的面试经验,整理出这么一套计算机校招、社招面试常见面试八股文。
在这里先替所有使用仓库的同学,谢谢各位贡献者啦。
**如果大家觉得仓库很用心的话,麻烦大佬帮忙点个 star ,这也是我们一直更新下去的动力。**
感谢支持,该仓库会一直维护,希望对各位有一丢丢帮助。
特别是2022年秋招,校招的小伙伴,来个star吧。
大厂面试题在线网址,可以查到近期公司的八股文面试题。http://interviewtop.top
这份仓库的pdf版百度云下载地址:
> [**点我下载**](https://pan.baidu.com/s/1jSJWShuC__vcMHqEQyntNw)
>
提取码:ozs7
同时也欢迎大家关注我的公众号:**后端技术小牛说**,不定期分享技术文和一线大厂内推机会。
<div align="center"> <img src="https://cdn.jsdelivr.net/gh/autoencoder-github/acticle/qcode.jpg" width = "150px" hight = "150px"/> </div>
## 📢计算机基础
### 数据结构与算法
- [数据结构与算法面试题背诵版](https://gitee.com/autoencoder/interviewtop/blob/master/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md)
### 操作系统
- [操作系统面试题背诵版](https://gitee.com/autoencoder/interviewtop/blob/master/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.md)
### 计算机网络
- [计算机网络面试题背诵版](https://gitee.com/autoencoder/interviewtop/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md)
### 数据库
- [数据库面试题背诵版](https://gitee.com/autoencoder/interviewtop/blob/master/%E6%95%B0%E6%8D%AE%E5%BA%93.md)
- Redis面试题背诵版
### 🍗Java语言特性
- [Java基础](https://gitee.com/autoencoder/interviewtop/blob/master/Java%E5%9F%BA%E7%A1%80.md)
- [Java多线程面试题背诵版](https://gitee.com/autoencoder/interviewtop/blob/master/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B.md)
- [Java虚拟机面试题背诵版](https://gitee.com/autoencoder/interviewtop/blob/master/java%E8%99%9A%E6%8B%9F%E6%9C%BA.md)
### 🍗JavaWeb框架
- Spring
- SpringMVC
- Mybatis
- Springboot
### 🍖分布式框架
- 消息中间件
- 分布式文件系统
+ [InterviewTop](README.md)
+ [Java 基础](Java基础.md)
+ [Java 多线程](Java多线程.md)
+ [Java 虚拟机](java虚拟机.md)
+ [推荐阅读](推荐阅读.md)
+ [操作系统](操作系统.md)
+ [数据库](数据库.md)
+ [数据结构与算法](数据结构与算法.md)
+ [计算机网络](计算机网络.md)
+ [设计模式](设计模式.md)
\ No newline at end of file
# Java 虚拟机
## 简述JVM内存模型
线程私有的运行时数据区: 程序计数器、Java 虚拟机栈、本地方法栈。
线程共享的运行时数据区:Java 堆、方法区。
## 简述程序计数器
程序计数器表示当前线程所执行的字节码的行号指示器。
程序计数器不会产生StackOverflowError和OutOfMemoryError。
## 简述虚拟机栈
Java 虚拟机栈用来描述 Java 方法执行的内存模型。线程创建时就会分配一个栈空间,线程结束后栈空间被回收。
栈中元素用于支持虚拟机进行方法调用,每个方法在执行时都会创建一个栈帧存储方法的局部变量表、操作栈、动态链接和返回地址等信息。
虚拟机栈会产生两类异常:
StackOverflowError:线程请求的栈深度大于虚拟机允许的深度抛出。
OutOfMemoryError:如果 JVM 栈容量可以动态扩展,虚拟机栈占用内存超出抛出。
## 简述本地方法栈
本地方法栈与虚拟机栈作用相似,不同的是虚拟机栈为虚拟机执行 Java 方法服务,本地方法栈为本地方法服务。可以将虚拟机栈看作普通的java函数对应的内存模型,本地方法栈看作由native关键词修饰的函数对应的内存模型。
本地方法栈会产生两类异常:
StackOverflowError:线程请求的栈深度大于虚拟机允许的深度抛出。
OutOfMemoryError:如果 JVM 栈容量可以动态扩展,虚拟机栈占用内存超出抛出。
## 简述JVM中的堆
堆主要作用是存放对象实例,Java 里几乎所有对象实例都在堆分配内存,堆也是内存管理中最大的一块。Java的垃圾回收主要就是针对堆这一区域进行。
可通过 -Xms 和 -Xmx 设置堆的最小和最大容量。
堆会抛出 OutOfMemoryError异常。
## 简述方法区
方法区用于存储被虚拟机加载的类信息、常量、静态变量等数据。
JDK6之前使用永久代实现方法区,容易内存溢出。JDK7 把放在永久代的字符串常量池、静态变量等移出,JDK8 中抛弃永久代,改用在本地内存中实现的元空间来实现方法区,把 JDK 7 中永久代内容移到元空间。
方法区会抛出 OutOfMemoryError异常。
## 简述运行时常量池
运行时常量池存放常量池表,用于存放编译器生成的各种字面量与符号引用。一般除了保存 Class 文件中描述的符号引用外,还会把符号引用翻译的直接引用也存储在运行时常量池。除此之外,也会存放字符串基本类型。
JDK8之前,放在方法区,大小受限于方法区。JDK8将运行时常量池存放堆中。
## 简述直接内存
直接内存也称为堆外内存,就是把内存对象分配在JVM堆外的内存区域。这部分内存不是虚拟机管理,而是由操作系统来管理。
Java通过通过DriectByteBuffer对其进行操作,避免了在 Java 堆和 Native堆来回复制数据。
## 简述java创建对象的过程
1. 检查该指令的参数能否在常量池中定位到一个类的符号引用,并检查引用代表的类是否已被加载、解析和初始化,如果没有就先执行类加载。
2. 通过检查通过后虚拟机将为新生对象分配内存。
3. 完成内存分配后虚拟机将成员变量设为零值
4. 设置对象头,包括哈希码、GC 信息、锁信息、对象所属类的类元信息等。
5. 执行 init 方法,初始化成员变量,执行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给引用变量。
## 简述JVM给对象分配内存的策略
1. 指针碰撞: 这种方式在内存中放一个指针作为分界指示器将使用过的内存放在一边,空闲的放在另一边,通过指针挪动完成分配。
2. 空闲列表: 对于 Java 堆内存不规整的情况,虚拟机必须维护一个列表记录哪些内存可用,在分配时从列表中找到一块足够大的空间划分给对象并更新列表记录。
## java对象内存分配是如何保证线程安全的
1. 对分配内存空间采用CAS机制,配合失败重试的方式保证更新操作的原子性。该方式效率低。
2. 每个线程在Java堆中预先分配一小块内存,然后再给对象分配内存的时候,直接在自己这块"私有"内存中分配。一般采用这种策略。
## 简述对象的内存布局
对象在堆内存的存储布局可分为对象头、实例数据和对齐填充。
对象头主要包含两部分数据: MarkWord、类型指针。MarkWord 用于存储哈希码(HashCode)、GC分代年龄、锁状态标志位、线程持有的锁、偏向线程ID等信息。
类型指针即对象指向他的类元数据指针,如果对象是一个 Java 数组,会有一块用于记录数组长度的数据,
实例数据存储代码中所定义的各种类型的字段信息。
对齐填充起占位作用。HotSpot 虚拟机要求对象的起始地址必须是8的整数倍,因此需要对齐填充。
## 如何判断对象是否是垃圾
引用计数法:设置引用计数器,对象被引用计数器加 1,引用失效时计数器减 1,如果计数器为 0 则被标记为垃圾。会存在对象间循环引用的问题,一般不使用这种方法。
可达性分析:通过 GC Roots 的根对象作为起始节点,从这些节点开始,根据引用关系向下搜索,如果某个对象没有被搜到,则会被标记为垃圾。可作为 GC Roots 的对象包括虚拟机栈和本地方法栈中引用的对象、类静态属性引用的对象、常量引用的对象。
## 简述java的引用类型
强引用: 被强引用关联的对象不会被回收。一般采用 new 方法创建强引用。
软引用:被软引用关联的对象只有在内存不够的情况下才会被回收。一般采用 SoftReference 类来创建软引用。
弱引用:垃圾收集器碰到即回收,也就是说它只能存活到下一次垃圾回收发生之前。一般采用 WeakReference 类来创建弱引用。
虚引用: 无法通过该引用获取对象。唯一目的就是为了能在对象被回收时收到一个系统通知。虚引用必须与引用队列联合使用。
## 简述标记清除算法、标记整理算法和标记复制算法
标记清除算法:先标记需清除的对象,之后统一回收。这种方法效率不高,会产生大量不连续的碎片。
标记整理算法:先标记存活对象,然后让所有存活对象向一端移动,之后清理端边界以外的内存
标记复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当使用的这块空间用完了,就将存活对象复制到另一块,再把已使用过的内存空间一次清理掉。
## 简述分代收集算法
根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。
一般将堆分为新生代和老年代,对这两块采用不同的算法。
新生代使用:标记复制算法
老年代使用:标记清除或者标记整理算法
## 简述Serial垃圾收集器
单线程串行收集器。垃圾回收的时候,必须暂停其他所有线程。新生代使用标记复制算法,老年代使用标记整理算法。简单高效。
## 简述ParNew垃圾收集器
可以看作Serial垃圾收集器的多线程版本,新生代使用标记复制算法,老年代使用标记整理算法。
## 简述Parallel Scavenge垃圾收集器
注重吞吐量,即cpu运行代码时间/cpu耗时总时间(cpu运行代码时间+ 垃圾回收时间)。新生代使用标记复制算法,老年代使用标记整理算法。
## 简述CMS垃圾收集器
注重最短时间停顿。CMS垃圾收集器为最早提出的并发收集器,垃圾收集线程与用户线程同时工作。采用标记清除算法。该收集器分为初始标记、并发标记、并发预清理、并发清除、并发重置这么几个步骤。
初始标记:暂停其他线程(stop the world),标记与GC roots直接关联的对象。并发标记:可达性分析过程(程序不会停顿)。
并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象,重新标记,暂停虚拟机(stop the world)扫描CMS堆中剩余对象。
并发清除:清理垃圾对象,(程序不会停顿)。
并发重置,重置CMS收集器的数据结构。
## 简述G1垃圾收集器
和之前收集器不同,该垃圾收集器把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。
初始标记:标记与GC roots直接关联的对象。
并发标记:可达性分析。
最终标记,对并发标记过程中,用户线程修改的对象再次标记一下。
筛选回收:对各个Region的回收价值和成本进行排序,然后根据用户所期望的GC停顿时间制定回收计划并回收。
## 简述Minor GC
Minor GC指发生在新生代的垃圾收集,因为 Java 对象大多存活时间短,所以 Minor GC 非常频繁,一般回收速度也比较快。
## 简述Full GC
Full GC 是清理整个堆空间—包括年轻代和永久代。调用System.gc(),老年代空间不足,空间分配担保失败,永生代空间不足会产生full gc。
## 常见内存分配策略
大多数情况下对象在新生代 Eden 区分配,当 Eden 没有足够空间时将发起一次 Minor GC。
大对象需要大量连续内存空间,直接进入老年代区分配。
如果经历过第一次 Minor GC 仍然存活且能被 Survivor 容纳,该对象就会被移动到 Survivor 中并将年龄设置为 1,并且每熬过一次 Minor GC 年龄就加 1 ,当增加到一定程度(默认15)就会被晋升到老年代。
如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 的一半,年龄不小于该年龄的对象就可以直接进入老年代。
空间分配担保。MinorGC 前虚拟机必须检查老年代最大可用连续空间是否大于新生代对象总空间,如果满足则说明这次 Minor GC 确定安全。如果不,JVM会查看HandlePromotionFailure 参数是否允许担保失败,如果允许会继续检查老年代最大可用连续空间是否大于历次晋升老年代对象的平均大小,如果满足将Minor GC,否则改成一次 FullGC。
## 简述JVM类加载过程
加载:
1. 通过全类名获取类的二进制字节流.
2. 将类的静态存储结构转化为方法区的运行时数据结构。
3. 在内存中生成类的Class对象,作为方法区数据的入口。
验证:对文件格式,元数据,字节码,符号引用等验证正确性。
准备:在方法区内为类变量分配内存并设置为0值。
解析:将符号引用转化为直接引用。
初始化:执行类构造器clinit方法,真正初始化。
## 简述JVM中的类加载器
BootstrapClassLoader启动类加载器:加载/lib下的jar包和类。C++编写。
ExtensionClassLoader扩展类加载器: /lib/ext目录下的jar包和类。java编写。
AppClassLoader应用类加载器,加载当前classPath下的jar包和类。java编写。
## 简述双亲委派机制
一个类加载器收到类加载请求之后,首先判断当前类是否被加载过。已经被加载的类会直接返回,如果没有被加载,首先将类加载请求转发给父类加载器,一直转发到启动类加载器,只有当父类加载器无法完成时才尝试自己加载。
加载类顺序:BootstrapClassLoader->ExtensionClassLoader->AppClassLoader->CustomClassLoader
检查类是否加载顺序:
CustomClassLoader->AppClassLoader->ExtensionClassLoader->BootstrapClassLoader
## 双亲委派机制的优点
1. 避免类的重复加载。相同的类被不同的类加载器加载会产生不同的类,双亲委派保证了java程序的稳定运行。
2. 保证核心API不被修改。
## 如何破坏双亲委派机制
重载loadClass()方法,即自定义类加载器。
## 如何构建自定义类加载器
1. 新建自定义类继承自java.lang.ClassLoader
2. 重写findClass、loadClass、defineClass方法
## JVM常见调优参数
- -Xms 初始堆大小
- -Xmx 最大堆大小
- -XX:NewSize 年轻代大小
- -XX:MaxNewSize 年轻代最大值
- -XX:PermSize 永生代初始值
- -XX:MaxPermSize 永生代最大值
- -XX:NewRatio 新生代与老年代的比例
- ![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
\ No newline at end of file
# 推荐阅读
>>## 推荐阅读
>>1. [设计模式面试背诵版](https://mp.weixin.qq.com/s/yZFKnNx6meLmMZ4SqgbYMg)
>>2. [java多线程并发面试背诵版](https://mp.weixin.qq.com/s/ilaLRnYvHVe_LH5yImhEcQ)
>>3. [java虚拟机并发面试背诵版](https://mp.weixin.qq.com/s/r4uigbq5XyoFiB_206YM6Q)
>>4. [数据库面试背诵版](https://mp.weixin.qq.com/s/6gnhiaN99eEG7bxJMuIOTA)
>>5. [计算机网络背诵版](https://mp.weixin.qq.com/s/MPWuwWvuaiUYNlUtnpa3fw)
>>![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
---
\ No newline at end of file
此差异已折叠。
# 数据库
## 简述数据库三大范式
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
数据库第二范式:关系模式必须满足第一范式,并且所有非主属性都完全依赖于主码。注意,符合第二范式的关系模型可能还存在数据冗余、更新异常等问题。关系模型(学号,姓名,专业编号,专业名称)中,学号->姓名,而专业编号->专业名称,不满足数据库第二范式
数据库第三范式:关系模型满足第二范式,所有非主属性对任何候选关键字都不存在传递依赖。即每个属性都跟主键有直接关系而不是间接关系。接着以学生表举例,对于关系模型(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)院校地址,院校电话和学号不存在直接关系,因此不满足第三范式。
## 简述MySQL的架构
MySQL可以分为应用层,逻辑层,数据库引擎层,物理层。
应用层:负责和客户端,响应客户端请求,建立连接,返回数据。
逻辑层:包括SQK接口,解析器,优化器,Cache与buffer。
数据库引擎层:有常见的MyISAM,InnoDB等等。
物理层:负责文件存储,日志等等。
## 简述执行SQL语言的过程
1. 客户端首先通过连接器进行身份认证和权限相关
2. 如果是执行查询语句的时候,会先查询缓存,但MySQL 8.0 版本后该步骤移除。
3. 没有命中缓存的话,SQL 语句就会经过解析器,分析语句,包括语法检查等等。
4. 通过优化器,将用户的SQL语句按照 MySQL 认为最优的方案去执行。
5. 执行语句,并从存储引擎返回数据。
## 简述MySQL的共享锁排它锁
共享锁也称为读锁,相互不阻塞,多个客户在同一时刻可以同时读取同一个资源而不相互干扰。排他锁也称为写锁,会阻塞其他的写锁和读锁,确保在给定时间内只有一个用户能执行写入并防止其他用户读取正在写入的同一资源。
## 简述MySQL中的按粒度的锁分类
表级锁: 对当前操作的整张表加锁,实现简单,加锁快,但并发能力低。
行锁: 锁住某一行,如果表存在索引,那么记录锁是锁在索引上的,如果表没有索引,那么 InnoDB 会创建一个隐藏的聚簇索引加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
Gap 锁:也称为间隙锁: 锁定一个范围但不包括记录本身。其目的是为了防止同一事物的两次当前读出现幻读的情况。
Next-key Lock: 行锁+gap锁。
## 如何解决数据库死锁
1. 预先检测到死锁的循环依赖,并立即返回一个错误。
2. 当查询的时间达到锁等待超时的设定后放弃锁请求。
## 简述乐观锁和悲观锁
乐观锁:对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。
悲观锁:对于数据冲突保持一种悲观态度,在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的。
## 简述InnoDB存储引擎
InnoDB 是 MySQL 的默认事务型引擎,支持事务,表是基于聚簇索引建立的。支持表级锁和行级锁,支持外键,适合数据增删改查都频繁的情况。
InnoDB 采用 MVCC 来支持高并发,并且实现了四个标准的隔离级别。其默认级别是 REPEATABLE READ,并通过间隙锁策略防止幻读,间隙锁使 InnoDB 不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定防止幻行的插入。
## 简述MyISAM存储引擎
MySQL5.1及之前,MyISAM 是默认存储引擎。MyISAM不支持事务,Myisam支持表级锁,不支持行级锁,表不支持外键,该存储引擎存有表的行数,count运算会更快。适合查询频繁,不适合对于增删改要求高的情况
## 简述Memory存储引擎
Memory存储引擎将所有数据都保存在内存,不需要磁盘 IO。支持哈希索引,因此查找速度极快。Memory 表使用表级锁,因此并发写入的性能较低。
## 索引是什么?
索引是存储引擎中用于快速找到记录的一种数据结构。在关系型数据库中,索引具体是一种对数据库中一列或多列的值进行排序的存储结构。
## 为什么引入索引?
为了提高数据查询的效率。索引对数据库查询良好的性能非常关键,当表中数据量越来越大,索引对性能的影响越重要。
## Mysql有哪些常见索引类型?
* 数据结构角度
> B-Tree索引
> 哈希索引
> R-Tree索引
> 全文索引
* 物理存储角度
> 主键索引(聚簇索引):叶子节点存的是整行的数据
> 非主键索引(二级索引):叶子节点存的主键的值
## 简述B-Tree与B+树
B-Tree 是一种自平衡的多叉树。每个节点都存储关键字值。其左子节点的关键字值小于该节点关键字值,且右子节点的关键字值大于或等于该节点关键字值。
B+树也是是一种自平衡的多叉树。其基本定义与B树相同,不同点在于数据只出现在叶子节点,所有叶子节点增加了一个链指针,方便进行范围查询。
B+树中间节点不存放数据,所以同样大小的磁盘页上可以容纳更多节点元素,访问叶子节点上关联的数据也具有更好的缓存命中率。并且数据顺序排列并且相连,所以便于区间查找和搜索。
B树每一个节点都包含key和value,查询效率比B+树高。
## 简述Hash索引
哈希索引对于每一行数据计算一个哈希码,并将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。只有 Memory 引擎显式支持哈希索引。
Hash索引不支持范围查询,无法用于排序,也不支持部分索引列匹配查找。
## 简述自适应Hash索引
InnoDB对于频繁使用的某些索引值,会在内存中基于 B-Tree 索引之上再创键一个哈希索引,这也被称为自适应Hash索引。
## 简述聚集索引和稀疏索引
聚集索引按每张表的主键构建一棵B+树,数据库中的每个搜索键值都有一个索引记录,每个数据页通过双向链表连接。表数据访问更快,但表更新代价高。
稀疏索引不会为每个搜索关键字创建索引记录。搜索过程需要,我们首先按索引记录进行操作,并按顺序搜索,直到找到所需的数据为止。
## 简述辅助索引与回表查询
辅助索引是非聚集索引,叶子节点不包含记录的全部数据,包含了一个书签用来告诉InnoDB哪里可以找到与索引相对应的行数据。
通过辅助索引查询,先通过书签查到聚集索引,再根据聚集索引查对应的值,需要两次,也称为回表查询。
## 简述联合索引和最左匹配原则
联合索引是指对表上的多个列的关键词进行索引。
对于联合索引的查询,如果精确匹配联合索引的左边连续一列或者多列,则mysql会一直向右匹配直到遇到范围查询(>,<,between,like)就停止匹配。Mysql会对第一个索引字段数据进行排序,在第一个字段基础上,再对第二个字段排序。
## 简述覆盖索引
覆盖索引指一个索引包含或覆盖了所有需要查询的字段的值,不需要回表查询,即索引本身存了对应的值。
## 为什么数据库不用红黑树用B+树
红黑树的出度为 2,而 B Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B Tree 大非常多,IO次数很多,导致会比较慢,因此检索的次数也就更多。
B+Tree 相比于 B-Tree 更适合外存索引,拥有更大的出度,IO次数较少,检索效率会更高。
## 基于主键索引的查询和非主键索引的查询有什么区别?
对于select * from 主键=XX,基于主键的普通查询仅查找主键这棵树,对于select * from 非主键=XX,基于非主键的查询有可能存在回表过程(回到主键索引树搜索的过程称为回表),因为非主键索引叶子节点仅存主键值,无整行全部信息。
## 非主键索引的查询一定会回表吗?
不一定,当查询语句的要求字段全部命中索引,不用回表查询。如select 主键 from 非主键=XX,此时非主键索引叶子节点即可拿到主键信息,不用回表。
## 简述MySQL使用EXPLAIN 的关键字段
explain关键字用于分析sql语句的执行情况,可以通过他进行sql语句的性能分析。
type:表示连接类型,从好到差的类型排序为
- system:系统表,数据已经加载到内存里。
- const:常量连接,通过索引一次就找到。
- eq_ref:唯一性索引扫描,返回所有匹配某个单独值的行。
- ref:非主键非唯一索引等值扫描,const或eq_ref改为普通非唯一索引。
- range:范围扫描,在索引上扫码特定范围内的值。
- index:索引树扫描,扫描索引上的全部数据。
- all:全表扫描。
key:显示MySQL实际决定使用的键。
key_len:显示MySQL决定使用的键长度,长度越短越好
Extra:额外信息
- Using filesort:MySQL使用外部的索引排序,很慢需要优化。
- Using temporary:使用了临时表保存中间结果,很慢需要优化。
- Using index:使用了覆盖索引。
- Using where:使用了where。
## 简述MySQL优化流程
1. 通过慢日志定位执行较慢的SQL语句
2. 利用explain对这些关键字段进行分析
3. 根据分析结果进行优化
## 简述MySQL中的日志log
redo log: 存储引擎级别的log(InnoDB有,MyISAM没有),该log关注于事务的恢复.在重启mysql服务的时候,根据redo log进行重做,从而使事务有持久性。
undo log:是存储引擎级别的log(InnoDB有,MyISAM没有)保证数据的原子性,该log保存了事务发生之前的数据的一个版本,可以用于回滚,是MVCC的重要实现方法之一。
bin log:数据库级别的log,关注恢复数据库的数据。
## 简述事务
事务内的语句要么全部执行成功,要么全部执行失败。
事务满足如下几个特性:
* 原子性(Atomicity):
>一个事务中的所有操作要么全部完成,要么全部不完成。
* 一致性(Consistency):
>事务执行前后数据库的状态保存一致。
* 隔离性(Isolation)
>多个并发事务对数据库进行操作,事务间互不干扰。
* 持久性(Durability)
>事务执行完毕,对数据的修改是永久的,即使系统故障也不会丢失
## 数据库中多个事务同时进行可能会出现什么问题?
* 丢失修改
* 脏读:当前事务可以查看到别的事务未提交的数据。
* 不可重读:在同一事务中,使用相同的查询语句,同一数据资源莫名改变了。
* 幻读:在同一事务中,使用相同的查询语句,莫名多出了一些之前不存在的数据,或莫名少了一些原先存在的数据。
## SQL的事务隔离级别有哪些?
* 读未提交:
>一个事务还没提交,它做的变更就能被别的事务看到。
* 读提交:
>一个事务提交后,它做的变更才能被别的事务看到。
* 可重复读:
>一个事务执行过程中看到的数据总是和事务启动时看到的数据是一致的。在这个级别下事务未提交,做出的变更其它事务也看不到。
* 串行化:
>对于同一行记录进行读写会分别加读写锁,当发生读写锁冲突,后面执行的事务需等前面执行的事务完成才能继续执行。
## 什么是MVCC?
MVCC为多版本并发控制,即同一条记录在系统中存在多个版本。其存在目的是在保证数据一致性的前提下提供一种高并发的访问性能。对数据读写在不加读写锁的情况下实现互不干扰,从而实现数据库的隔离性,在事务隔离级别为读提交和可重复读中使用到。
在InnoDB中,事务在开始前会向事务系统申请一个事务ID,该ID是按申请顺序严格递增的。每行数据具有多个版本,每次事务更新数据都会生成新的数据版本,而不会直接覆盖旧的数据版本。数据的行结构中包含多个信息字段。其中实现MVCC的主要涉及最近更改该行数据的事务ID(DB_TRX_ID)和可以找到历史数据版本的指针(DB_ROLL_PTR)。InnoDB在每个事务开启瞬间会为其构造一个记录当前已经开启但未提交的事务ID的视图数组。通过比较链表中的事务ID与该行数据的值与对应的DB_TRX_ID,并通过DB_ROLL_PTR找到历史数据的值以及对应的DB_TRX_ID来决定当前版本的数据是否应该被当前事务所见。最终实现在不加锁的情况下保证数据的一致性。
## 读提交和可重复读都基于MVCC实现,有什么区别?
在可重复读级别下,只会在事务开始前创建视图,事务中后续的查询共用一个视图。而读提交级别下每个语句执行前都会创建新的视图。因此对于可重复读,查询只能看到事务创建前就已经提交的数据。而对于读提交,查询能看到每个语句启动前已经提交的数据。
## InnoDB如何保证事务的原子性、持久性和一致性?
利用undo log保障原子性。该log保存了事务发生之前的数据的一个版本,可以用于回滚,从而保证事务原子性。
利用redo log保证事务的持久性,该log关注于事务的恢复.在重启mysql服务的时候,根据redo log进行重做,从而使事务有持久性。
利用undo log+redo log保障一致性。事务中的执行需要redo log,如果执行失败,需要undo log 回滚。
## MySQL是如何保证主备一致的?
MySQL通过binlog(二进制日志)实现主备一致。binlog记录了所有修改了数据库或可能修改数据库的语句,而不会记录select、show这种不会修改数据库的语句。在备份的过程中,主库A会有一个专门的线程将主库A的binlog发送给
备库B进行备份。其中binlog有三种记录格式:
1. statement:记录对数据库进行修改的语句本身,有可能会记录一些额外的相关信息。优点是binlog日志量少,IO压力小,性能较高。缺点是由于记录的信息相对较少,在不同库执行时由于上下文的环境不同可能导致主备不一致。
2. row:记录对数据库做出修改的语句所影响到的数据行以及对这些行的修改。比如当修改涉及多行数据,会把涉及的每行数据都记录到binlog。优点是能够完全的还原或者复制日志被记录时的操作。缺点是日志量占用空间较大,IO压力大,性能消耗较大。
3. mixed:混合使用上述两种模式,一般的语句使用statment方式进行保存,如果遇到一些特殊的函数,则使用row模式进行记录。MySQL自己会判断这条SQL语句是否可能引起主备不一致,如果有可能,就用row格式,
否则就用statement格式。但是在生产环境中,一般会使用row模式。
## redo log与binlog的区别?
1. redo log是InnoDB引擎特有的,只记录该引擎中表的修改记录。binlog是MySQL的Server层实现的,会记录所有引擎对数据库的修改。
2. redo log是物理日志,记录的是在具体某个数据页上做了什么修改;binlog是逻辑日志,记录的是这个语句的原始逻辑。
3. redo log是循环写的,空间固定会用完;binlog是可以追加写入的,binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
## crash-safe能力是什么?
InnoDB通过redo log保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
## WAL技术是什么?
WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。事务在提交写入磁盘前,会先写到redo log里面去。如果直接写入磁盘涉及磁盘的随机I/O访问,涉及磁盘随机I/O访问是非常消耗时间的一个过程,相比之下先写入redo log,后面再找合适的时机批量刷盘能提升性能。
## 两阶段提交是什么?
为了保证binlog和redo log两份日志的逻辑一致,最终保证恢复到主备数据库的数据是一致的,采用两阶段提交的机制。
1. 执行器调用存储引擎接口,存储引擎将修改更新到内存中后,将修改操作记录redo log中,此时redo log处于prepare状态。
2. 存储引擎告知执行器执行完毕,执行器生成这个操作对应的binlog,并把binlog写入磁盘。
3. 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交commit状态,更新完成。
## 只靠binlog可以支持数据库崩溃恢复吗?
不可以。
历史原因:
1. InnoDB在作为MySQL的插件加入MySQL引擎家族之前,就已经是一个提供了崩溃恢复和事务支持的引擎了。InnoDB接入了MySQL后,发现既然binlog没有崩溃恢复的能力,那引入InnoDB原有的redo log来保证崩溃恢复能力。
实现原因:
2. binlog没有记录数据页修改的详细信息,不具备恢复数据页的能力。binlog记录着数据行的增删改,但是不记录事务对数据页的改动,这样细致的改动只记录在redo log中。当一个事务做增删改时,其实涉及到的数据页改动非常细致和复杂,包括行的字段改动以及行头部以及数据页头部的改动,甚至b+tree会因为插入一行而发生若干次页面分裂,那么事务也会把所有这些改动记录下来到redo log中。因为数据库系统进程crash时刻,磁盘上面页面镜像可以非常混乱,其中有些页面含有一些正在运行着的事务的改动,而一些已提交的事务的改动并没有刷上磁盘。事务恢复过程可以理解为是要把没有提交的事务的页面改动都去掉,并把已经提交的事务的页面改动都加上去这样一个过程。这些信息,都是binlog中没有记录的,只记录在了存储引擎的redo log中。
3. 操作写入binlog可细分为write和fsync两个过程,write指的就是指把日志写入到文件系统的page cache,并没有把数据持久化到磁盘,fsync才是将数据持久化到磁盘的操作。通过参数设置sync_binlog为0的时候,表示每次提交事务都只write,不fsync。此时数据库崩溃可能导致部分提交的事务以及binlog日志由于没有持久化而丢失。
## 简述MySQL主从复制
MySQL提供主从复制功能,可以方便的实现数据的多处自动备份,不仅能增加数据库的安全性,还能进行读写分离,提升数据库负载性能。
主从复制流程:
1. 在事务完成之前,主库在binlog上记录这些改变,完成binlog写入过程后,主库通知存储引擎提交事物
2. 从库将主库的binlog复制到对应的中继日志,即开辟一个I/O工作线程,I/O线程在主库上打开一个普通的连接,然后开始binlog dump process,将这些事件写入中继日志。从主库的binlog中读取事件,如果已经读到最新了,线程进入睡眠并等待ma主库产生新的事件。
读写分离:即只在MySQL主库上写,只在MySQL从库上读,以减少数据库压力,提高性能。
![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
\ No newline at end of file
# 数据结构与算法
## 简述数据结构栈
栈是一种线性表,其限制只能在表尾进行插入或删除操作。由于该特性又称为后进先出的线性表。
## 简述数据结构队列
队列是一种先进先出的线性表。其限制只能在线性表的一端进行插入,而在另一端删除元素。
## 简述二叉树
二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成。
## 简述满二叉树
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
## 简述完全二叉树
一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
## 简述二叉树的前中后序遍历算法
前序遍历:若二叉树为空树,则执行空逻辑,否则:
1. 访问根节点
2. 递归前序遍历左子树
3. 递归前序遍历右子树
中序遍历:若二叉树为空树,则执行空逻辑,否则:
1. 递归中序遍历左子树
2. 访问根节点
3. 递归中序遍历右子树
后序遍历:若二叉树为空树,则执行空逻辑,否则:
1. 递归后序遍历左子树
2. 递归后序遍历右子树
3. 访问根节点
## 简述解决Hash冲突的方法
开放定址法:当发生哈希冲突时,如果哈希表未被装满,那么可以把这个值存放到冲突位置中的下一个空位置中去
链地址法:对相同的哈希地址,设置一个单链表,单链表内放的都是哈希冲突元素。
## 简述AVL树
AVL树是一种改进版的搜索二叉树,其引入平衡因子(左子支高度与右子支高度之差的绝对值),通过旋转使其尽量保持平衡。
任何一个节点的左子支高度与右子支高度之差的绝对值不超过1。
## 简述红黑树
红黑树本身是有2-3树发展而来,红黑树是保持黑平衡的二叉树,其查找会比AVL树慢一点,添加和删除元素会比AVL树快一点。增删改查统计性能上讲,红黑树更优。
红黑树主要特征是在每个节点上增加一个属性表示节点颜色,可以红色或黑色。红黑树和 AVL 树类似,都是在进行插入和删除时通过旋转保持自身平衡,从而获得较高的查找性能。红黑树保证从根节点到叶尾的最长路径不超过最短路径的 2 倍,所以最差时间复杂度是 O(logn)。红黑树通过重新着色和左右旋转,更加高效地完成了插入和删除之后的自平衡调整。
## 简述稳定排序和非稳定排序的区别
稳定排序:排序前后两个相等的数相对位置不变,则算法稳定
非稳定排序:排序前后两个相等的数相对位置发生了变化,则算法不稳定
## 常见的稳定排序算法有哪些
插入排序、冒泡排序、归并排序
## 常见的不稳定排序算法有哪些
希尔排序、直接选择排序、堆排序、快速排序
## 简述插入排序
插入排序:每一趟将一个待排序记录按其关键字的大小插入到已排好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。
排序算法稳定。时间复杂度 O(n²),空间复杂度 O(1)。
## 简述希尔排序
希尔排序:把记录按下标的一定增量分组,对每组进行直接插入排序,每次排序后减小增量,当增量减至 1 时排序完毕。
排序算法不稳定。时间复杂度 O(nlogn),空间复杂度 O(1)。
## 简述直接选择排序
直接选择排序:每次在未排序序列中找到最小元素,和未排序序列的第一个元素交换位置,再在剩余未排序序列中重复该操作直到所有元素排序完毕。
排序算法不稳定。时间复杂度 O(n²),空间复杂度 O(1)。
## 简述堆排序
堆排序:将待排序数组看作一个树状数组,建立一个二叉树堆。通过对这种数据结构进行每个元素的插入,完成排序工作。
排序算法不稳定,时间复杂度 O(nlogn),空间复杂度 O(1)。
## 简述冒泡排序
冒泡排序:比较相邻的元素,如果第一个比第二个大就进行交换,对每一对相邻元素做同样的工作。
排序算法稳定,时间复杂度 O(n²),空间复杂度 O(1)。
## 简述快速排序
快速排序:随机选择一个基准元素,通过一趟排序将要排序的数据分割成独立的两部分,一部分全部小于等于基准元素,一部分全部大于等于基准元素,再按此方法递归对这两部分数据进行快速排序。
排序算法不稳定,时间复杂度 O(nlogn),空间复杂度 O(logn)。
## 简述归并排序
归并排序:将待排序序列分成两部分,然后对两部分分别递归排序,最后进行合并。
排序算法稳定,时间复杂度都为 O(nlogn),空间复杂度为 O(n)。
## 简述图
图是由顶点集合和顶点之间的边集合组成的一种数据结构,分为有向图和无向图。
有向图:边具有方向性
无向图:边不具有方向性
## 简述邻接矩阵
用一个二维数组存放图顶点间关系的数据,这个二维数组称为邻接矩阵。
对于无向图,邻接矩阵是对称矩阵
## 简述邻接表
邻接表是通过链表表示图连接关系的一种方。对于表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。
## 简述图的深度优先搜索DFS
将图中每个顶点的访问标志设为 FALSE, 之后搜索图中每个顶点,如果未被访问,则以该顶点V0为起始点出发,访问此顶点,然后依次从V0的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和V0有路径相通的顶点都被访问到。
## 简述图的广度优先搜索
从图中的某个顶点V0出发,并在访问此顶点之后依次访问V0的所有未被访问过的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V0有路径相通的顶点都被访问到。
## 简述最小生成树和其对应的算法
对于有 n 个结点的原图,生成原图的极小连通子图,其包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
普里姆算法:取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点 w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n-1 个顶点为止。
克鲁斯卡尔算法:先构造一个只含 n 个顶点的子图 SG,然后从权值最小的边开始,若它的添加不使 SG 中产生回路,则在 SG 上加上这条边,如此重复,直至加上 n-1 条边为止。
## 简述最短路径算法
Dijkstral算法为求解一个点到其余各点最小路径的方法,其算法为:
假设我们求解的是顶点v到其余各个点的最短距离。n次循环至n个顶点全部遍历:
1. 从权值数组中找到权值最小的,标记该边端点k
2. 打印该路径及权值
3. 如果存在经过顶点k到顶点i的边比v->i的权值小
4. 更新权值数组及对应路径
## 简述堆
堆是一种完全二叉树形式,其可分为最大值堆和最小值堆。
最大值堆:子节点均小于父节点,根节点是树中最大的节点。
最小值堆:子节点均大于父节点,根节点是树中最小的节点。
## 简述set
Set是一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。
![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
# 计算机网络
## 简述OSI七层协议
OSI七层协议包括:物理层,数据链路层,网络层,运输层,会话层,表示层, 应用层
## 简述TCP/IP五层协议
TCP/IP五层协议包括:物理层,数据链路层,网络层,运输层,应用层
## 物理层有什么作用
主要解决两台物理机之间的通信,通过二进制比特流的传输来实现,二进制数据表现为电流电压上的强弱,到达目的地再转化为二进制机器码。网卡、集线器工作在这一层。
## 数据链路层有什么作用
在不可靠的物理介质上提供可靠的传输,接收来自物理层的位流形式的数据,并封装成帧,传送到上一层;同样,也将来自上层的数据帧,拆装为位流形式的数据转发到物理层。这一层在物理层提供的比特流的基础上,通过差错控制、流量控制方法,使有差错的物理线路变为无差错的数据链路。提供物理地址寻址功能。交换机工作在这一层。
## 网络层有什么作用
将网络地址翻译成对应的物理地址,并决定如何将数据从发送方路由到接收方,通过路由选择算法为分组通过通信子网选择最佳路径。路由器工作在这一层。
## 传输层有什么作用
传输层提供了进程间的逻辑通信,传输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个传输层实体之间有一条端到端的逻辑通信信道。
## 会话层有什么作用
建立会话:身份验证,权限鉴定等;
保持会话:对该会话进行维护,在会话维持期间两者可以随时使用这条会话传输局;
断开会话:当应用程序或应用层规定的超时时间到期后,OSI会话层才会释放这条会话。
## 表示层有什么作用
对数据格式进行编译,对收到或发出的数据根据应用层的特征进行处理,如处理为文字、图片、音频、视频、文档等,还可以对压缩文件进行解压缩、对加密文件进行解密等。
## 应用层有什么作用
提供应用层协议,如HTTP协议,FTP协议等等,方便应用程序之间进行通信。
## TCP与UDP区别
TCP作为面向流的协议,提供可靠的、面向连接的运输服务,并且提供点对点通信
UDP作为面向报文的协议,不提供可靠交付,并且不需要连接,不仅仅对点对点,也支持多播和广播
## 为何TCP可靠
TCP有三次握手建立连接,四次挥手关闭连接的机制。
除此之外还有滑动窗口和拥塞控制算法。最最关键的是还保留超时重传的机制。
对于每份报文也存在校验,保证每份报文可靠性。
## 为何UDP不可靠
UDP面向数据报无连接的,数据报发出去,就不保留数据备份了。
仅仅在IP数据报头部加入校验和复用。
UDP没有服务器和客户端的概念。
UDP报文过长的话是交给IP切成小段,如果某段报废报文就废了。
## 简述TCP粘包现象
TCP是面向流协议,发送的单位是字节流,因此会将多个小尺寸数据被封装在一个tcp报文中发出去的可能性。
可以简单的理解成客户端调用了两次send,服务器端一个recv就把信息都读出来了。
## TCP粘包现象处理方法
固定发送信息长度,或在两个信息之间加入分隔符。
## 简述TCP协议的滑动窗口
滑动窗口是传输层进行流量控制的一种措施,接收方通过通告发
送方自己的窗口大小,从而控制发送方的发送速度,防止发送方发送速度过快而导致自己被淹没。
## 简述TCP协议的拥塞控制
拥塞是指一个或者多个交换点的数据报超载,TCP又会有重传机制,导致过载。
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量.
当cwnd < ssthresh 时,使用慢开始算法。
当cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
当cwnd = ssthresh 时,即可使用慢开始算法,也可使用拥塞避免算法。
慢开始:由小到大逐渐增加拥塞窗口的大小,每接一次报文,cwnd指数增加。
拥塞避免:cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1。
快恢复之前的策略:发送方判断网络出现拥塞,就把ssthresh设置为出现拥塞时发送方窗口值的一半,继续执行慢开始,之后进行拥塞避免。
快恢复:发送方判断网络出现拥塞,就把ssthresh设置为出现拥塞时发送方窗口值的一半,并把cwnd设置为ssthresh的一半,之后进行拥塞避免。
## 简述快重传
如果在超时重传定时器溢出之前,接收到连续的三个重复冗余ACK,发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,不需要等待超时重传定时器溢出再发送该报文。
## TCP三次握手过程
1. 第一次握手:客户端将标志位SYN置为1,随机产生一个值序列号seq=x,并将该数据包发送给服务端,客户端
进入syn_sent状态,等待服务端确认。
2. 第二次握手:服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务端将标志位SYN和
ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端以确认连接请求,服务端进入syn_rcvd状态。
3. 第三次握手:客户端收到确认后检查,如果正确则将标志位ACK为1,ack=y+1,并将该数据包发送给服务端,服务端进行检查如果正确则连接建立成功,客户端和服务端进入established状态,完成三次握手,随后客户端和服务端之间可以开始传输
数据了
## 为什么TCP握手需要三次,两次行不行?
不行。TCP进行可靠传输的关键就在于维护一个序列号,三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值。
如果只是两次握手, 至多只有客户端的起始序列号能被确认, 服务器端的序列号则得不到确认。
## 简述半连接队列
TCP握手中,当服务器处于SYN_RCVD 状态,服务器会把此种状态下请求连接放在一个队列里,该队列称为半连接队列。
## 简述SYN攻击
SYN攻击即利用TCP协议缺陷,通过发送大量的半连接请求,占用半连接队列,耗费CPU和内存资源。
优化方式:
1. 缩短SYN Timeout时间
2. 记录IP,若连续受到某个IP的重复SYN报文,从这个IP地址来的包会被一概丢弃。
## TCP四次挥手过程
1. 第一次挥手:客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入fin_wait_1状态。
2. 第二次挥手:服务端收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1,服务端进入Close_wait状态。此时TCP连接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。
3. 第三次挥手:服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端进入Last_ack状态。
4. 第四次挥手:客户端收到FIN后,客户端进入Time_wait状态,接着发送一个ACK给服务端,确认后,服务端进入Closed状态,完成四次挥手。
## 为什么TCP挥手需要4次
主要原因是当服务端收到客户端的 FIN 数据包后,服务端可能还有数据没发完,不会立即close。
所以服务端会先将 ACK 发过去告诉客户端我收到你的断开请求了,但请再给我一点时间,这段时间用来发送剩下的数据报文,发完之后再将 FIN 包发给客户端表示现在可以断了。之后客户端需要收到 FIN 包后发送 ACK 确认断开信息给服务端。
## 为什么四次挥手释放连接时需要等待2MSL
MSL即报文最大生存时间。设置2MSL可以保证上一次连接的报文已经在网络中消失,不会出现与新TCP连接报文冲突的情况。
## 简述DNS协议
DNS协议是基于UDP的应用层协议,它的功能是根据用户输入的域名,解析出该域名对应的IP地址,从而给客户端进行访问。
## 简述DNS解析过程
1、客户机发出查询请求,在本地计算机缓存查找,若没有找到,就会将请求发送给dns服务器
2、本地dns服务器会在自己的区域里面查找,找到即根据此记录进行解析,若没有找到,就会在本地的缓存里面查找
3、本地服务器没有找到客户机查询的信息,就会将此请求发送到根域名dns服务器
4、根域名服务器解析客户机请求的根域部分,它把包含的下一级的dns服务器的地址返回到客户机的dns服务器地址
5、客户机的dns服务器根据返回的信息接着访问下一级的dns服务器
6、这样递归的方法一级一级接近查询的目标,最后在有目标域名的服务器上面得到相应的IP信息
7、客户机的本地的dns服务器会将查询结果返回给我们的客户机
8、客户机根据得到的ip信息访问目标主机,完成解析过程
## 简述HTTP协议
http协议是超文本传输协议。它是基于TCP协议的应用层传输协议,即客户端和服务端进行数据传输的一种规则。该协议本身HTTP 是一种无状态的协议。
## 简述cookie
HTTP 协议本身是无状态的,为了使其能处理更加复杂的逻辑,HTTP/1.1 引入 Cookie 来保存状态信息。
Cookie是由服务端产生的,再发送给客户端保存,当客户端再次访问的时候,服务器可根据cookie辨识客户端是哪个,以此可以做个性化推送,免账号密码登录等等。
## 简述session
session用于标记特定客户端信息,存在在服务器的一个文件里。
一般客户端带Cookie对服务器进行访问,可通过cookie中的session id从整个session中查询到服务器记录的关于客户端的信息。
## 简述http状态码和对应的信息
1XX:接收的信息正在处理
2XX:请求正常处理完毕
3XX:重定向
4XX:客户端错误
5XX:服务端错误
常见错误码:
301:永久重定向
302:临时重定向
304:资源没修改,用之前缓存就行
400:客户端请求的报文有错误
403:表示服务器禁止访问资源
404:表示请求的资源在服务器上不存在或未找到
## 转发和重定向的区别
转发是服务器行为。服务器直接向目标地址访问URL,将相应内容读取之后发给浏览器,用户浏览器地址栏URL不变,转发页面和转发到的页面可以共享request里面的数据。
重定向是利用服务器返回的状态码来实现的,如果服务器返回301或者302,浏览器收到新的消息后自动跳转到新的网址重新请求资源。用户的地址栏url会发生改变,而且不能共享数据。
## 简述http1.0
规定了请求头和请求尾,响应头和响应尾(get post)
每一个请求都是一个单独的连接,做不到连接的复用
## 简述http1.1的改进
HTTP1.1默认开启长连接,在一个TCP连接上可以传送多个HTTP请求和响应。使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
服务端无法主动push
## 简述HTTP短连接与长连接区别
HTTP中的长连接短连接指HTTP底层TCP的连接。
短连接: 客户端与服务器进行一次HTTP连接操作,就进行一次TCP连接,连接结束TCP关闭连接。
长连接:如果HTTP头部带有参数keep-alive,即开启长连接网页完成打开后,底层用于传输数据的TCP连接不会直接关闭,会根据服务器设置的保持时间保持连接,保持时间过后连接关闭。
## 简述http2.0的改进
提出多路复用。多路复用前,文件时串行传输的,请求a文件,b文件只能等待,并且连接数过多。引入多路复用,a文件b文件可以同时传输。
引入了二进制数据帧。其中帧对数据进行顺序标识,有了序列id,服务器就可以进行并行传输数据。
## http与https的区别
http所有传输的内容都是明文,并且客户端和服务器端都无法验证对方的身份。
https具有安全性的ssl加密传输协议,加密采用对称加密,
https协议需要到ca申请证书,一般免费证书很少,需要交费。
## 简述TLS/SSL, HTTP, HTTPS的关系
SSL全称为Secure Sockets Layer即安全套接层,其继任为TLSTransport Layer Security传输层安全协议,均用于在传输层为数据通讯提供安全支持。
可以将HTTPS协议简单理解为HTTP协议+TLS/SSL
## https的连接过程
1. 浏览器将支持的加密算法信息发给服务器
2. 服务器选择一套浏览器支持的加密算法,以证书的形式回发给浏览器
3. 客户端(SSL/TLS)解析证书验证证书合法性,生成对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,用服务器的公钥对客户端密钥进行非对称加密。
4. 客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端对称密钥发送给服务器
5. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
6. 服务器将加密后的密文发送给客户端
7. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成
## Get与Post区别
Get:指定资源请求数据,刷新无害,Get请求的数据会附加到URL中,传输数据的大小受到url的限制。
Post:向指定资源提交要被处理的数据。刷新会使数据会被重复提交。post在发送数据前会先将请求头发送给服务器进行确认,然后才真正发送数据。
## Get方法参数有大小限制吗
一般HTTP协议里并不限制参数大小限制。但一般由于get请求是直接附加到地址栏里面的,由于浏览器地址栏有长度限制,因此使GET请求在浏览器实现层面上看会有长度限制。
## 了解REST API吗
REST API全称为表述性状态转移(Representational State Transfer,REST)即利用HTTP中get、post、put、delete以及其他的HTTP方法构成REST中数据资源的增删改查操作:
- Create : POST
- Read : GET
- Update : PUT/PATCH
- Delete: DELETE
## 浏览器中输入一个网址后,具体发生了什么
1. 进行DNS解析操作,根据DNS解析的结果查到服务器IP地址
2. 通过ip寻址和arp,找到服务器,并利用三次握手建立TCP连接
3. 浏览器生成HTTP报文,发送HTTP请求,等待服务器响应
4. 服务器处理请求,并返回给浏览器
5. 根据HTTP是否开启长连接,进行TCP的挥手过程
6. 浏览器根据收到的静态资源进行页面渲染
![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
\ No newline at end of file
# 设计模式
## 简述设计模式七大原则
开放封闭原则:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。
单一职责原则:一个类、接口或方法只负责一个职责,降低代码复杂度以及变更引起的风险。
依赖倒置原则:针对接口编程,依赖于抽象类或接口而不依赖于具体实现类。
接口隔离原则:将不同功能定义在不同接口中实现接口隔离。
里氏替换原则:任何基类可以出现的地方,子类一定可以出现。
迪米特原则:每个模块对其他模块都要尽可能少地了解和依赖,降低代码耦合度。
合成复用原则:尽量使用组合(has-a)/聚合(contains-a)而不是继承(is-a)达到软件复用的目的。
## 简述设计模式的分类
创建型模式:在创建对象的同时隐藏创建逻辑,不使用 new 直接实例化对象。有工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式:通过类和接口间的继承和引用实现创建复杂结构的对象。有适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式:通过类之间不同通信方式实现不同行为。有策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
## 简述简单工厂模式
简单工厂模式指由一个工厂对象来创建实例,适用于工厂类负责创建对象较少的情况。例子:Spring 中的 BeanFactory 使用简单工厂模式,产生 Bean 对象。
## 简述工厂模式
工厂方法模式指定义一个创建对象的接口,让接口的实现类决定创建哪种对象,让类的实例化推迟到子类中进行。例子:Spring 的 FactoryBean 接口的 getObject 方法也是工厂方法。
## 简述抽象工厂模式
抽象工厂模式指提供一个创建一系列相关或相互依赖对象的接口,无需指定它们的具体类。例子:java.sql.Connection 接口。
## 简述单例模式
一个单例类在任何情况下都只存在一个实例。
饿汉式实现
```java
public class Singleton {
private Singleton(){}
private static Singleton instance
= new Singleton();
public static Singleton getInstance() {
return instance;
}
}
```
懒汉式实现
```java
public class Singleton {
private DoubleCheckSingleton(){}
private volatile static
Singleton instance;
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
```
## 简述代理模式
代理模式为其他对象提供一种代理以控制对这个对象的访问。优点是可以增强目标对象的功能,降低代码耦合度,扩展性好。缺点是在客户端和目标对象之间增加代理对象会导致请求处理速度变慢,增加系统复杂度。
静态代理:在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
动态代理:程序运行期间动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
## 简述适配器模式
适配器模式将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。
## 简述模板模式
模板模式定义了一个操作中的算法的骨架,并将一些步骤延迟到子类,适用于抽取子类重复代码到公共父类。
可以封装固定不变的部分,扩展可变的部分。但每一个不同实现都需要一个子类维护,会增加类的数量。
## 简述装饰器模式
装饰者模式可以动态地给对象添加一些额外的属性或行为,即需要修改原有的功能,但又不愿直接去修改原有的代码时,设计一个Decorator套在原有代码外面。
## 简述观察者模式
观察者模式表示的是一种对象与对象之间具有依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
>>![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
---
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册