计算机网络
TCP分片和UDP分片
TCP分片是在传输层,UDP是在IP层
MTU:一个网络包的最大长度,一般为1500字节
MSS:除去IP头部和TCP头部后,网络包能容纳的TCP包的最大长度
为什么TCP分片不在IP层分片呢?
如果将TCP交由IP层分片,当有一个TCP数据包(TCP头部+数据)超过了MTU大小,那么IP层会将其分成若干片,但是这样有个隐患:如果当一个IP包丢失,那么整个IP报文的所有分片都得重传
为什么呢? 因为IP层不具备超时重传的功能,超时和重传都是TCP层具有的功能
如果一个IP包丢失,接收方组装后发现丢失了一部分,那么不会返回发送方ACK回应,那么就会触发TCP层的超时重传,重传整个TCP包,效率很低
所以,三次握手时都会协商MSS的大小,由TCP层进行分片,形成的IP层自然不会大于MTU,不需要IP层进行分片,如果丢失时,只需要重传MSS大小的分片就可以,提高了重传的效率。
第二次握手丢失了,会发生什么?
如果第二次服务端对客户端的SYN,ACK确认报文丢失了,那么服务端迟迟收不到客户端的确认报文,就会触发超时计时器,重传SYN=1,ACK=1的确认报文。
而客户端发送了第一次握手后,迟迟收不到确认报文,就会以为自己的SYN请求丢失了,也会重传SYN=1的请求报文
客户端SYN包超时重传的最大次数,是由tcp_syn_retries决定的,默认值是5次;
服务端SYN、ACK包超时重传的最大次数,是由tcp_synack_retries决定的,默认值是5次
操作系统
解释一下进程的上下文切换
各个进程之间是共享CPU资源的,在不同的时候进行切换,让不同的进程可以在CPU执行,那么一个进程切换到另一个进程运行,叫做进程的上下文切换
在解释进程的上下文切换之前,我先说一下CPU的上下文切换
CPU实际上每个时刻只能执行一个任务,但是因为切换的速度比较快,造成了多个任务同时运行的错觉。
但是在运行任务前,CPU必须知道任务从哪里加载,又是从哪里运行。因此操作系统会设置好CPU寄存器和程序计数器
寄存器存放任务,程序计数器存放下一条指令执行的位置
因此寄存器和程序计数器是CPU执行任务时必须的环境,叫做CPU的上下文
而CPU的上下文切换就是将上一个任务的CPU上下文保存起来,将下一个任务的上下文加载进来,可以根据任务的不同,把CPU上下文切换分为:进程上下文切换、线程上下文切换、中断上下文切换
进程是由内核管理调度的,所以进程的上下文切换发生在内核态。进程的上下文切换不仅包含了用户空间的资源,还包含了内核空间的资源
通常会把交换的信息存储在进程的PCB中,运行另一个进程时,会从PCB中取出上下文恢复到CPU中,使得这个进程可以继续执行。
1111
知乎对于上下文切换的文章 https://zhuanlan.zhihu.com/p/52845869
数据库
讲一下binlog、redo log、undo log
binlog(归档日志)是MySQL数据库级别的日志,记录对数据库进行修改的更新操作,记录的是SQL语句的原始逻辑,都会被记录下来,用于数据库的同步和恢复(比如主从数据库之间使用bin log进行同步)
redo log(重做日志)是InnoDB引擎级别的日志,用来记录InnoDB引擎的事务日志,保证了事务的持久性,无论事务是否提交都会记录redo log,用于数据的恢复。
undo log(回滚日志)当数据修改时,undo log会记录修改前的数据,可以实现事务的回滚,回滚到某个版本的数据,实现MVCC
redo log的两阶段提交了解过吗?
redo log在事务执行过程中可以不断写入,每执行一个更新语句都会写入redo log,而binlog只在事务提交阶段写入。因此两个日志的写入时机不一样,为了保证两个日志逻辑的一致,防止因为某些原因导致的日志没有写入,然后发生数据不一致的情况。
InooDB采用两阶段提交的方式,将redo log的写入拆为两个步骤:prepare和commit,这就是两阶段提交。
事务执行过程中写入的redo log是prepare阶段,事务提交时等binlog写入完成后,redo log才会进入commit阶段提交。
原本的机制有什么问题?
如果redo log写完之后,binlog写入异常,那么主从MySQL库之间是通过binlog同步的,同步的数据会不一致
为什么这样能解决数据不一致的情况?
如果在这个过程中,binlog写入发生异常,那么redo log的状态会一直在prepare状态,那么事务回滚。
如果redo log的commit阶段发生异常,那么如果binlog已经写入完成了,那么会提交事务,根据binlog恢复数据即可。
Java基础
线程池的作用?线程池参数?线程池拒绝策略和等待队列类型之间的关系?
核心线程数corePoolsize、最大线程数maximumPoolsize、最大存活时间keepAliveTime、存活时间单位unit、工作队列workQueue、线程工厂threadFactory、拒绝策略handler
七个核心参数
线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、
keepAliveTime、unit、workQueue、threadFactory、handler。下面会对这7个参数一一解释。
1. corePoolSize 线程池核心线程大小
线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会 被
销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。
2. maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池后,首先会缓存到工作队列(后面会介绍)中,如果工作队
列满了,则会创建一个新线程,然后从工作队列中的取出一个任务交由新线程来处
理,而将刚提交的任务放入工作队列。线程池不会无限制的去创建新线程,它会有一
个最大线程数量的限制,这个数量即由maximunPoolSize来指定。
3. keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时
间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
4. unit 空间线程存活时间单位
keepAliveTime的计量单位
5. workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。
jdk中提供了四种工作队列:
a. ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队
尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到 corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
b. LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排 序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
c. SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
d. PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
6. threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
7. handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限
制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这
个问题的,jdk中提供了4中拒绝策略:
a. CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
b. AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
c. DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
d. DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列