2.讲一下Java的IO流和异常处理
- 按照流的流向分,可以分为输入流和输出流;
- 按照操作单元划分,可以划分为字节流和字符流;
- 按照流的角色划分为节点流和处理流。
\
3.进程的调度算法
3.1. 批处理系统
无太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。
(1) 先来先服务 first-come first-serverd(FCFS)
非抢占式的调度算法,按照请求的顺序进行调度。
有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。
(2)短作业优先 shortest job first(SJF)
非抢占式的调度算法,按估计运行时间最短的顺序进行调度。
长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
(3)最短剩余时间优先 shortest remaining time next(SRTN)
最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
3.2. 交互式系统
交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。
(1)时间片轮转调度算法(RR,Round-Robin)
当某个进程执行的时间片用完时,调度程序便停止该进程的执行,并将它送就绪队列的末尾,等待分配下一时间片再执行。然后把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程,在一给定的时间内,均能获得一时间片处理机执行时间
将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
时间片轮转算法的效率和时间片的大小有很大关系:
因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。
而如果时间片过长,那么实时性就不能得到保证。
(2) 优先级调度
为每个进程分配一个优先级,按优先级进行调度。
为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
(3)多级反馈队列
一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。
多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。
每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。
可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。
\
3.3 高响应比优先调度算法(HRRN,Highest Response Ratio Next)
按照高响应比((已等待时间+要求运行时间)/ 要求运行时间)优先的原则【等待时间长和运行时间短都会增加其优先值】,每次先计算就绪队列中每个进程的响应比,然后选择其值最大的进程投入运行。
4.讲一下https如何实现可靠性
- 对称加密+非对称加密+摘要算法+数字签名
对称加密: 对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。而在大多数的对称算法中,加密密钥和解密密钥是相同的,所以也称这种加密算法为秘密密钥算法或单密钥算法。常见的对称加密有:DES(Data Encryption Standard)、AES(Advanced Encryption Standard)、RC4、IDEA
非对称加密: 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey);并且加密密钥和解密密钥是成对出现的。非对称加密算法在加密和解密过程使用了不同的密钥,非对称加密也称为公钥加密,在密钥对中,其中一个密钥是对外公开的,所有人都可以获取到,称为公钥,其中一个密钥是不公开的称为私钥。非对称加密算法对加密内容的长度有限制,不能超过公钥长度。比如现在常用的公钥长度是 2048 位,意味着待加密内容不能超过 256 个字节。
摘要算法: 数字摘要是采用单项Hash函数将需要加密的明文“摘要”成一串固定长度(128位)的密文,这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。“数字摘要“是https能确保数据完整性和防篡改的根本原因。
数字签名: 数字签名技术就是对“非对称密钥加解密”和“数字摘要“两项技术的应用,它将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。
数字签名的过程如下:
明文 --> hash运算 --> 摘要 --> 私钥加密 --> 数字签名
数字签名有两种功效:
一、能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。
二、数字签名能确定消息的完整性。
5.https如何防止被恶意攻击
Https双向验证
服务器端对请求它的客户端要进行身份验证,客户端对自己所请求的服务器也会做身份验证。服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信。客户端如果发现服务端为不可信任的,那么也中止通信
6.讲一下redits的持久化机制
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失
Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:
(1)RDB:是Redis DataBase缩写快照
RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
(2)AOF:持久化
AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
Redis 4.0 对于持久化机制的优化
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化 (默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
7.缓存穿透、击穿、雪崩,选一个讲一下
缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。
解决方案
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案\
- 设置热点数据永远不过期。
- 加互斥锁,互斥锁
8.讲一下分布式都学了什么
CAP+RPC+BASE
9.原创算法题:判断sql语句是是否有嵌套的select
二面 飞书视频
1.自我介绍+项目介绍
2.Java垃圾回收机制,了解:堆内内存和堆外内存吗?
堆内内存(on-heap memory)\
- 堆内内存是java程序员在日常工作中解除比较多的, 可以在jvm参数中使用-Xms, -Xmx 等参数来设置堆的大小和最大值
- 堆内内存 = 年轻代 + 老年代 + 持久代
堆外内存(off-heap memory)
- 定义
- 堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存
- java.nio.DirectByteBuffer
- Java 开发者经常用 java.nio.DirectByteBuffer 对象进行堆外内存的管理和使用, 该类会在创建对象时就分配堆外内存。
\
- JDK1.8 取消了方法区, 由MetaSpace(元空间)代替。-XX:MaxPermSize由 -XX:MetaspaceSize, -XX:MaxMetaspaceSize 等代替
- 对堆外内存的申请主要是通过成员变量unsafe来操作
- 使用堆外内存的优点
- 减少了垃圾回收机制(GC 会暂停其他的工作)
- 加快了复制的速度
- 堆内在flush到远程时, 会先复制到直接内存(非堆内存), 然后再发送。
- 而堆外内存(本身就是物理机内存)几乎省略了该步骤
- 使用堆外内存的缺点
- 内存难以控制
- 使用了堆外内存就间接失去了JVM管理内存的可行性,改由自己来管理,当发生内存溢出时排查起来非常困难
3.Http1.1和Http2.0的区别
**
**4.Http的keep alive
HTTP Keep-Alive
在http早期,每个http请求都要求打开一个tpc socket连接,并且使用一次之后就断开这个tcp连接。
使用keep-alive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,可以减少tcp连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。
但是,keep-alive并不是免费的午餐,长时间的tcp连接容易导致系统资源无效占用。配置不当的keep-alive,有时比重复利用连接带来的损失还更大。所以,正确地设置keep-alive timeout时间非常重要。
keepalvie timeout
Httpd守护进程,一般都提供了keep-alive timeout时间设置参数。比如nginx的keepalive_timeout,和Apache的KeepAliveTimeout。这个keepalive_timout时间值意味着:一个http产生的tcp连接在传送完最后一个响应后,还需要hold住keepalive_timeout秒后,才开始关闭这个连接。
当httpd守护进程发送完一个响应后,理应马上主动关闭相应的tcp连接,设置 keepalive_timeout后,httpd守护进程会想说:”再等等吧,看看浏览器还有没有请求过来”,这一等,便是keepalive_timeout时间。如果守护进程在这个等待的时间里,一直没有收到浏览发过来http请求,则关闭这个http连接。
默认一般2个小时;
TCP KEEPALIVE
链接建立之后,如果应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,当链接很久没有数据报文传输时如何去确定对方还在线,到底是掉线了还是确实没有数据传输,链接还需不需要保持,这种情况在TCP协议设计中是需要考虑到的。
TCP协议通过一种巧妙的方式去解决这个问题,当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。
5.1数据库的ACID,如何实现的
MySQL 作为一个关系型数据库,以最常见的 InnoDB 引擎来说,是如何保证 ACID 的。
- (Atomicity)原子性: 事务是最小的执行单位,不允许分割。原子性确保动作要么全部完成,要么完全不起作用;
- (Consistency)一致性: 执行事务前后,数据保持一致;
- (Isolation)隔离性: 并发访问数据库时,一个事务不被其他事务所干扰。
- (Durability)持久性: 一个事务被提交之后。对数据库中数据的改变是持久的,即使数据库发生故障。
原子性
接着说说原子性。前文有提到 undo log ,回滚日志。隔离性的MVCC其实就是依靠它来实现的,原子性也是。 实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。 当事务对数据库进行修改时,InnoDB会生成对应的 undo log;如果事务执行失败或调用了 rollback,导致事务需要回滚,便可以利用 undo log 中的信息将数据回滚到修改之前的样子。 undo log 属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB 会根据 undo log 的内容做与之前相反的工作:
对于每个 insert,回滚时会执行 delete;对于每个 delete,回滚时会执行insert;对于每个 update,回滚时会执行一个相反的 update,把数据改回去。
以update操作为例:当事务执行update时,其生成的undo log中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态。
持久性
Innnodb有很多 log,持久性靠的是 redo log。
隔离性
binlog ,说到这,可能会疑问还有个 bin log 也是写操作并用于数据的恢复,有啥区别呢。 层次:redo log 是 innoDB 引擎特有的,server 层的叫 binlog(归档日志)内容:redolog 是物理日志,记录“在某个数据页上做了什么修改”;binlog 是逻辑日志,是语句的原始逻辑,如“给 ID=2 这一行的 c 字段加 1 ”写入:redolog 循环写且写入时机较多,binlog 追加且在事务提交时写入
一致性
一致性是事务追求的最终目标,前问所诉的原子性、持久性和隔离性,其实都是为了保证数据库状态的一致性。当然,上文都是数据库层面的保障,一致性的实现也需要应用层面进行保障。也就是你的业务,比如购买操作只扣除用户的余额,不减库存,肯定无法保证状态的一致。
5.2redo log 和 binlog 和undo log
redo log (重做日志)是 InnoDB 存储引擎独有的,它让 MySQL 拥有了崩溃恢复能力。
redo log 它是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎。
比如 MySQL 实例挂了或宕机了,重启时, InnoDB 存储引擎会使用 redo log 恢复数据,保证数据的持久性与完整性。
binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于 MySQL Server 层。不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。
那 binlog 到底是用来干嘛的?
可以说 MySQL 数据库的 数据备份、主备、主主、主从 都离不开 binlog ,需要依靠 binlog 来同步数据,保证数据一致性。
我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行 回滚 ,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。
6.数据库主从复制、读写分离,从库如何顶上主库
主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将这些日志重新执行(重做);从而使得从数据库的数据与主数据库保持一致。
主从复制的作用
- 主数据库出现问题,可以切换到从数据库。
- 可以进行数据库层面的读写分离。
- 可以在从数据库上进行日常备份。
MySQL主从复制解决的问题
- 数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
- 负载均衡:降低单个服务器的压力
- 高可用和故障切换:帮助应用程序避免单点失败
- 升级测试:可以用更高版本的MySQL作为从库
MySQL主从复制工作原理
- 在主库上把数据更高记录到二进制日志
- 从库将主库的日志复制到自己的中继日志
- 从库读取中继日志的事件,将其重放到从库数据中
基本原理流程,3个线程以及之间的关联
主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;
从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进自己的relay log中;
从:sql执行线程——执行relay log中的语句;
7.redits哨兵机制,数据存储不下如何处理
哈希算法+一致性哈希
8.AQS原理
AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。
AQS 定义两种资源共享方式
- Exclusive(独占):只有一个线程能执行,如 ReentrantLock。又可分为公平锁和非公平锁:
- 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
- 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
- Share(共享):多个线程可同时执行,如 CountDownLatch、Semaphore、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。
ReentrantReadWriteLock 可以看成是组合式,因为 ReentrantReadWriteLock 也就是读写锁允许多个线程同时对某一资源进行读。
不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS 已经在顶层实现好了。
- 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
- 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
9.TCP头部的RST标志位
RST表示复位,用来异常的关闭连接,在TCP的设计中它是不可或缺的。就像上面说的一样,发送RST包关闭连接时,不必等缓冲区的包都发出去(不像上面的FIN包),直接就丢弃缓存区的包发送RST包。而接收端收到RST包后,也不必发送ACK包来确认。
TCP处理程序会在自己认为的异常时刻发送RST包。例如,A向B发起连接,但B之上并未监听相应的端口,这时B操作系统上的TCP处理程序会发RST包。
又比如,AB正常建立连接了,正在通讯时,A向B发送了FIN包要求关连接,B发送ACK后,网断了,A通过若干原因放弃了这个连接(例如进程重启)。网通了后,B又开始发数据包,A收到后表示压力很大,不知道这野连接哪来的,就发了个RST包强制把连接关了,B收到后会出现connect reset by peer错误。
三面 飞书视频
1.Https和Http的区别
**
**2.数据库的ACID特性和具体的实现方式
1.原⼦性( Atomicity ): 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么完全不起作⽤;
2.⼀致性( Consistency ): 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的;
3. 隔离性( Isolation ): 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的;
4. 持久性( Durability ): ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。\
原子性:undolog
持久性:redolog
隔离性:binlog +MVCC+锁
一致性:undolog+redolog+binlog
3.设计一个线程池,要求线程池中的线程不能被饿死
是不是可以将进程调度算法应用到这里面:高响应比优先,多级反馈队列
4.线程上下文切换是如何实现的?如何自己设计实现呢?
场景:进程A下CPU,进程B上CPU
保存进程A的上下文环境(程序计数器,程序状态字,其他寄存器…)
用新状态和其他相关信息更新进程A的PCB
把进程A移至合适的队列(就绪,阻塞…)
将进程B的状态设置为运行态
从进程B的PCB中恢复上下文(程序计数器,程序状态字,其他寄存器…)
- schedule 调度函数
- Context_switch()进程上下文切换
如何自己设计实现呢?
线程的通信方式吧:信号量、信号、synchronized+wait+notify、threadlocal
5.操作系统分页、分段、TLB
程序是由若⼲个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。 不同的段是有不同的属性的,所以就⽤分段( Segmentation )的形式把这些段分离出来。 分段的好处就是能产⽣连续的内存空间,但是会出现内存碎⽚和内存交换的空间太⼤的问题。
要解决这些问题,那么就要想出能少出现⼀些内存碎⽚的办法。另外,当需要进⾏内存交换的时候,让需要交换写⼊或者从磁盘装载的数据更少⼀点,这样就可以解决问题了。这个办法,也就是 内存分⻚ ( Paging )。 分⻚是把整个虚拟和物理内存空间切成⼀段段固定尺⼨的⼤⼩ 。这样⼀个连续并且尺⼨固定的内存空间,我们叫 ⻚ ( Page )。在 Linux 下,每⼀⻚的⼤⼩为 4KB
多级⻚表 ( Multi-Level Page Table ),解决了空间上的问题,但是虚拟地址到物理地址的转换就多了⼏道转换的⼯序,这显然就降低了这俩地址转换的速度,也就是带来了时间上的开销。
利⽤局部性原理,把最常访问的⼏个⻚表项存储到访问速度更快的硬件,于是计算机科学家们,
就在 CPU 芯⽚中,加⼊了⼀个专⻔存放程序最常访问的⻚表项的 Cache,这个 Cache 就是 TLB
( Translation Lookaside Buffer ) ,通常称为⻚表缓存、转址旁路缓存、快表等。
在 CPU 芯⽚⾥⾯,封装了内存管理单元( Memory Management Unit )芯⽚,它⽤来完成地址转换和 TLB的访问与交互。 有了 TLB 后,那么 CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的⻚表。TLB 的命中率其实是很⾼的,因为程序最常访问的⻚就那么⼏个;
6.给缓存设计索引,索引应该是什么结构
B+树;
7.分布式了解哪些?
- BASE;
- CAP;
- RPC;
作者:Li-Xiao-Hu
链接:www.nowcoder.com/discuss/817…\