计算机实训-期末考试复习
面试准备-知识储备
数据结构
一、优先级队列
Java中:PriorityQueue
- 特性?:
是一种特殊的队列。每一个元素都有一个优先级。当出队操作时,队列会按照元素优先级的高低顺序从队列中取出一个元素并删除。 - 实现原理?:
堆(如二叉堆)等数据结构来实现。 - 使用场景?:
任务调度、事件处理等场景。(确保优先级高的任务或事件先被处理,从而提高系统的响应速度和效率。)
点击查看代码
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer a,Integer b){
return b-a;//此时是大根堆,默认是小根堆
}
});
PriorityQueue的peek()和element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)。
二、堆和树
-
堆和树的关系?
堆总是一颗完全二叉树。(因此可以层序的规则采用顺序的方式来高效存储)(也就是说,完全二叉树适合使用数组进行存储,层序遍历。一般二叉树不适合顺序方式进行存储,浪费存储空节点的空间)(左孩子2i+1,右孩子2i+2) -
堆的左旋和右旋?(错了吧,下面是AVL树的左右旋)
平衡二叉树不平衡时:左左-右旋。右右-左旋。左右-左旋再右旋。右左-右旋再左旋。 -
堆的调整?
如果parent比chi1和chi2都小,循环结束。否则进入循环(parent与chi1和chi2较小者进行交换->继续向下调整parent)OlogN -
堆的插入?
插入到最后,然后慢慢进行,向上调整(如果新节点小于它的父节点,则交换,继续向上遍历) -
堆的删除?
根节点和最后一个元素交换,弹出根节点。(交换上去的节点循环向下调整,即可) -
除了红黑树还有什么平衡树?
AVL树(二叉搜索树的基础上加伤平衡的限制)。2-3tree(2种节点:2节点有1个key2条边,左小于根右大于根。3节点有2个key3条边,左小于key1中在key1和key2之间右大于key2)。B树,B+树。 -
红黑树为什么是平衡树?
红黑树是一种自平衡的二叉搜索树,它的每一个节点有一个存储位标示颜色,通过对路径上的颜色约束,红黑树保证没有一条路径比其它路径长2倍,因此是接近平衡的。 -
红黑树的5条规则:
- 每一个节点非红即黑
- 根节点是黑色的
- null节点是黑色。(所有叶子节点都是黑色的)
- 红色节点不相邻
- 从任意一个节点到叶子节点(不包括这个节点),黑色节点数量相同。(这个规则也叫红黑树的黑高)
红黑树通过变色、左旋、右旋来修复规则被破坏的情况。
- 红黑树的插入和删除(还不太了解)???
插入:
- 插入位置是根节点:直接把这个节点变成黑色
- 插入位置的父亲节点是黑色,直接插入新节点即可(默认红色)
- 插入位置的父节点是红色:
- 节点的叔叔节点是红色:父和叔变黑色,爷变红色
- 节点的叔叔节点是黑色:1以爷为中心左旋,2新的爷变黑新的兄弟变红
删除:
用后继节点替换删除节点,只会影响后继节点所在的子树。
然后对后继节点所在的子树进行变色操作。
使用:增offer,删poll
操作系统
一、进程、线程、协程
- 进程和线程的区别?
- 进程是资源调度的最小单位,线程是CPU调度(运行调度)的最小单位。
- 进程作为程序独立运行的载体保障程序正常执行。
- 进程的存在使得操作系统资源利用率大幅提升。
- 进程目的:方便操作系统管理
- 线程,比进程更小的独立运行的基本单位。
- 线程包含在进程之中,是进程中实际运行工作的单位
- 一个进程可以并发多个线程,每个线程执行不同的任务。
- 进程控制块PCB,线程控制块TCB
- 线程目的:提高并发量、吞吐量。
- 关系:进程的线程共享进程资源。
- 线程和协程的区别?
Coroutines协程
问题:在多核场景下,如果是IO密集型场景,就算有多个线程来处理,也未必能提升CPU的利用率,反而会增加线程切换的开销。另外,多线程之间假如存在临界区或者共享内存,那么同步的开销也是不可忽视的。
协程:轻量级的线程。
- 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。
- 一个线程可以拥有多个协程,协程不是被操作系统内核所管理,而是完全由程序所控制。
- 与其让操作系统调度,不如让我来,这就是协程。
- 比线程更小的粒度,运行效率更高,可以支持更高的并发。
- 优缺点:可以减少上下文切换的成本,无法发挥CPU的多核优势,协程主要运用在多IO的场景。
重要的是,协程不是被操作系统内核所管理,而是完全由程序所控制(也就是在用户态执行)
好处:性能得到很大的提升,不会像线程切换那样消耗资源。
协程和线程的主要区别是它将不再被内核调度,而是交给程序自己,二线程是将自己交给内核调度,所以也不难理解golang中调度器的存在。
总结:为了提升用户线程的最大利用效率,提出了协程的概念。
- 线程间通信?
- 互斥量(锁)
- 条件变量
- 事件:事件机制,允许一个线程在处理完一个任务后,主动唤醒另一个线程执行任务。
- 进程间通信?
- 管道:半双工,只能在一个方向上流动,有固定的读端和写端。
- 信号:通知接收进程某个事件已经发生。
- 消息队列:消息的链表,是一系列保存在内核中消息的列表。
- 共享内存:多个进程将同一块物理内存(用户空间)映射到自己的虚拟地址空间当中。
- 套接字:此方法主要用于在客户端和服务器之间通过网络进行通信。
- 为什么线程更高效?(错了吧,切换更高效吧?)
- 每个进程都有自己的虚拟地址空间,把虚拟地址转化为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用Cache来缓存常用的地址映射,这里叫TLB。每个进程都有自己的页表,当进程切换时,页表也要切换,页表切换后缓存TLB就失效了,导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来就是程序运行会变慢。
而线程切换不会导致TLB失效,因为线程无需切换地址空间。因此我们通常说线程切换比进程切换快,原因就在这里。
虚拟内存技术:
虚拟内存是操作系统为每一个进程提供的一种抽象,每个进程都有属于自己的、私有的、地址连续的虚拟内存。当然我们知道最终进程的数据及代码必然要放到物理内存上,那么必须有某种机制能记住虚拟内存对物理内存的地址空间映射。操作系统通过页表记住这种映射关系。有了页表就可以将虚拟内存地址转换为物理内存地址了,这种机制就是虚拟内存。
-
线程的生命周期:创建,就绪,运行,阻塞,终止。
-
进程调度算法:
- 先到先服务
- 短作业优先
- 时间片轮转
- 优先级
- 多级反馈队列
- 为什么多线程有线程安全问题?如何解决?
原因:
- 修改共享内存。
- 事务不保证原子性。一个线程对变量的操作过程中可能被其它线程打断。
- 内存可见性:每个线程都有自己的工作内存(私有),同步回主内存中存在延迟。
- 多线程中的指令集重排
解决:
- 加锁,synchronized
- 引入volatile关键字,保证内存可见性。(但volatile不保证原子性,适用一读一写,多写的情况还是会出错)能够防止指令集重排
- 引入wait和notify。wait需要搭配synchronized来使用。
wait和sleep的区别:
- wait是用于线程间通信的,sleep是让线程阻塞一段时间
- wait需要搭配synchronized使用,sleep不需要
- wait是object方法,sleep是thread的静态方法。
相同:都可以让线程放弃执行一段时间。
- 什么是僵尸进程?会占用CPU吗?孤儿进程?
不占用cpu。
子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。
- 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
- 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init(进程号为1)所收养,并由init进程对它们完成状态收集工作。
孤儿进程并不会有危害:init接管其原父进程的作用,init进程会循环的wait,给孤儿进程善后。
任何一个子进程(init除外)在exit之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。(包括进程号,退出状态,运行时间等)如果用ps命令查看的话,会有很多状态为Z的进程。如果不调用wait/waitpid的话,进程号一直不释放,但系统所能使用的进程号是有限的,可能导致没有可用的进程号而使得系统不能产生新的进程。
解决:僵死进程不是问题的根源。罪魁祸首是他们的父进程,我们把产生大量僵死进程的父进程毙掉(通过kill发送SIGTERM或者SIGKILL信号)。它的僵死进程就变成了孤儿进程,被init进程接管,init给它们善后。
二、锁
- 说说你对锁的了解?
- java中:synchronized关键字,Lock接口的实现类(ReentrantLock可重入锁,ReadLock读锁,WriteLock写锁)(ReadWriteLock其实是一个工厂接口,ReentrantReadWriteLock是ReadWriteLock的实现类,它包含2个静态内部类,ReadLock和WriteLock,这两个类又分别实现了Lock接口)
- 乐观锁和悲观锁:并不是特指某个锁,而是在并发情况下的2种策略。悲观锁阻塞事务,乐观锁回滚重试。
- 唯一的乐观锁-CAS:CAS是实现自旋锁的基础。CAS利用CPU指令,从硬件层面保证了操作的原子性。(java.util.concurrent.atomic包里的原子类)(乐观锁根本不是锁,它根本没锁住对象,而是一个无限充实的算法而已)(乐观锁==自旋锁)(ABA问题-在栈中可能带来问题,引入版本号)
- synchronized锁升级:无锁-偏向锁(第一个线程)-轻量级锁(2个线程竞争,另1个忙等)-重量级锁(忙等太久了,挂起等待唤醒)
- 可重入锁(java中reentrant开头命名的锁)
- 公平锁、非公平锁。(synchronized是非公平锁,且不能变成公平锁)
- 可中断锁:java中只是发送中断信号,何时中断、是否中断取决于获取锁的线程。
- 读写锁、共享锁(读锁)、互斥锁(写锁)
- 死锁条件?
四个:
- 互斥条件:每个资源只能被一个进程使用
- 占有等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不可抢占:进程已经获得的资源,未使用完,不能强行剥夺。
- 循环等待:若干进程之间形成一条头尾相接的循环等待资源关系。(根本条件)
三、操作系统杂项
- while 1这个语句操作系统层面让它停止?Java层面让它停止?
- 通过kill命令,首先使用ps命令查找正在执行的命令的进程ID(PID),然后使用kill PID命令终止该进程的执行。
- 通过任务管理器,图形化界面,选择结束任务或终止进程选项。
- 虚拟内存?(对物理内存的补充,速度接近于内存,成本接近于辅存)(上面有详细的虚拟内存介绍)
- 页表,段页?
- 页式存储管理
- 将进程逻辑空间等分成若干大小的页面
- 相应的把物理内存空间分成与页面大小的物理块
- 以页面为单位把进程空间装进物理内存中分散的物理块
- 页表记录进程逻辑空间与物理空间的映射
- 页面大小应该适中,过大难以分配,过小页表管理空间大。页面大小通常512B~8K
有一段连续的逻辑分布在多个页面中,将大大降低执行效率
- 段式存储管理
- 将进程逻辑空间划分成若干份(非等分)
- 段的长度由连续逻辑的长度决定
- 主函数MAIN,子程序段X,子函数Y等
段式存储管理相比业式存储管理更加灵活
- 段页式存储管理
- 分页管理有效提高内存利用率
- 分段管理更加灵活
- 两者结合,形成段页式存储管理。
- 先将逻辑空间按段式管理分成若干段
- 再把段内空间按页式管理分成若干页
缺页中断
在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存时,会产生一次缺页中断。此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。
- 磁盘属于外设,读写磁盘需要系统调用。
- 保护CPU现场-分析中断原因-中断处理-恢复CPU环境
- 在指令执行期间产生和处理中断信号
- 一条指令执行期间,可能产生多次缺页中断
- 用户态、内核态?
用户态:
- 用户空间:存放的是用户程序的代码和数据
- 进程在执行用户自己的代码(非系统调用之类的函数)
- CPU只可以访问有限的内存,不允许访问外设
内核态: - 内核空间:存放的是内核代码和数据
- 进程执行操作系统内核的代码
- CPU可以访问内存所有数据,包括外围设备
内核态 -> 用户态切换: - 系统调用、异常中断、外围设备中断。
计算机网络
一、HTTP、HTTPS
- HTTP协议常见的状态码?
- 200~299 成功
- 200 OK,请求没问题
- 204 No Content,响应报文没有实体的主体部分
- 300~399 重定向
- 304 Not Modified,所请求的资源未修改。
- 400~499 客户端错误
- 400 Bad Request,客户端请求的语法错误。
- 401 Unauthorized,客户端未对自己进行认证
- 403 Forbidden,请求被服务器拒绝了
- 404 Not Found,服务器无法找到请求的URL
- 500~599 服务端错误
- 500 Internal Server Error,服务器内部错误
- 502 Bad Gate way,网关或代理服务器从远程服务器接收到了一个无效的响应。
- 503 Service Unavailabel,服务器无法为该请求提供服务
- 504 Gatewaty Timeout,网关或代理服务器,没有及时从远端服务器获取请求。
- 502、500怎么排查?
- HTTP有哪些常见的方法?
- get:请求服务器发送某个资源(幂等
- head:与get类似,只返回首部(作用:判断响应码是否正常,检查某些资源是否改变)(幂等
- post:向服务器写入数据
- put:向服务器写入资源
- trace:观察请求报文到达服务器的最终样子
- delete:请求服务器删除请求url所指定的资源
- options:(代理、防火墙等会使用到)让服务器返回资源支持操作的方法。
- HTTP报文结构?
- 1请求报文
- 请求方法+请求地址+HTTP版本
- 请求头(编码方式、语言)
- 请求内容
- 2响应报文
- 请求地址
- 状态码/状态解释
- 应答头(持久连接还是非持久、内容长度、日期)
-
GET可以新增吗?GET和POST的区别?POST和PUT的区别?
GET是请求服务器发送某个资源,不能让服务器新增资源。
GET请求资源,POST写入数据
POST写入数据,PUT写入资源 -
HTTPS是怎么实现安全的?
HTTP存在的问题:
- 通信使用明文(不加密),内容可能被窃听。
- 不验证通信方的身份,因此有可能遭遇伪装。
HTTPS采用SSL后,拥有了加密、证书、完整性保护这些功能。
SSL(Secure Sockets Layer安全套接字协议)及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议
HTTPS = HTTP + TLS/SSL
SSL的实现方式:
- 对称加密:采用协商的密钥对数据加密
- 非对称加密:实现身份认证和密钥协商
- 摘要算法:验证信息的完整性(哈希函数SHA-2)
- 数字签名:身份验证
HTTPS超文本传输安全协议,使用HTTP进行通信,但利用SSL/TLS来加密数据包。所以它又叫:HTTP over TLS/SSL
特性:
- 保密性:客户端的连接被加密,隐藏了URL、cookie和其它敏感元数据。
- 对称加密 and 非对称加密:
- HTTPS数据通过对称加密传输
- 对称加密的密钥通过非对称加密传输
- 真实性:确保客户端正在与真实的服务端通信,而非中间人
- 数字证书 and 签名:
- 1.首先站点的所有者生成一个密钥对,然后掏钱将站点的信息如域名、组织信息等以及公钥提交给CA(Certification Authority认证权威)机构审核,即证书签名请求CSR。
- 2.CA机构审核通过后,用它独有的私钥对CSR信息(其实是CSR信息的hash值,用于加速加、解密)进行加密,即形成数字签名,用于验证证书是否被篡改,经过签名后一个完整的数字证书就成了其中包含站点信息、数字签名。
- 申请到了数字证书,安装到server中
- 1.当client请求时,server返回数字证书,先查看证书认证的域名所有者是谁?如果与你访问的域名不一致,毫无疑问你正在遭受中间人攻击,这是一个假站点请停止访问。
- 2.如果一致,接着client查看证书的签发CA机构是谁?找到浏览器或操作系统中对应的内置CA公钥,找不到?对不起,这个站点不安全(这其实也是垄断恶化付费的根源),如果找到则使用公钥解密签名得到hash值和此时证书中的CSR信息的hash值做对比。如果一致,则这个证书没有被修改,你访问的站点很安全,取出证书中的公钥来做加密通信吧。
- 准确性:客户端与服务端之间发送的数据没有被篡改
HTTPS不保护的信息?
- 虽然HTTPS对整个HTTP请求和响应进行加密,但DNS解析和连接监听仍然可以获得一些其他信息,例如完整的域名或子域以及原始IP地址
- 别有用心者还可能通过分析加密的HTTPS流量以获取特殊信息比如在网站上花费的时间,或用户数据包相对大小。
攻击HTTPS连接有多难?
攻击分为3类:
- 通过密码分析或其他协议的弱点破坏HTTPS连接的质量
- 黑掉客户端,将恶意根证书安装到系统或浏览器信任库中。
- 获得浏览器信任的“流氓”证书,即通过操纵或破坏证书颁发机构。
- HTTPS的握手过程?
HTTP 3次握手,4次挥手--基于TCP
HTTPS:
- 1客户端发起一个hello client请求,请求中会携带TLS版本信息、加密套件候选列表、压缩算法候选列表以及一个随机数
- 2服务端收到请求后也会给客户端发一个server hello请求,请求中会告诉客户端它选择的协议版本,加密套件,压缩算法以及一个随机数。
- 3服务端会给客户端发一个server certificate请求,里面包含服务端的数字证书,用于客户端进行校验。
- 4服务端会给客户端发一个sever hello done告诉客户端信息已经发送完毕。
- 5客户端收到证书以后进行校验获取到服务端的公钥
- 6客户端会将自己的数字证书发送给服务端用于校验
- 7客户端计算出一个随机数pre-master,然后用公钥进行加密发送给服务器端。
- 8服务端和客户端都根据自己的随机数+对端的随机数+pre-master算出对称密钥,然后再根据对称密钥进行通信。
- 为什么HTTPS最后采用会话密钥来加密数据?(不知道想问什么?)
为什么不是对称加密 or 非对称加密?
- 对称加密:密钥在初始的传输过程中可能被截获
- 非对称加密:黑客可以截获信息,通过公钥解密。
- HTTP为什么是无状态(如何理解)?HTTPS是无状态吗?
无状态是指协议对于事物处理没有记忆能力,不能保存每次客户端提交的信息,即当服务器返回应答之后,这次事务的所有信息就都丢掉了。(如果用户发来一个新的请求,服务器也无法知道它是否与上次的请求有联系)
优点:
- 服务器不用为每个客户端连接分配内存来记忆大量状态,也不用在客户端失去连接时去清理内存,节省服务器端资源,以更高效地去处理业务。
缺点:
- 缺少状态意味着如果后续处理需要前面的信息,则客户端必须重传,这样可能导致每次连接传送的数据量增大。
解决HTTP无状态的方法:
四种会话跟踪技术:cookie,session,url重写,作为隐藏域嵌入HTML表单中(隐藏表单域)
HTTPS也是无状态的。
-
HTTP的cookie存放在哪里?cookie中有哪些结构?
存放客户端
结构:name:cookie的名称。value:cookie对应的值。domain:服务器域名。expiry:cookie有效终止日期。 -
详细说一下HTTP协议
超文本传输协议,网络通信协议,它允许将超文本标记语言HTML文档从web服务器传送到客户端的浏览器。
端口80,应用层协议,传输层用的tcp。和其他应用层协议一样,是为了实现某一类具体应用的协议。
- HTTP协议是一个无状态、无连接、单向的应用层协议,它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求作出应答处理。
- 弊端:HTTP协议无法实现服务器主动向客户端发起消息。
- 一个get走https,如果我在中间代理服务器抓到这个包,请求参数会被加密吗?
会被加密,HTTPS对整个HTTP请求和响应加密。
二、cookie、session、token
- cookie和session的区别?
- cookie:第一次访问时,服务器给客户端发送cookie,在响应头中以set-cookie首部字段标记。
- cookie的失效:失效时间expire默认-1,关闭浏览器失效,改为0,立即失效不保存cookie
- sesison保存在服务器上,记录客户端信息。
- 首次请求,请求中不带sessionId,服务端生成sessionId返回给客户端作为cookie保存下来。
- 根据sessionId在服务端内存中找到session,获取用户信息。
- 除了cookie,另外两种方式保存sessionId:url重写,表单中隐藏字段。
- session设置有效期,超过这个时间不活跃的session,服务器将其删除。(被动失效)
- session也可以主动失效:
区别:
- 存放位置不同:cookie在客户端,session在服务端
- 安全性:cookie存储在浏览器对客户端可见,客户端的某些应用程序可能会窥探、复制甚至修改cookie的内容
- 安全性:session存储在服务器端,对客户端是透明的,不存在敏感信息泄漏的风险。
- 因此建议重要、敏感信息不要保存在cookie,或者将cookie加密,服务端对cookie解密,确保cookie只有服务端可以读懂
- 有效期:cookie可以设置为保存很久甚至永久,session会定期被清理。
- session会随着访问量的增大使得服务器压力越来越大。
-
session保存在什么地方?
-
自定义token怎么控制过期时间?
token在身份认证中是令牌(临时)的意思,在词法分析中是标记的意识。一般作为邀请、登陆系统使用。 -
HTTP的cookie存放在哪里?cookie中有哪些结构?
-
token如何强制失效?
三、TCP、UDP
-
TCP建立连接的过程?3次握手?
-
TCP协议断开连接的过程?4次挥手?为什么不是3次?为什么时间是2MSL?
-
MSL是什么?具体时间你知道吗?
maximum segment lifetime 最大报文生存时间
RFC 793规定MSL为2min,实际应用30s,1min,2min -
TCP与UDP?
- udp:原端口目的端口+16位udp长度+16位udp校验和
- tcp:原端口目的端口+序号+确认号+控制位+窗口
- udp性能负载低,速度快,实现简单,适用于简单场景,无连接,不可靠。tcp相反
- udp:DNS、TFTP简单文件传送协议、DHCP动态主机配置协议
- tcp:SMTP,FTP,TELNET远程终端协议,HTTP
局域网地址VPN
四、对称加密、非对称加密
- 对称加密、非对称加密?
- 密钥:一个--一对
- 效率:高--低
- 安全性:较高--更高
- 管理成本:高--低
- 常见算法:DES、3DES、AES--RSA、DH、ECC
五、浏览器键入网址全过程
- 浏览器键入网址全过程?输入1个url到展示页面的过程?
简单来说,7步:
- 1 查找浏览器缓存。如果查找到缓存中有我们URL对应的文件,则判断是否命中强缓存,如果命中直接读取使用即可,未命中则进行下面步骤
- 2 DNS域名解析。将输入的URL解析成对应的IP地址
- 先查询浏览器的DNS缓存,缓存中维护一张域名与IP地址的对应表。若没有命中,将继续搜索操作系统的DNS缓存。
- 仍未命中:操作系统将域名发送到本地域名服务器。本地域名服务器查询自己的DNS缓存。(递归)
- 仍未命中:迭代向下查询(防止根域名服务器压力过大)向根域名服务器请求,获得顶级域名服务器的地址。
- 本地域名服务器迭代向顶级域名服务器请求,获得权威域名服务器地址
- 本地域名服务器迭代向权威域名服务器请求,获得最终IP
- 本地域名服务器将IP返回给操作系统,同时自己将IP地址缓存起来。
- 操作系统将IP地址返回给浏览器,同时自己将IP地址缓存起来
- 至此,浏览器就得到了域名对应的IP地址,并将IP地址缓存起来
3~7:参考http的工作原理
- 3 生成HTTP请求报文。80端口请求报文包括起始行、首部、主体
- 4 TCP连接。客户端与服务端进行TCP三次握手,建立连接。
- 5 发送HTTP请求。握手成功后,客户端向服务端发送http请求,请求数据。
- 6 服务器收到请求并返回数据。客户端根据返回的结果进行渲染展示,同时判断是否需要将文件存入缓存
- 7 TCP断开连接。客户端与服务端进行TCP4次挥手,断开连接
六、子网掩码、websocket、DNS?
- 子网掩码的作用?
-
用来指明一个IP地址的哪些位标识的是主机所在的子网,以及哪些位标识的是主机号掩码。
-
子网掩码不能单独存在,它必须结合IP地址一起使用
-
子网掩码只有一个作用,就是将某个IP地址划分成网络地址和主机地址2部分。
-
发方与收方的网络地址相同,属于同一网段,将在本地网络内转发
-
不相同,则发到对方的网关 192.168.80.1,再由网关发送给路由器转发出去。
- websocket的原理?
- 目的:解决HTTP协议无法实现服务器主动向客户端发起消息的弊端
- 特点:websocket连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端
- websocket只需要建立一次连接,就可以一直保持连接状态。
- 双向、性能高
关于websocket
- 建立在TCP协议之上
- 与HTTP协议有着良好的兼容性,默认端口也是80和443,并且握手阶段采用HTTP协议,因此握手不容易屏蔽,能通过各种HTTP代理服务器。
- 数据格式比较轻量、性能开销小、通信高效
- 可以发送文本、也可以发送二进制数据
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符ws,如果加密则是wss,服务器网址就是url
底层协议包装:
HTTP和websocket有什么关系?
新协议,并无关系。只是规范了现有浏览器的握手规范而已。也就是说它是HTTP协议上的一种补充
- DNS的算法?
指的是:DNS服务器在收到DNS请求后,如果在本地查询到结果,则直接返回结果。但如果在本地查询不到结果的情况下,采取何种策略获取请求这所需要的信息。
DNS:Domain Name System 域名系统:存储域名和IP地址相互映射关系的一个分布式数据库,它能够使人方便地访问互联网。
- 递归:深度优先:
- 迭代:广度优先:
客户机和服务器之间的查询就是迭代查询(客户端-本地DNS服务器)
服务器之间的查询是迭代查询(本地DNS服务器-根、顶级、权威域名服务器)
七、五层、七层模型
- TCP/IP5层模型说一下?每层作用?
- 应用层:定义了运行在不同端系统上的应用程序进程如何相互传递报文。(定义了进程交换的报文类型、报文的语法、字段的含义、进程如何发生数据、怎样发送数据等等)
- 传输层:提供主机间不同进程的通信,向上面的应用层提供通信服务,并屏蔽下面的核心网络细节。使得面向传输层编程就像是在2个主机进程之间有一条端到端的逻辑通信信道一样。当采用TCP是,这条逻辑通信信道就是一条可靠的通信信道,尽管下面的网络是不可靠的。建立端口到端口的通信(端口范围0~65535,0~1023为系统占用端口)
- 网络层:主机间通信。目的是向上提供简单灵活的、无连接的、尽最大努力交付的数据报服务,网络层不提供服务质量的承诺
- 数据链路层:定义了电信号的分组方式(电信号多少位一组,每组什么意思)统一的标准:以太网协议ethernat
- 物理层:基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0
- OSI七层模型?每层作用?
- 应用层:HTTP、FTP、SMTP
- 表示层:负责数据的格式转换和加密解密,常见的协议JPEG,ASCII等
- 会话层:建立、管理和终止会话,确保数据的可靠传输。协议:RPC、NetBIOS等
- 传输层:TCP、UDP
- 网络层:IP、ICMP
- 数据链路层:以太网、PPP等
- 物理层:RS-232、V.35等
- 路由器在哪一层?交换机在哪一层?
路由器:网络层。
交换机:数据链路层
MySQL
存储引擎
-
mysql的b树和b+树?
-
mysql为啥用b+树存储而不是b树?
- 非叶子节点仅存放索引,更矮胖,磁盘IO更少
- 大量冗余节点(非叶子节点都是冗余索引),插入、删除效率高。不会像B树那样发生复杂的树的变化。
- B+树的叶子节点通过双向链表连接起来,有利于范围查询。
- innodb中索引是怎么存储的?
innodb:索引和数据存在一起、聚簇索引
- db.opt:数据库默认字符集、字符校验规则
- .frm:表结构
- .ibd:表数据(这个文件也叫独占表空间文件)
innodb表空间文件的结构:(小到大)
- 行:记录是按行存储的
- 页:16KB,按页读
- 区:1MB,64个页,利于范围查询
- 段:索引段、数据端、回滚段
MyISAM:索引和数据分开、非聚簇索引
- MYI:存索引(MyISAM中:主键索引、辅助索引在结构上无区别)
- MYD:存数据
索引
- 为什么我的sql已经命中索引了还是很慢?怎么解决?
- 索引字段重复值或者空值太多。
- 查询条件太广返回结果数太多,全索引扫描。
- 没有用到覆盖索引,造成大量回表
- 表中包含多个索引,命中的索引不是最优的索引
- mysql中索引的个数有上限吗?
- 单表的索引不超过5个,单个索引中的字段数不超过5个(这句话是错误的)
- MySQL本身没有对单表的索引数量进行限制。所以我们建立索引时只需从业务维度出发,针对业务上需要进行查询的维度建立索引,避免建立无效或低效的索引即可。
- mysql索引注意事项?
- 小表不需要加索引,数据量小的表会增加存储空间,减缓查询速度。
- 频繁更新的表不适合加过多的索引,因为每次更新操作都需要重建索引,虽然查询速度加快,但更新却会变慢。
优化索引的方式:
- 前缀索引优化:减少索引字段的大小。(局限:order by无法使用前缀索引。无法将前缀索引用作覆盖索引)
- 覆盖索引优化:不用查询出包含整行记录的所有信息,也就减少了大量IO操作
- 主键索引最好是自增的:插入记录是追加操作,不需要移动数据。(如果是非自增,随机插入,页分裂,内存碎片)
- 索引最好设置为not null:因为可以使索引更加复杂,null没意义但是会占用物理空间。
- 防止索引失效:
- 左模糊匹配or左右模糊匹配。
- 在查询条件时对索引进行计算、函数、类型转换。
- 联合索引未遵循最左匹配原则。
- WHERE子句中,OR前是索引列,OR后不是索引列。
- mysql联合索引在B+tree中怎么存储的?
a-b索引:
- 先按a排序
- a相同的情况下,按b排序
- 非叶子节点:a,b,索引页号
- 叶子节点:a,b,主键值
-
索引是什么?
索引是一种数据结构,用于加快数据库查询的速度和性能。
分类(按数据结构:B+树,Hash,Full-text)(按物理存储:聚簇,辅助)(按字段特性:主键,唯一,普通,前缀)(字段个数:单列,联合) -
聚簇索引和非聚簇索引?
- 聚簇:叶子节点存放数据(根据主键构造的索引)(尽量选择有序的id)
- 非聚簇:叶子节点存放主键值(可能回表or覆盖索引)(也叫二级索引、辅助索引)
innodb主键索引-聚簇索引(一个表只有一个聚簇索引)(如果没有主键,innodb选一个非空的唯一索引代替)。二级索引-非聚簇
MyISAM:都是非聚簇。(不支持聚簇索引)
- 还有一下关键字like orderby走索引的情况?
like左模糊匹配 and 左右模糊匹配 不走索引
like 右模糊匹配(前缀匹配)走索引
-
左模糊匹配并不是一定不走索引的!如果数据库表中的字段只有主键+二级索引,
-
type=index全扫描二级索引树。因为查二级索引树就可以找到全部结果了。同时二级索引树记录的东西少。
-
但如果数据表加了非索引字段,走的就是全表扫描了
-
同理:联合索引不遵循最左匹配原则,如果数据库中的字段都是索引的话,也会全扫描二级索引树 type=index
-
结论:后置百分号可以用到索引,前置百分号和两侧百分号用不了索引。
orderby 看用不用回表:
- select a from ? order by a 不用回表,走索引a,type=index
- 如果select?用到的数据,索引中无法全部获取,则需要回表,优化:直接全表扫描type=all。
- 给一个联合索引abc select * from user where a in (xxx) and c>= xxx and b=xxx走索引情况?把a改成等于是什么情况?深入问了一下索引失效场景?
in()
- 括号里只有一个主键时,type=const主键索引
- 里面有多个主键时,type=range,走索引,范围扫描
- 更多时,type=all,全表扫描
在联合索引中:
-
应该用了in还是会走索引吧???因为可以找到边界索引项(即in里的最小值)
-
小tip:最左匹配原则失效,根据已经查到的索引行回表查询主键索引(类似全表扫描,但不是全表)
改为等于:
走联合索引
索引失效的场景:上面有!
事务 and 隔离级别 and MVCC
- mysql的隔离级别有哪些?
- 未提交读
- 已提交读:MyISAM默认的隔离级别。彻底解决脏读
- 可重复读:InnoDB默认的隔离级别。彻底解决脏读,不可重复读(部分解决幻读)
- 串行化:彻底解决脏读,不可重复读,幻读
-
可重复读举个例子?
事务A、B开启事务。(关闭了事务的自动提交)。A查到x=100。B将x改为50
A查到x还是100。B事务commit后,A查到x还是100。 -
mysql事务?
- ACID属性
- 并行事务引发的问题:脏读,不可重复读,幻读。
- 事务的隔离级别,如何实现的。
- 事务开始的时机,并不是“开始事务”,而是之后执行的第一条SELECT语句
- mysql的MVCC?
是什么:
通过版本链(事务的Read View里的字段和记录中2个隐藏列的对比)来控制并发事务访问同一个记录时的行为
2个隐藏列:
trx_id:事务id
roll_pointer:指向undo log中的旧版本记录
- 已提交读:每次读取数据时,都会生成一个新的Read View
- 可重复读:启动事务生成一个Read View,整个事务期间都用这个read View。
- Read View里的4个字段:
- create_trx_id :创建该read view的事务的id
- m_ids :创建read view时,当前数据库中的活跃事务(启动了但还没提交的事务)的事务id列表
- min_trx_id :m_ids的最小值。
- max_trx_id: 全局事务中最大的事务id值+1。
慢SQL的查询 and 解决
- 慢sql怎么查询?怎么解决?
工具:MySQL Slow Query Log
- 首先,找到慢SQL,这就用到慢查询日志了
- show variables like ‘%slow_query%’
得到 - 把slow_query_log的值改为on
- show variables like 'long%'
- 得到
- 把set long_query_time = 1;单位是秒
- 超过long_query_time的SQL会被记录在慢查询日志host_name-slow.log当中
- 接下来需要用explain分析一下该SQL的执行计划了,根据具体情况找出SQL和索引该怎么去优化
(show profile命令可以查看sql具体的运行时间,全局变量的名字是:profiling - 分析完
怎么解决:
优化索引:
- 添加索引
- 修改查询条件
- 分解查询语句
- 上面有具体的优化索引方式
- explain一般要关注哪些消息?
- 主要看possile_keys, key, key_len(这三个一般套起来分析)
- 还有就是extra,type(看全表扫描还是索引,还是索引范围扫描)
- explain有哪些字段?
- type字段:扫描方式是什么
- All 全表扫描
- index 全索引扫描
- range 索引范围扫描(通常使用此级别以下的type)
- ref 非唯一索引扫描
- eq_ref 唯一索引扫描(多表联查)
- const 结果只有一条的主键或唯一索引扫描(常用于与常量进行比较)
- extra字段:
- using index:覆盖索引,不回表,尽量覆盖,可以提高效率
- using filesort:需要额外的排序,不能通过索引得到排序结果,尽量避免这种情况,会使得速度很慢
- possile_key:可能使用的索引
- key:实际使用的索引
- key_len:使用索引字段的长度
表结构
- mysql中表的字段有上限吗?
有
- 表的列数:5.7版本后65535列
- 行数据的大小:InnoDB限制的是最大支持存储在一页中的数据大小16KB
- mysql死锁场景说一下?出现死锁的情况?
死锁发生的原因:
两个个事务都进行了当前读,对同一个区间同时加了间隙锁(不会冲突,目的是阻止区间被插入,预防幻读),然后又都想在该间隙中插入数据(生成插入意向锁),此时双方都在等待对方释放间隙锁,循环等待,发生死锁。
死锁的必要条件:
互斥,占有等待,不可抢占,循环等待
避免死锁:
数据库层面:2个策略打破循环等待(发生后的消除办法)
- 设置事务等待锁的超时时间。 innodb_lock_wait_timeout 默认50秒。
- 开启主动死锁检测。 innodb_deadlock_detect 设置为on,默认开启。
还有:死锁预防,死锁避免,死锁检测,死锁消除。
日志
-
redo log和undo log哪个先提交?
undo log也需要redo log保证持久化。
buffer pool中有undo页,对undo页的修改也会记录到redo log中。redo log每秒刷盘,提交事务时也会刷盘。数据页和undo页都是通过这个机制保证持久化的。 -
redo log和undo log的作用?
redo log记录的是此次事务完成后的数据状态,记录的是更新之后的值。解决事务提交后发生的崩溃。
undo log记录的是此次事务开始前的数据状态,记录的是更新之前的值。解决事务提交前发生的崩溃。
ACID中的持久性:crash-safe崩溃恢复能力靠: redo log + WAL 实现。
WAL(Write-Ahead Logging)技术:
MySQL的写操作不是立刻写到磁盘上的,而是先写日志,然后在合适的时间写到磁盘上。
- redo log
- undo log
- 实现事务回滚,保证事务的原子性:出现错误或ROLLBACK。
- 实现MVCC(Read View + undo log)的关键因素之一。快照读(普通SELECT)时,根据事务Read View里的信息,顺着undo log找到满足其可见性的记录。
- redo log
- 确保事务的持久性。以防在发生故障的时间点,还有脏页未写入磁盘,在重启mysql的时候,根据redo log进行重做,从而达到事务的持久性这一特性。
具体sql分析
- 怎么分析一条sql?(explain分析,上面有)
- explain的细节?给你一个explain命令让你分析?(上面有)
- 给你一条sql你会怎么去优化它?(上面有)
- select ... for update 加不加锁,当前读(加锁)
- select id from *** where id=** 加不加锁(innoDB可重复读,快照读ReadView,MVCC,不加锁)
- 如果mysql在执行时,cpu使用率过高,在主从下要如何解决?
可能是索引,索引并非越多越好,过多的索引会导致CPU使用率居高不下,由于数据的改变,会造成索 引文件的改动,过多的磁盘I/O造成CPU负荷太重。
在主从下(读写分离了,压力相对小):
- 查询语句问题。太复杂,优化查询语句
- 死锁。(上面有解决方法)
Redis
数据类型 and 数据结构
- 用过哪些redis的数据结构?用什么结构实现延迟消息队列?
- 数据结构:SDS,IntSet,Dict,ZipList,QuickList,SkipList,RedisObject
- 数据类型:String,List,Set,ZSet,Hash
- 延迟消息队列:ZSet,核心思想是在程序中开启一个一直循环的延迟任务的检测器,用于检测和调用延迟任务的执行。
- 方法1:zrangebyscore查询所有任务,然后再进行执行
- 方法2:判断最早的任务,再与当前时间进行判断,如果任务执行时间大于当前时间则表示应该立即执行延迟任务
- redis的底层数据结构?字符串怎么弄的、hash怎么做的、分布式锁怎么做的?
- 字符串:编码方式int,embstr,raw
- int(type,encoding,lru,refcount,ptr里面直接存放数据)
- embstr(type,encoding,lru,refcount,ptr后面紧接着SDS)
- raw(type,encoding,lru,refcount,ptr指向SDS)
- 详细说一下SDS:
- 简单动态字符串Simple Dynamic String
- 动态扩容:如果新字符串小于1M,则新空间为扩展后字符串长度的2倍+1(alloc=新字符串长度的2倍)+(结束字符\0的1位)
- 如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1。称为内存预分配。
- alloc=新空间大小-1(除去结束标示)
- 属性:len:buf已保存的字符串字节数,不包含结束标示。alloc:buf申请的总的字节数,不包含结束结束标示。flag:不同SDS的头类型,用来控制SDS的头大小。buf[]
- hash:编码方式压缩列表,ht
- 默认采用ZipList编码,以节省内存,ZipList中相邻的2个entry分别保存field和value
- 当数据量较大时,Hash结构会转为HT编码,也就是Dict,触发条件有2个:
- ziplist中的元素超过了hash-max-ziplist-entry(默认512)
- ziplist中的任意entry大小超过了hash-max-ziplist-value(默认64字节)
- dict:看另一篇博客
- ZipList:看另一篇博客
- 分布式锁:
- 像setnx这样的命令(不可重入
- redisson:(可重入)redis-map数据类型(key:lock名,value(hash:field线程名,val:次数)
- redis的数据类型有哪些?说一下redis的hashmap的底层实现?
(看另一篇博客) - Hash结构可以设置过期时间吗?
可以,expire - bitmap的使用场景?
- 位图。并非一种独立的数据结构,而是基于String提供的功能。可用于如二值状态的场景中,基于bit的01来存储状态
统计用户是否在线。统计用户每个月签到情况。优惠券限领1张
表结构相关
- key的大小有限制吗?
key的大小限制是512M,但一般建议key的大小不要超过1KB
String类型的value的最大值也是512M,集合、链表、哈希等key类型,单个元素的value上限也是512M - 怎么解决BigKey问题?
bigkey问题:key对应的value很大(非常占内存)
String类型的值大于10KB,Hash、List、Set、ZSet类型的元素个数超过5000个
- 原因:1数据结构不合理,没有合适地切分数据。2批量写入数据到一个key中。3缓存失效策略不合理,key长时间没有被清除
- 带来的问题:客户端超时阻塞。引发网络阻塞。阻塞工作线程。内存分布不均。
- 如何找到bigkey:使用 RdbTools 第三方开源工具,可以用来解析 Redis 快照(RDB)文件,找到其中的大 key。
- 如何删除bigkey:分批次删除。异步删除(利用unlink命令代替del来删除)
- 如何解决bigkey问题:分割bingkey。使用hash数据结构。使用redis分布式特性。
分片集群
- redis分片集群,如何分片的?有什么好处?
- 解决的问题:海量数据存储问题。高并发写问题
- 如何分片:数据key和插槽hash slot绑定。每一个master节点拥有0~16383号插槽的部分插槽。
- redis根据key的有效部分计算插槽值(如果有{}就用里面的,没有就用全部的key)。计算出的哈希值对16384取余。
- 余数作为插槽,寻找插槽所在的实例
- 好处:1海量数据存储。2高并发写
redis过期删除 and 内存淘汰策略?
- redis过期删除和内存淘汰策略?
- 内存淘汰:不用自己维护,利用redis的内存淘汰机制,当内存不足时自动淘汰部分数据,下次查询时更新缓存。(一致性-差,维护成本-无)
- 超时剔除:给缓存数据添加TTL时间,到期后自动删除缓存,下次查询时更新缓存。(一致性-一般,维护成本-低)
- 主动更新:编写业务逻辑,在修改数据库数据库的同时,更新缓存。(一致性-好,维护成本-高)
- 主动更新策略:1更新数据库同时更新缓存(用的最多)。2缓存与数据库整合为一个服务。3调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库。
- 更新数据库同时更新缓存:删除缓存而不是更新缓存。
- 如何保证同时成功 and 失败:1单体系统,将缓存与数据库操作放在一个事务。2分布式系统,利用TCC等分布式事务方案。
- (线程安全问题)先删缓存还是先操作数据库:一般先操作数据库,再删除缓存。(更安全:缓存双删:删写删)
redis为什么快
- redis为什么快?
- 完全基于内存
- 数据结构优
- 单线程,避免上下文切换 and 竞争
- 多路IO复用模型,非阻塞IO
使用场景
- 缓存击穿、缓存穿透解决方案?
- 击穿:互斥锁。逻辑过期。
- 穿透:null。布隆。
- redis用在哪,分布式锁、缓存
发布 and 订阅
- redis发布和订阅你在项目哪里用了?
(消息队列的实现方式?)
消息队列
特性
- RocketMQ有哪些特性?
组成:producer,broker,consumer三部分。生产、存储、消费消息。
topic一类消息的集合,订阅的基本单位。namesever帮助找到topic对应的broker。
- 顺序消息:分区有序 or 全局有序
- 延时消息
- 批量消息:前提是这些消息有相同的topic
- 延迟队列你清楚吗?
底层实现:
- 18个delay_topic对应18中延迟级别,18个队列(最低1s最高2h)
- 定时任务每1s遍历18个delay_topic
- 如果消费时间>当前时间,就把消息重发给real_topic这个队列,消费者才可以消费。
使用场景
- 消息队列的使用场景
作用:解耦、异步、削峰
场景:异步处理、应用解耦、流量削峰、日志处理、消息通讯
RocketMQ VS Kafka
- Kafka RocketMQ区别,用在哪?
- 性能:kafka性能好,TPS百万条/s。RocketMQ7万/s
- 消费失败重试:kafka不支持重试。rocketMQ消费失败支持定时重试。
- 事务消息:kafka不支持,rocketMQ支持。
- 延迟消息:kafka不支持,rocketMQ支持。
- 开发语言:kafka是Scala,RocketMQ是java
Kafka作用?丢消息怎么办?怎么知道丢消息?
kafka是一个分布式流式处理平台。
作用:
- 消息队列
- 容错的持久方式存储记录消息流:kafka会把消息持久化到磁盘,有效避免消息丢失的风险
应用场景:
- 消息队列
- 数据处理:构建实时的流数据处理程序来转换或处理数据流。
- Kafka是用来干嘛?丢消息怎么办?怎么知道丢消息了
存在丢消息的情况:可能发生在broker,producer,consumer
- broker:异步刷盘,批量刷盘导致丢消息。(解决:减少刷盘间隔。
- 为了解决该问题,kafka通过producer和broker协同处理单个broker丢失参数的情况。一旦producer发现broker消息丢失,即可自动进行retry。除非retry次数超过阀值(可配置),消息才会丢失。此时需要生产者客户端手动处理该情况。那么producer是如何检测到数据丢失的呢?是通过ack机制,类似于http的三次握手的方式。
- producer:producer在发送数据时可以将多个请求进行合并后发送。
- 解决:改为同步发送消息。
- consumer:Consumer自动提交的机制是根据一定的时间间隔,将收到的消息进行commit。commit过程和消费消息的过程是异步的。也就是说,可能存在消费过程未成功(比如抛出异常),commit消息已经提交了。此时消息就丢失了。
- 解决:提交类型改为手动
- 怎么知道丢消息了:
- ???
消息队列选型问题,消息可靠性保证?
- 消息队列,选型问题,消息可靠性保证?
可靠性:
- producer:同步阻塞式发送,同步检查brocker返回的状态来判断消息是否持久化成功
- broker:同步刷盘。(消息写入pagechache之后,立刻通知刷盘线程刷盘)
- consumer:消费返回成功才可以,最多尝试16次,然后放入死信队列,可以通过相关接口从死信队列获取到响应的消息。
- 消息回溯,已经消费过的消息,如果发现逻辑有问题,可以再次消费(没过期的情况下)
项目12306
Java
Java基础
- java里数组和链表的区别是什么?遍历时性能的差异?
遍历性能:都是On。实际数组比链表快。原因:
- CPU缓存会把一片连续的内存空间读入, 因为数组结构是连续的内存地址, 所以数组全部或者部分元素被连续存在CPU缓存里面
- 链表的节点是分散的,这时候CPU缓存帮不上忙,只能是去读取内存
- 而缓存的速率要比内存快。
- CPU --》寄存器--》缓存 --》内存
- java里的hashmap怎么实现的?
-
数据结构Dict:
三层结构:dict-dictht-dictentry(字典-哈希表-哈希节点) -
dict:type(内置不同哈希函数),privdata(私有数据,哈希运算使用),ht[2](包含2个哈希表,0记录当前数据,1为空,rehash使用),rehashidx(rehash的进度)
-
dictht:**table(指向entry数组,数组中保存的是只想entry的指针),size(哈希表大小),sizemask(哈希表大小的掩码,=size-1),used(entry个数)
-
dictentry:key(键),val(值),*next(下一个entry的指针)
-
扩容:LoadFactor>=1且服务器没有执行后台进程。或者LoadFactor>5。第一个大于等于used+1的2^n
-
收缩:LoadFactor<0.1。第一个大于等于used的2^n
-
rehash:按新的size申请内存,创建dictht,赋值给dict.ht[1]。设置rehashidx=0标示开始rehash。将ht0每一个dictEntry都rehash到ht1。将ht1赋值给ht0,ht1初始化为空哈希表,释放原来ht0的内存。
-
渐进式rehash:每次增删改是,检查rehashidx是否>=-1,将idx下标的ht0中的元素rehash到ht1,将idx++,直到ht0所有元素都rehash到ht1。rehashidx赋值为-1,代笔rehash结束。(新增操作直接写入ht1,保证ht0的数据值增不减)
-
总结:Dict的结构:
类似java的HashTable,底层是数组+链表来解决哈希冲突
Dict包含2个哈希表,ht[0]平常用,ht[1]用来rehash -
总结:Dict的伸缩:
当LoadFactor>5或者LoadFactor>1且没有子进程任务时,Dict扩容
当LoadFactor小于0.1时,Dict收缩
扩容后大小为第一个大于等于used + 1的2^n
收缩大小为第一个大于等于used的2^n
Dict采用渐进式rehash,每次访问Dict时执行一次rehash
rehash时ht[0]只减不增,新增操作只在ht[1]执行,其它操作在两个哈希表 -
编码方式2数据结构ZipList
-
总结:
压缩列表可以看做一种连续内存空间的“双向链表”
列表的节点之间不是通过指针连接,而是记录上一节点和本节点长度来寻址,内存占用较低。
如果列表数据过多,导致链表过长,可能影响查询性能。
增或删较大数据时有可能发生连续更新问题。
- 重写和重载?
- 重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。
- 而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
- 重写是一种“多态性”:同名的方法,用不同的对象来区分调用的是哪一个方法。
- 重载:允许存在多个同名方法,而这些方法的参数不同,编译器根据方法不同的参数表,对同名方法的名称做修饰,对于编译器而言,这些同名方法就成了不同的方法。
- 它们的调用地址在编译期就绑定了。
- 对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
JVM
- jvm内存分布,有垃圾回收的是哪些地方?
堆、栈(Java虚拟机栈,本地方法栈)、方法区,程序计数器。
- 堆:主要存储就是 类实例后的对象,堆区也是垃圾回收的主要区域。GC 也可以认为是以对象为单位进行释放的。(1.8后StringTable在这里面)
- Java虚拟机栈:是Java虚拟机为执行Java方法而提供的一块内存区域,用于支持方法调用和执行。它由栈帧组成,保存了方法的参数、局部变量、返回值等信息。随着方法的结束而结束
- 本地方法栈:是Java虚拟机(JVM)中的一块内存区域,用于存储类的结构信息、常量、静态变量、编译器编译后的代码等数据。它是所有线程共享的,与堆区不同,每个线程都有自己独立的方法栈和程序计数器。主要维护的是JDK , 内部集成的一些由C/C++ 语言所编写的本地方法或类对象,本地方法栈的大小是可以配置的,可以通过JVM参数来指定。如果本地方法栈空间不足,会抛出栈溢出异常(StackOverflowError)。
- 方法区:是Java虚拟机(JVM)中的一块内存区域,用于存储类的结构信息、类加载器、常量、静态变量、编译器编译后的代码等数据。它是所有线程共享的。(1.8后方法区在本地内存空间中)
- 程序计数器:就是一个单纯的存地址的整数,用于存储当前正在执行的指令地址或下一条将要执行的指令地址。随着线程的销毁而销毁。
- 主要回收堆
- 垃圾回收:可达性分析算法(起点:栈上的局部变量(引用),常量池中引用的对象,方法区中静态成员引用的对象)
- 垃圾处理机制:标记清除,复制算法,标记整理。(JVM的清除思想结合了上述的3种思想-分代回收思想)
- 1.新创建的对象,放在伊甸区。当垃圾回收(GC)扫描(可行性分析)到伊甸区之后,大部分对象都会在第一轮GC中被淘汰
在年轻代中,主要采用的回收策略是复制算法(Copying Algorithm)和标记-清除算法 (Mark-Sweep Algorithm) - 2.伊甸区中的对象,如果没有在第一轮GC 中淘汰,就会通过复制算法,拷贝到生存区,生存区分为大小相同的两部分,一次只使用其中的一半,当生存区的对象在一轮GC 后被标记,其他幸存的对象就会通过复制算法拷贝到另一块未使用的幸存区,然后清除全部标记对象。如此交替。
- 3.当一个对象在生存区,经过若干论都没有被淘汰,年龄增长到一定的程度,就会通过复制算法拷贝到老年区,进入老年区的对象,被淘汰的概率就很低了,此时针对老年区 GC 的频率也随之降低,如果老年区中发现某个对象是垃圾,就使用标记整理删除。
- 4.特殊情况,当一个对象非常大的时候,是直接进入老年区的(大对象进行复制算法成本比较高)
-
JVM的内存分区说一下?为啥堆要分区?
为什么堆要分区:
这其实是和对象的存活概率挂钩的,存活时间长的,放在老年代,减少GC(垃圾回收)扫描的概率,新创建出来的对象实例,一般存放在伊甸园,而且不同的区域采用的垃圾回收的算法也是不一样的。 -
Java运行时内存区的组成,栈和堆的作用?
-
堆:JVM堆可以被认为是一个大的内存池,用于存储所有对象实例以及数组,这些数据都是在运行时动态创建的。
- 在JVM启动时,堆被划分为三个区域:新生代(Young Generation)、老年代(Old Generation)和永久代(PermGen)。
- 其中,新生代用于存储新创建的对象,老年代则用于存储已经存活一段时间的、长周期的对象,而永久代则用于存储类的定义信息、字符串常量等。
- 永久代则是用于存储类定义信息、方法信息、常量池、字符串池等数据。由于这些数据都是静态的,所以它们很少会被回收。
- JDK8h后,PermGen已经被MetaSpace取代,MetaSpace是使用本地内存而不是JVM堆来存储这些数据,因此不会受到JVM堆大小的限制。
-
栈:JVM栈是Java虚拟机中的一个非常重要的组成部分,它用于管理Java程序运行时的方法调用和参数传递。
垃圾回收器
- 新生代:
- serial:复制算法,单线程,暂停所有工作线程。
- PraNew:复制算法,多线程,暂停。
- Parallel Scavenge:复制算法,多线程,高吞吐量。
- 老年代:
- serial Old:标记整理,单线程
- Parallel Old:标记整理,多线程
- CMS:标记整理,多线程,最低停顿时间。
- 初始标记:stop the world
- 并发标记
- 重新标记:stop the world
- 并发清除
- 回收整个Java堆(新生代和老年代)
- G1标记整理算法(JDK1.7后的全新回收器,用于取代CMS收集器)
- 1.初始标记(它标记了从GC Root开始直接可达的对象)
- 2.并发标记(从GC Roots开始对堆中对象进⾏可达性分析,找出存活对象)
- 3.最终标记(标记那些在并发标记阶段发⽣变化的对象,将被回收)
- 4.筛选回收(⾸先对各个Regin的回收价值和成本进⾏排序,根据⽤户所期待的GC停顿时间指定回收计划,回收一部分Region)
三色标记
黑:已被处理,需要保留。
白:还未处理。
灰:正在处理。
JUC
- 多线程这块你用过哪些工具?
-
countDownLatch闭锁(重点)
-
栅栏CyclicBarrier
-
信号量Semaphore(重点)
- volatile关键字的作用,它能否保证原子性?
保证成员变量、静态成员变量的可见性(多线程并发时,由于JIT的优化,线程只查看变量在自己缓存中的值,而不去看主存中现在的实时值,这里涉及了JMM)。
与synchronized对比:
- synchronized语句块既可以保证代码块的原子性,也同时保证了代码块中变量的可见性
- 缺点是synchronized属于重量级操作,性能相对更低。
作用:
-
保证内存可见性
-
禁止指令重排
-
原子性:
不保证。原因:
让一个volatile的integer自增(i++),其实要分成3步:1)读取volatile变量值到local; 2)增加变量的值;3)把local的值写回,让其它的线程可见。
最后一步jvm让这个最新的变量的值在所有线程可见,也就是最后一步让所有的CPU内核都获得了最新的值,但中间的几步是不安全的,中间如果其他的CPU修改了值将会丢失。
- 2 synchronized和reentrantReadWriteLock的区别?AQS的原理?
ReentrantLock vs synchronized:
- 可中断(lock.lockInterruptibly() 获得锁有竞争时,进入阻塞队列,可以被其它线程用 t1.interrupt() 打断,不等了!)
- 可以设置超时时间(lock.tryLock() 此刻获得不到就不要了! lock.tryLock(1,TimeUnit:SECONDs) 1s内得不到就不要了!)
- 可以设置为公平锁(ReentrantLock lock = new ReentrantLock(true) 这样子是公平锁,在阻塞队列中先到先得!)
- 支持多个条件变量(多间waitSet,先创建Condition对象,获得锁,condition1.await()进入这一间waitSet等待,condition1.signal()随机唤醒这间休息室中的一个线程。也就是多间休息室)
相同点:都支持可重入。
synchronized和reentrantReadWriteLock的区别
-
大型网站中很重要的一块内容就是数据的读写,ReentrantLock虽然具有完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务),但是效率非常低。所以在JDK中提供了一种读写锁ReentrantReadWriteLock,使用它可以加快运行效率。
-
读写锁表示两个锁,一个是读操作相关的锁,称为共享锁;另一个是写操作相关的锁,称为排他锁。我把这两个操作理解为三句话:
-
读和读之间不互斥,因为读操作不会有线程安全问题
-
写和写之间互斥,避免一个写操作影响另外一个写操作,引发线程安全问题
-
读和写之间互斥,避免读操作的时候写操作修改了内容,引发线程安全问题
-
总结起来就是,多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。
-
区别:synchronized我们都不陌生,我们使用他可以让我们的代码变的线程安全;
而ReentrantReadWriteLock也可以达到相同的效果,但是ReentrantReadWriteLock相比synchronized有一些不同,synchronized同时只能有一个线程得到相同对象的锁,我得到了其他任何人都不能得到。而ReentrantReadWriteLock中有读锁和写锁,读读不互斥,ReentrantReadWriteLock可以允许多个线程同时得到读锁,这样在值变更比较少的情况是很实用的,大家(线程)都只是读,那么我们都可以得到读锁,这样不像synchronized那样同时只能有一个线程得到锁。
AQS
AQS是多线程同步器,它是JUC包中多个组件的底层实现,比如Lock、CountDownLatch、Semaphore都用到了AQS
从本质上来说,AQS 提供了两种锁机制,分别是排它锁,和共享锁
排它锁,就是存在多线程竞争同一共享资源时,同一时刻只允许一个线程访问该 共享资源,
也就是多个线程中只能有一个线程获得锁资源,比如 Lock 中的 ReentrantLock 重入锁实现就是用到了 AQS 中的排它锁功能。
共享锁也称为读锁,就是在同一时刻允许多个线程同时获得锁资源,比如 CountDownLatch 和 Semaphore 都是用到了 AQS 中的共享锁功能。
- 设计AQS整个体系需要解决的三个核心的问题:①互斥变量的设计以及多线程同时更新互斥变量时的安全性②未竞争到锁资源的线程的等待以及竞争到锁资源的线程释放锁之后的唤醒③锁竞争的公平性和非公平性。
- AQS采用了一个int类型的互斥变量state用来记录锁竞争的一个状态,0表示当前没有任何线程竞争锁资源,而大于等于1表示已经有线程正在持有锁资源。一个线程来获取锁资源的时候,首先判断state是否等于0,如果是(无锁状态),则把这个state更新成1,表示占用到锁
- 此时如果多个线程进行同样的操作,会造成线程安全问题。AQS采用了CAS机制来保证互斥变量state的原子性。未获取到锁资源的线程通过Unsafe类中的park方法对线程进行阻塞,把阻塞的线程按照先进先出的原则加入到一个双向链表的结构中,当获得锁资源的线程释放锁之后,会从双向链表的头部去唤醒下一个等待的线程再去竞争锁
- 另外关于公平性和非公平性问题,AQS的处理方式是,在竞争锁资源的时候,公平锁需要判断双向链表中是否有阻塞的线程,如果有,则需要去排队等待;而非公平锁的处理方式是,不管双向链表中是否存在等待锁的线程,都会直接尝试更改互斥变量state去竞争锁。
- juc下包了解哪些,用过哪些?synchronized和volatile还有lock接口?
包:ReentrantLock、CountDownLatch、Semaphore、Atomic 包
包名用法:java.util.concurrent.atomic
-
ReentrantLock 是一个可重入的互斥锁,与 synchronized 关键字类似,但是它提供了更强的功能。它具有公平锁和非公平锁两种模式,可以使用 tryLock() 方法尝试获取锁,还可以通过 Condition 接口实现更灵活的线程通信。
-
CountDownLatch 是一个倒数计数器,可以用于控制线程的执行顺序。它可以让一个或多个线程等待其他线程完成操作后再继续执行,也可以设置等待超时时间。使用 CountDownLatch 需要先指定计数器的初始值,每次线程完成一个操作后调用 countDown() 方法减少计数器的值,当计数器的值减到 0 时,等待的线程将被唤醒。
-
Semaphore 是一个信号量,可以用于控制线程的并发数量。它可以限制同时访问某个资源的线程数量,也可以限制同时执行某个任务的线程数量。使用 Semaphore 需要指定信号量的初始值,每次线程访问资源或执行任务前先调用 acquire() 方法获取许可证,执行完毕后再调用 release() 方法释放许可证。
-
Atomic 包提供了多种原子性的操作类,如 AtomicBoolean、AtomicInteger、AtomicLong 等,可以用于在多线程环境下安全地更新变量。这些类提供了 compareAndSet()、getAndSet() 等原子性操作,可以确保多线程访问时不会出现竞态条件。同时,它们也可以用于实现自旋锁、计数器等功能。
-
synchronized和volatile是java关键字
-
lock接口的实现类:reentrantlock、ReadwriteLock
Spring
- spring的aop是如何实现的?
-
面向切面编程,就是指对很多功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”
-
切点:指要对哪些Joinpoint进行拦截,即被拦截的连接点
-
通知:指拦截到Joinpoint之后要做的事情,即对切入点增强的内容。
-
切面:切点+通知
-
aop的底层实现是代理模式加反射.
-
spring aop采用动态代理实现切面
-
创建一个代理对象,该代理对象会调用该接口的真正实现,并且在代理对象中调用真实实现类的前后做方法增强。
- spring AOP的原理?
设计模式
- 线程安全单例模式?2设计模式了解吗,写一个单例模式?
- 饿汉式(不管用不用,先创建再说)(本身就线程安全)
- 懒汉式(用的时候再创建)
线程安全的懒汉式:
点击查看代码
public class Singleton{
private volatile static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance == null){
synchronized(){
if(uniqueInstance == null) uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
- 设计模式:建造者模式、单例模式、责任链模式、代理模式
- 讲一下代理模式?
- 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
作用:
- 中介隔离作用
- 开闭原则,增加功能
分类:
-
静态代理:静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
-
动态代理:动态代理是在程序运行时通过反射机制动态创建的。
-
在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。
Linux
- 用过哪些linux命令?
- ls: 列出当前目录下的文件和文件夹。
- cd: 切换目录。使用 "cd" 命令,后面跟上目标目录的路径,可以进入该目录。
- pwd: 显示当前工作目录的路径。
- mkdir: 创建一个新的目录。
- rm: 删除文件或目录。
- cp: 复制文件和目录
- mv: 移动文件和目录,也可用于重命名文件和目录。
- cat: 查看文件的内容。
- chmod: 修改文件或目录的权限。
- ps: 显示当前运行的进程列表。
- top: 动态显示系统资源使用情况和运行的进程。
- 根据进程名字搜进程id的命令?
- pidof命令可以用来查询某个进程的PID
- 服务器的负载情况用什么命令查看?
- uptime
- w:第一行和uptime一样,第二行以下显示当前登录的用户列表
- top:动态显示系统资源使用情况和运行的进程。
-
linux进程、线程区别?(同上)
-
逻辑地址和物理地址的区别,为什么要用逻辑地址和物理地址?
为什么用逻辑地址:
- (1)物理地址的程序只有装入程序所规定的内存空间上才能正确执行,如果程序所规定内存空间不空闲或不存在,程序都无法执行。
- (2)使用物理地址编程意味着由程序员分配内存空间,这在多道程序系统中,势必造成程序所占内存空间的相互冲突。
- (3)在多道程序系统中,程序员无法事先协商每个程序所应占的内存空间的位置,系统也无法保证程序执行时,它所需的内存空间都空闲。
- (4)基于上述原因,必须引入一个统一的、在编程时使用的地址,它能够在程序执行时根据所分配的内存空间将其转换为对应的物理地址,这个地址就是逻辑地址。
- (5)逻辑地址的引入为内存的共享、保护和扩充提供方便。
-
在log文件中查找所有IP
使用grep命令结合正则表达式
grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" nohup.out
-
怎么在linux查询某个文件中的某个关键字?
grep "关键字" 文件名
grep "hello world" example.txt
杂项
- 哪些协议会丢包?
TCP - 路由选择协议?
RIP(常用)、OSPF
-
RIP:它通过UDP报文进行路由信息的交换,使用的端口号为520。在网络层。
-
RIP:
- 优点:收敛速度快,刚刚开始路由器只知道于自己直连的路由信息,但是通过定时与相邻路由器交换路由表,可以快速在AS中达到收敛
- 缺点:好消息传播快,坏消息传播慢。因为坏消息有可能需要经过多次更新后直到距离达到16才会被AS中其它路由都发现。
-
路由表中记录了:目的网络;距离;下一跳
- 仅和相邻路由器交换信息。
- 交换的信息是当前本路由器所知道的全部信息,即自己现在路由表。
- 按固定的时间间隔交换信息,如每隔30s或网络拓扑发生变化时。
- nginx正向代理和反向代理?
- Nginx是一款高性能的Web服务器和反向代理服务器,它可以用于处理静态和动态内容,并且可以作为负载均衡器来分发请求。
- 正向代理服务器用来让局域网客户机接入外网以访问外网资源
- 在正向代理服务器中,我们的角色是客户端,目的是要访问外网的资源;
- 反向代理服务器用来让外网的客户端接入局域网中的站点以访问站点中的资源。
- 在反向代理服务器中,我们的角色是站点,目的是把站点的资源发布出去让其他客户端能够访问。
- 零拷贝?
零拷贝指的是,从一个存储区域到另一个存储区域的 copy 任务没有CPU 参与。
零拷贝需要 DMA 控制器的协助。DMA,Direct Memory Access,直接内存存取,是 CPU的组成部分,其可以在 CPU 内核(算术逻辑运算器ALU 等)不参与运算的情况下将数据从一个地址空间拷贝到另一个地址空间。
Linux 系统(CentOS6 及其以上版本)对于零拷贝是通过 sendfile 系统调用实现的。
- 举例:将一个硬盘中的文件通过网络发送出去
整个拷贝过程均没有 CPU 的参与,这就是零拷贝。
可以用:方法一:Mmap+Write实现零拷贝。方法二:sendFile()实现零拷贝
(零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,从而减少上下文切换以及数据拷贝的时间。零拷贝是一种IO操作优化技术,它并不是真的没有数据拷贝,而是广义上讲的减少和避免不必要的数据拷贝。)
- 零拷贝Zero-Copy是一种IO操作优化技术,可以快速高效地将数据从文件系统移动到网络接口,而不需要从其内核空间复制到用户空间
CPU的组成?
寄存器,控制器,运算器,时钟。
- 运算器:算数逻辑单元ALU
- 控制器:程序计数器PC
- 给你一个1t的文件,里面全部存储数字,如何去重重复的数字?
通过uniq
- 使用sort命令读取需要进行去重操作的文件:
- 使用uniq命令进行去重操作:
- 这种方法的原理是通过sort命令对数据进行排序,然后使用uniq命令去掉重复的行。需要注意的是,这种去重方法只能去重连续重复的行,而对于非连续重复的行,则无法进行去重。
- 如果此时要根据文章的一些内容来查询,要怎样做?es的倒排索引?es的倒排索引底层?
es
-
正排索引:是以文档对象的唯一 ID 作为索引,以文档内容作为记录的结构。
-
倒排索引:Inverted index,指的是将文档内容中的单词作为索引,将包含该词的文档 ID 作为记录的结构。
-
记录索引表:单词-频率-出现的位置
- 项目中的开源组件底层是怎么实现的?
(跳过)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!