golang面试

文章目录

        • Golang
        • Goroutine
        • WaitGroup
        • channel+select
        • context
        • MYSQL
        • **分布式**
        • 缓存Redis
        • K8S

github: https://github.com/lifei6671/interview-go

Golang

1、slice扩容:
old.cap*2<1024,翻倍,否则扩大1/4,
若还是小于需要的容量,则直接扩容到需要的
2、channel底层
堆上的hchan结构体,buf(循环数组)
sendx(下一个要发送的数据下标)recvx(接收数据的下标)
sendq(goroutine待发送队列),recvq(待接收队列)
lock mutex互斥锁
无缓冲channel,直接给hchan分配内存
有缓存的channel,元素不含指针,会为hchan和底层数组分配一段连续的地址
含指针,为hchan和底层数组分别分配地址
3、slice原理:
结构体,24个字节,array指向底层数组的指针,
len,cap
4、make和new:
他们是函数,不是关键字
make就是专门用来创建map,channel,slice数据结构的函数,返回他们本身。
new分配任意类型,置零,返回内存地址的指针。
var声明值类型,系统默认分配内存空间,置零。
var声明引用类型,系统默认nil,必须进行内存分配后才能使用。
5、defer:
将defer函数插到函数尾部,不需要链表和栈上参数拷贝。
先进后出,panic之后的defer无法执行。
6、数组和切片的区别:
数组是固定长度,切片长度不固定
函数传参,数组是深拷贝,不会修改原数组,切片是浅拷贝会修改对应的数组
数组获取长度时间复杂度是O(N),切片是O(1)
7、深拷贝和浅拷贝:
append()或者copy(sliceNew,sliceOld)
引用类型默认都是浅拷贝
8、线程安全:
slice没有加锁不是线程安全
可以用互斥锁,读写锁,原子操作,channel
9、rpc流程:
调用方把请求参数序列化,通过TCP传输给服务提供方
服务方从TCP通道接收二进制数据,根据RPC协议将二进制数据分割,反序列化还原请求对象,找到对应实现,完成方法调用。将结果序列化回写到对应TCP通道
调用方获取到数据包后,反序列化成应答对象。
序列化方式:Protobuf

Goroutine

同一个用户地址,并行独立执行Function
栈内存2KB,栈空间可以自动扩容
channel用于goroutine间的通信,通过管道,可以用同步的方式写异步代码。

线程
内核级的交互,消耗大,
栈内存1-8MB,初始化后不能更改
控制方式跟进程控制类似。上下文保存成本高
多个thread通信复杂,加锁容易出现Bug.

communicating sequential processes(CSP)

use pipe, asynchronous code in a synchronous style

WaitGroup

适合多个Goroutine做同一件事,等待事情结束

var wg sync.WaitGroup
wg.Add(2)
wg.Done()

channel+select

主动通知停止

stop := make(chan bool)

for{
    select {
        case <- stop:
        default:
        
    }
}

context

多个Goroutine,或者Goroutine里还有Goroutine.用channel的话需要很多channel,很麻烦。

ctx, cancel := context.WithCancel(context.Backgroud())
for{
    select{
        case <- ctx.Done():
        default:
        
    }
}

MYSQL

1、mysql分页: limit
2、连接多个查询结果: union
3、内连接和外连接: right (outer) join … on
4、B+树原理和优势:
叶子节点,所有数据值信息,指向下一个,链表
只在叶子节点存储数据,单个节点数据量更小,相同磁盘I/O,查询更多
双向链表连接,基于范围的顺序查找。
N个叶子节点,搜索复杂度O(logdN),d表示最大子节点个数(大于100),千万级别3~4次。
5、索引失效:
like前缀匹配"%…" 左右子节点都可能符合
索引列上做了计算,函数,类型转换操作
字符串不加引号导致类型转换
6、回表:检索辅助索引B+树,获取主键,再通过主键索引,获取整行数据
7、索引优化
前缀索引优化:针对前5个字符建立索引
覆盖索引优化:建立组合索引,不用查询所有信息
联合索引优化:把区分度大的字段排在前面
8、用索引
字段有唯一性限制,常用的,where查询,经常用于GROUP BY和ORDER BY
9、不用索引:
不常用的,重复的,数据少的,经常更新的
10、事务隔离级别
读未提交,读已提交,可重复读,串行化
脏读: 读未提交。(升级到读已提交)
不可重复读:同一事务内,读了数据,然后受其他事务影响(UPDATE),再读数据不同。(升级到可重复读)
幻读: 同一事务内,受其他事务影响(INSERT,Delete),在不同时间执行得到不同结果。
(不能升级到串行化,行锁解决不了,需要间隙锁)
11、事务的特性:
ACID,原子性,一致性,隔离性,持久性
12、数据库锁
悲观锁: SELECT … FOR UPDATE
乐观锁: CAS机制,对比数据时间戳或版本号
13、死锁:
线程A已有资源A,想要资源B,线程B已有资源B,想要资源A,相互等待。
互斥:
持有并等待:可以一次性申请所有资源
不可剥夺:占用部分资源的线程进一步申请其他资源
循环等待:按序申请资源预防
14、读写分离,主从复制过程
主库收到请求,写入binlog,提交事务,事务提交完成后就返回响应
从库创建一个线程,连接主库的log dump线程,接收主库的binlog日志,写入relay log,返回主库响应。
从库再创建一个线程,读relay log,回放binlog更新数据。
一个主库一般跟2~3个从库。
15、主从复制模型:
同步复制,异步复制,半同步复制(等待一部分从库就行)
16、千万级别表优化:
优化sql和索引
加缓存redis
读写分离
垂直拆分,将大系统拆分为小系统,分库,电商分为商品,订单,用户等;分表将商品信息拆分商品基本信息和商品描述。(需要管理冗余列,有表连接操作,单表数据量依然大)
水平切分,根据时间或id,单表数据量变小,表结构相同,程序改动少,(分片事务一致性难以解决,跨节点join性能差,数据分片在扩容时需要迁移)
17、基本概念:
外键:如果一个表A的主键还存在与另一个表B中,那么B中这个字端可以作为A表的外键。(有一种好记的方法是,存在与外面的主键就是外键)

