June's Studio.

Golang常见面试题

字数统计: 2.2k阅读时长: 8 min
2022/12/23

Golang开发面试题汇总

请分析以下代码两个方法的执行效率,哪个更快,为什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

const matrixLength = 200000

func foo() {
// 二维数组
matrixA := CreateMatrix(matrixLength)
matrixB := CreateMatrix(matrixLength)

for i := 0; i < matrixLength; i++ {
for j := 0; j < matrixLength; j++ {
matrixA[i][j] = matrixA[i][j] + matrixB[i][j]
}
}
}

func bar() {
// 二维数组
matrixA := CreateMatrix(matrixLength)
matrixB := CreateMatrix(matrixLength)

for i := 0; i < matrixLength; i++ {
for j := 0; j < matrixLength; j++ {
matrixA[i][j] = matrixA[i][j] + matrixB[j][i]
}
}
}

行优先与列优先问题,因CPU会遵从局部性原理,总是习惯于从连续的一片内存地址中读取。延展问题:如果CPU在多核状态下读取cache,如何保证缓存一致性,MESI协议的四个cache状态介绍。

以下代码的执行结果是什么?并简单解释为什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {
var a uint = 1
var b uint = 2
fmt.Println(a - b)
}

计算机基础数字运算问题,计算机对负数补码,最终二进制位反向填充得到该类型的机器位数最大值


3. 以下代码的执行结果是什么?
package main

import "fmt"

func main() {
a := []int{1, 2, 3, 4, 5}
b := a
b = append(a[:2], b[3:]...)
fmt.Println(a, b) // 12455 1245
}

切片基本运用,延展问题:切片在Go当中是什么类型的数据?
切片的扩容机制是什么,如果有一个长度5000的切片,扩容之后会变成多少?

slice 可变数组;
切片的底层是引用数组类型,所以切片是引用类型扩容机制:
预估容量,内存对齐

channel是否是线程安全的?基于什么实现线程安全的?无缓冲与有缓冲的区别是什么?如何利用channel来控制goroutine执行顺序?简单描述下channel的底层数据结构。

Golang的Channel,发送一个数据到Channel 和从Channel接收一个数据都是原子性的

实际上缓冲区就是一个数组

1
ch:=make(chan int , 5)

ch 是存在于函数栈上的一个指针,指向堆上的 hchan结构,channl底层结构:

  • 支持协程间并发访问, lock mutex
  • 有缓冲区,需要知道缓冲区在哪: buf unsafe.Pointer
  • 缓冲区已经存储了多少个元素qcount unit
  • 缓冲区最多存储多少个元素 dataqsiz unit
  • 每个元素站多大空间 elemsize uint16
  • golang运行中,内存复制 垃圾回收等机制,还需要知道缓冲区依赖数据的类型信息
    所以还需要一个指针,指向元素类洗净的元数据 elemtype *_type
  • channel 支持交替的读写,需要分别记录读和写的下标位置 sendx uint,recvx unit
  • 当读和写不能立即完成时,需要能够让当前协程在channel上等待,所以需要两个等待队列,针对读和写:recvq waitq, sendq waitq
  • channel可以close,所以需要一个字段记录close状态:closed unit32

chanel缓冲区被称为环形缓冲区

如果可以请利用channel写一个简单的控制流程代码,要求创建两个goroutine,一个打印字符串“hello”,另一个打印字符串“world”,最终结果无论执行多少次都必须先输出hello,再输出world。

channel的基本使用,利用通道阻塞(锁)来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main  

func hello(ch chan string) {
ch <- "hello"
}

func world(ch chan string) {
ch <- "world"
}

func main() {

ch1 := make(chan string) // 无缓冲区通道
go hello(ch1)
println(<-ch1)
go world(ch1)
println(<-ch1)
}

map是否是线程安全的?如何解决这一问题?代价是什么?如何减小锁的颗粒级别?

并发处理的简单应用。将map按业务颗粒拆成多个维度,将多协程竞争一把锁改成多协程竞争多把锁。

在并发情况下,只读是线程安全的,同时读写是线程安全的。

需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是以语言原生形态提供,而是在 sync 包下的特殊结构。Store 表示存储,Load 表示获取,Delete 表示删除。

一般情况下解决并发读写 map 的思路是加一把大锁,或者把一个 map 分成若干个小 map,对 key 进行哈希,只操作相应的小 map。前者锁的粒度比较大,影响效率;后者实现起来比较复杂,容易出错。

而使用 sync.map 之后,对 map 的读写,不需要加锁。并且它通过空间换时间的方式,使用 read 和 dirty 两个 map 来进行读写分离,降低锁时间来提高效率。

Golang中有哪几种锁类型?Mutex与RWMutex的区别是什么?Mutex是乐观锁还是悲观锁,简单介绍一下RWMutex的底层数据结构。

延展问题:Mutex锁有几种模式?什么情况下会触发饥饿模式,什么情况下会取消饥饿模式?

简单介绍一下GMP模型。如果某个G发生自旋一直占用CPU,Go会如何解决这个问题?

