写在前面

知乎面试下来感觉还行吧,毕竟知乎挺小的,不过也是挺注重基础的,面试官水平也很好。

笔试

一面

进程和线程的区别?

进程是操作系统资源分配的基本单位、而线程是任务调度和执行的基本单位。

每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;

轻量级的进程线程共享代码和数据空间独立的运行栈程序计数器

虚拟地址是什么?

固定大小的虚拟页(VP)

对于进程来说,使用的都是虚拟地址。

每个进程维护一个单独的页表。

页表是一种数组结构,存放着各虚拟页的状态,是否映射,是否缓存。

内存分段分页讲讲?

分段:将程序分为代码段、数据段、堆栈段等;
分段地址通过段表,转换成线性地址; 分段地址包括段号和段内陆址; 段表,包括短号、段长、基址;

分页:将段分成均匀的小块,通过页表映射物理内存;
分页机制就是将虚拟地址空间分为大小相等的页;物理地址空间也分为若干个物理块(页框);页和页框大小相等。实现离散分配; 分页机制的实现需要 MMU 硬件实现;负责分页地址转换; 页大小(粒度)太大浪费;太小,影响分配效率。

http 1.0,1.1,2.0 区别?

HTTP1.0:中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准。

HTTP1.1:

  1. 在请求头引入了range头域,它允许只请求资源的某个部分,方便了开发者自由的选择以便于充分利用带宽和连接。
  2. 新增了多个错误状态响应码,如:413
  3. 长连接

HTTP2.0:

发出的多个请求可以在同一个连接上并行处理,当请求数量较大时,不会因为某一个请求任务过重而导致其他任务无法正常执行

post和get的区别

GET用于获取信息,是幂等的,且可缓存。

POST用于修改服务器上的数据,非幂等,不可缓存。

TCP 连接是怎么样的?

为什么是三次?断开为什么是四次?

三次握手

接收和发送信息是正常让 接收方 知道 发送方 有发送信息的能力让 发送方 知道 接收方 有发送和接受信息的能力让 接收方 知道 发送方 有接受信息的能力

四次挥手

四次挥手则是为了保证等数据完成的被接收完再关闭连接。

都达到关闭连接的条件才能断开。
发起关闭连接
确认报文自己知道客户端想要关闭连接了
 FIN 报文已经发送完了准备关闭连接了
 ACK 报文

2MSL有什么用?

2MSL 即两倍的 MSL,TCP 的 TIME_WAIT 状态也称为 2MSL 等待状态,当 TCP 的一端发起主动关闭,在发出最后一个 ACK 包后,即第3次握手完成后发送了第四次握手的 ACK 包后就进入了 TIME_WAIT 状态,必须在此状态上停留两倍的 MSL 时间,等待 2MSL 时间主要目的是怕最后一个 ACK 包对方没收到, 那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的 FIN包 后可以再发一个 ACK应答包。

chan用过吧?对一个关闭的 chan 读写会怎么样?

零值如果有元素,就继续读剩下的元素,如果没有就是这个chan类型的零值,比如整型是 int,字符串是"" src/runtime/chan.go

redis 缓存击穿 ?雪崩?有了解过吗?

其实对于这些缓冲击穿,缓冲穿透,缓存雪崩都是由于中译之后比较容易混淆,如果我们还原回英文,那么就会很容易理解了。

cache penetration缓存 key 的取值范围hotspot invalidcache penetrationcache avalanche

跳表说一下?

redis.h/zskiplistNoderedis.h/zskiplistzskiplistNodezskiplist

在这里插入图片描述

上图中展示了一个跳跃表示例,最左边的就是 zskiplist 结构。

最大的那个节点的层数前进指针和跨度节点中用BW字样标记的后退指针,他指向当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用。

算法:最长公共子串

二面

一上来就做题,难受,做完题就聊项目,聊完项目就聊聊八股。

用两个协程打印交替打印A1B2C3D4E5…

两个 chan +一个 wg
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	chNumber := make(chan struct{})
	chLetter := make(chan struct{})
	i:=0
	index := 0
	a := "abcdefghijklmnopqrskuvwxyz"
	go func() {
		for {
			select {
			case <-chNumber:
				fmt.Print(i)
				i++
				fmt.Print(i)
				i++
				chLetter <- struct{}{}
				break
			default:
				break
			}
		}
	}()

	go func(wg *sync.WaitGroup) {
		for {
			select {
			case <-chLetter:
				if index > len(a)-1 {
					wg.Done()
					return
				}
				fmt.Print(string(a[index]))
				index++
				fmt.Print(string(a[index]))
				index++
				chNumber<- struct{}{}
				break
			default:
				break
			}
		}
	}(&wg)
	chNumber<- struct{}{}
	wg.Wait()
}

慢查询如何排查?

可以先检查索引,再看看我们的 sql语句 是否足够性能好。
然后可以用 pprof 进行检测,排查慢的逻辑并优化。

es 用过是吧?简单说一下原理?

ES是一个搜索引擎,我所知道的是用到了倒排索引来进行检测的。

mysql 索引结构?和其他数据结构对比?

B 树
在这里插入图片描述
B+树

在这里插入图片描述

log (n)O(1)相近的数据预读进内存空间局部性外部存储

讲讲 聚集索引 和 非聚集索引?

聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据。

将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,
参考链接

[1]. https://blog.csdn.net/weixin_39790760/article/details/110815922