分布式

17、CAP理论:
可用性,一致性,容错性,主要是因为网络问题,容错性必需,
实际可以用BASE,基础可用性,软状态,最终一致性,比如淘宝的库存有一个中间值,然后才异步去计算库存。
18、分布式存储:
数据分片,复制,副本,容错,一致性,协议算法
通过hash分片,根据余数到不同节点,还有范围分片,可以对热点数据多分节点,需要存储分片的元数据,根据元数据分片
19、分布式事务一致:
放弃强一致性,选择最终一致性,
通过消息队列,kafka,解耦,流量削峰
MQ自动应答导致消息丢失:手动应答,执行成功后才删除持久化的消息
20、分布式锁:
可用性
死锁:获得了锁,因为网络问题或者崩溃;集群同步不一致,新的进程可能拿到锁,之前的进程以为自己还有锁
mysql:防止幻读,用行锁select for update锁住这行数据,将查询和插入的SQL
在一个事务内提交;也有可能出现交叉死锁,采用乐观锁
redis:lock_key, unique_value(唯一标识)
NX:lock_key不存在时,才对lock_key进行设置操作
PX:过期时间
21、消息队列消息丢失问题:
消息生成阶段:MQ Broker的ack确认响应,表示发送成功,只要处理好返回值和异常
消息存储阶段:交给MQ消息中间件保证,Broker会做副本,保证一条消息至少同步2个节点再返回ack
消息消费阶段:从Broker上拉取消息,消费段不立即发送消费确认,等执行完业务逻辑后再发送。
22、MQ的消息检测:
在消息生产段给每个消息指定一个全局唯一ID,或附加连续递增版本号,消费段做对应的版本检验。
生成端发送消息前,通过拦截器将版本号注入消息,再通过拦截器检测版本号的连续性或消费状态
22、消息重复消费问题:
数据库中建一张消息日志表,消息ID,消息执行状态
23、消息积压问题:
性能问题,一般是消费端问题。
增加消费端的数量,降级非核心业务,优化消费端业务逻辑处理。
24、ElasticSearch原理:
查询分析:自然语言处理拼写纠错
分词技术:将查询语句进行分词
关键词检索:在倒排索引库中进行匹配,
搜索排序:对多个文档进行相关度计算,排序,返回用户检索结果。

缓存Redis

1、Redis单线程:
大部分操作在内存中完成,避免了多线程之间的竞争
I/O多路复用机制处理大量客户端Socket请求。
6.0后增加了多线程。
另外线程: 网络I/O线程,Redis的持久化,集群同步
2、数据持久化方式
文件追加:AOF日志,记录所有操作命令,以文本的形势追加到文件中
快照:RDB快照,将某一个时刻的内存数据,以二进制方式写入磁盘
混合持久化:4.0集成RDB和AOF
3、为什么先执行命令,再写日志?
不对命令语法检查,只记录成功命令,不会阻塞当前写操作。
(数据可能丢失,可能阻塞其他操作)
4、RDB快照如何实现?
解决AOF故障恢复慢,记录Redis某一时刻的数据,不是操作,把RDB文件读入内存,就可以快速恢复。
5、缓存常见问题
缓存穿透:预设值
缓存并发:利用Redis的setNX方法
缓存雪崩:分散缓存时间,缓存不过期

网络
1、TCP三次握手:
客户端发起SYN,
服务端接收后发送SYN,ACK,
客户端最后发送一个ACK,根据ACK可以重发丢失数据
(两次握手,如果有一个SYN网络中阻塞,后面客户端又发一个SYN包,服务器接收到后建立2个连接,而客户只有一个连接)
ACK=期望数据序列号
2、四次挥手:
客户端发FIN包,服务器发ACK包,服务器还可以发送未发送数据,客户端可以接收数据
服务器发FIN包,客户端发ACK包,进入超时等待状态,服务器立即关闭连接。(客服端没收到ACK包,服务器会重发FIN包)。
3、HTTPS原理
非对称加密
客户端发送TLS版本号,加密套件,第1随机数
服务器确认TLS版本,加密套件,生成第2随机数,发送证书,公钥
客户端生成第3随机数(预主秘钥),用公钥加密后发送出去
服务器用私钥解密,获得预主秘钥
服务端,客户端用预主秘钥,第1,2随机数生成会话秘钥
4、RSA原理:
2个质数: p,q
质数相乘:N
欧拉函数:T
选公钥E:质数,1 私钥D: (DxE)%T=1,私钥=(D,N)

K8S

1、docker原理:
容器化技术,用Cgroups和namespaces进行进程隔离
容器中所有内容保存在镜像,包含代码和所需文件
2、VM虚拟机:
虚拟机监控层将请求转发物理系统,将更改放到缓存
3、docker架构:
c/s架构,server端是执行docker请求的守护进程或物理机,虚拟机。
4、容器文件系统:
分层构建,联合挂载
5、namespace原理
每个namespace下的资源对其他namespace不可见,PID可以相同。
有六类namespace
User,PID,Network,Mount,
UTS主机名和域名
IPC信号量,消息队列,共享内存

你可能感兴趣的:(golang,面试)