延展问题:超过一定时间后,Go的调度器会抢占回来,抢占的时机是STW。

简单阐述一下Go中会产生逃逸的几种场景。

延展问题:你是如何分析逃逸的,会借助什么样的工具? gcflags

简单介绍下你之前使用过的框架是如何实现优雅启停的,信号与信号量的区别,信号量与互斥量的区别?

延展问题:Linux有多少种信号,可靠与非可靠的区别?可以举例几个常见的信号么?

线程与协程的 区别是什么?可以简单介绍下用户态与内核态的区别么?

Linux操作系统的基础了解。

MySQL当中为什么通常主键ID都是自增的,有什么样的考虑?什么是覆盖索引?什么是前缀索引?如果对前缀索引字段进行排序,可以命中索引吗

底层数据结构和聚簇非聚簇索引的了解。延展问题:MySQL是利用什么机制来保障事务的ACID?MVCC只针对哪两个隔离级别起作用?为什么?

MySQL优化器什么情况下不会使用索引?如果有联合索引(a、b、c),查询条件为 :

1
2
- C = 1 and A = 2 and B = 3
- A = 1 and B > 2 and C = 3

分别会命中索引吗?为什么?

其他

kafka为什么性能好?从底层存储的实现以及架构上(partion),kafka中有哪些默认分区策略(key传没传,hash取模,轮询,随机(轮序和随机在有无可用分区的情况下是怎么切换的)),ConsumerGroup的理解(什么时候触发重平衡,基本流程是什么)

  1. make 和 new 的区别﹖
  2. 了解过golang的内存管理吗?
  3. 调用函数传入结构体时,应该传值还是指针﹖说出你的理由?
  4. 线程有几种模型?Goroutine的原理了解过吗,讲一下实现和优势?
  5. Goroutine什么时候会发生阻塞?
  6. PMG模型中Goroutine有哪几种状态?
  7. 每个线程/协程占用多少内存知道吗?
  8. 如果Goroutine—直占用资源怎么办,PMG模型怎么解决的这个问题?
  9. 如果若干线程中一个线程OOM,会发生什么?如果是Goroutine 呢? 项目中出现过OOM吗,怎么解决的?
  10. 项目中错误处理是怎么做的?
  11. 如果若干个Goroutine,其中有一个panic,会发生什么?
  12. defer可以捕获到其Goroutine的子Goroutine 的panic吗?
  13. 开发用Gin框架吗?Gin怎么做参数校验?
  14. 中间件使用过吗?怎么使用的。Gin的错误处理使用过吗?Gin中自定义校验规则知道怎么做吗?自定义校验器的返回值呢?
  15. golang中解析tag是怎么实现的?反射原理是什么?通过反射调用函数
  16. golang的锁机制了解过吗? Mutex的锁有哪几种模式,分别介绍一下? Mutex锁底层如何实现了解过吗?
  17. channel、channel使用中需要注意的地方?
  18. 数据库用的什么?数据库锁有了解吗?mysql锁机制讲一下。mysql分库分表。
  19. 讲一下redis分布式锁?redis主从模式和集群模式的区别了解过吗?redis的数据类型有哪些?redis持久化怎么做的?
  20. 编程题:你了解的负载均衡算法有什么?实现一个负载均衡算法。
CATALOG
  1. 1. Golang开发面试题汇总
    1. 1.1. 请分析以下代码两个方法的执行效率,哪个更快,为什么?
    2. 1.2. 以下代码的执行结果是什么?并简单解释为什么?
    3. 1.3. channel是否是线程安全的?基于什么实现线程安全的?无缓冲与有缓冲的区别是什么?如何利用channel来控制goroutine执行顺序?简单描述下channel的底层数据结构。
    4. 1.4. 如果可以请利用channel写一个简单的控制流程代码,要求创建两个goroutine,一个打印字符串“hello”,另一个打印字符串“world”,最终结果无论执行多少次都必须先输出hello,再输出world。
    5. 1.5. map是否是线程安全的?如何解决这一问题?代价是什么?如何减小锁的颗粒级别?
    6. 1.6. Golang中有哪几种锁类型?Mutex与RWMutex的区别是什么?Mutex是乐观锁还是悲观锁,简单介绍一下RWMutex的底层数据结构。
    7. 1.7. 简单介绍一下GMP模型。如果某个G发生自旋一直占用CPU,Go会如何解决这个问题?
    8. 1.8. 简单阐述一下Go中会产生逃逸的几种场景。
    9. 1.9. 简单介绍下你之前使用过的框架是如何实现优雅启停的,信号与信号量的区别,信号量与互斥量的区别?
    10. 1.10. 线程与协程的 区别是什么?可以简单介绍下用户态与内核态的区别么?
    11. 1.11. MySQL当中为什么通常主键ID都是自增的,有什么样的考虑?什么是覆盖索引?什么是前缀索引?如果对前缀索引字段进行排序,可以命中索引吗
    12. 1.12. MySQL优化器什么情况下不会使用索引?如果有联合索引(a、b、c),查询条件为 :
    13. 1.13. 其他