Go开发工程师-Golang基础知识篇

开篇

我们尝试从2个方面来进行介绍:

1. 社招实际面试问题

2. 问题涉及的基础点梳理

社招面试题

米哈游

1. Go 里面使用 Map 时应注意问题和数据结构

2. Map 扩容是怎么做的?
3. Map 的 panic 能被 recover 掉吗?了解 panic 和 recover 的机制吗?

4. Map 怎么知道自己处于竞争状态?是 Go 编码实现的还是底层硬件实现的?

5. 有对比过 sync.Map 和加锁的区别吗?

富途牛牛

1. Golang 标准库中 map 的底层数据结构是什么样子的

2. Map 的查询时间复杂度如何分析?

3. 极端情况下有很多哈希冲突,Golang 标准库如何去避免最坏的查询时间复杂度?

4. Golang map Rehash 的策略是怎样的?什么时机会发生 Rehash?

5. Rehash 具体会影响什么?哈希结果会受到什么影响?

6. Rehash 过程中存放在旧桶的元素如何迁移?

7. sync.Map 的 Load() 方法流程?

8. sync.Map Store() 如何保持缓存层和底层 Map 数据是相同的? 是不是每次执行修改都需要去加锁?

基础点梳理

常见的基础数据结构,包括数组、map,链表、栈和队列、堆、树、图,不过在Golang日常的使用和面试问题中,其实主要就只有slice和map。

在面试场景中更重中之重,几乎一定会考察的就是map的数据结构,一方面是在Golang应用开发中比较广泛,另一方面是这里有几个相对好考察的点,基本上我们掌握如下一些问题,对于面试来说基本也就够了,毕竟就算大厂面试,一般最多也就一个小时,这还包括算法题,所以大体上不会在这方面非常的细节。

Slice

一个slice是一个数组某个部分的引用。在内存中,它是一个包含3个域的结构体:指向slice中第一个元素的指针,slice的长度,以及slice的容量。

理解了这个概念,我们大体去了解这么几件事:

1. 数组是定长的,但是切片是非常灵活的,所以可以动态扩容,那动态扩容如何去扩,可以直接参考这里的文章。

切片的容量是怎样增长的 | Go 程序员面试笔试宝典

2. 很多人在写函数传参中,很多人知道Golang所有的函数参数传递都是值传递,那我们不免有一个担心,如果我们直接把slice传入进去到底会不会因为数据量过大,导致出问题。

其实不会,因为对于传参来说,就是一个普通的结构体,包括三个参数,长度/容量/底层数据的地址,所以这几个拷贝是比较快的。

Map

Golang面试数据结构中,Map是重中之重,所以我们必须把这里的问题都弄清楚,

1. Map的底层数据结构是什么样子的

2. Map的日常使用中需要注意哪些问题

3. Map的扩容是怎么做的

4. sync.Map的实现

我们总结下来,关于Map基本上就是如下几个场景:

1. 先说说Map的底层数据结构:

map 的实现原理 | Go 程序员面试笔试宝典

map的实现 · 深入解析Go

这两篇文章都可以参考:

重点是对这张图有一些概念,map的底层实现就是Hash,bmap就是我们常说的桶,桶里面会最多装8个key,这些 key 之所以会落入同一个桶,是因为它们经过哈希计算后,哈希结果是“一类”的。在桶内,又会根据 key 计算出来的 hash 值的高 8 位来决定 key 到底落入桶内的哪个位置。

另外注意一个细节是Bucket中key/value的放置顺序,是将keys放在一起,values放在一起,为什么不将key和对应的value放在一起呢?如果那么做,存储结构将变成key1/value1/key2/value2… 设想如果是这样的一个map[int64]int8,考虑到字节对齐,会浪费很多存储空间。不得不说通过上述的一个小细节,可以看出Go在设计上的深思熟虑。

2. Map的日常使用中需要注意哪些问题

1. 未初始化时写操作会panic,记得使用make方法进行初始化

2. 并发读写导致panic,这个是map日常非常需要注意的地方,尤其是在日常开发中,因为很多时候我们可能不会刻意为之,但是不经意的会在程序中出现这个问题。

3. Map的扩容是怎么做的

扩容触发条件
  • 负载因子 > 6.5(平均每个桶元素超过6.5个)

  • 溢出桶过多(当B < 15时溢出桶超过2^B;当B >= 15时溢出桶超过2^15

负载因子 = 元素总数量(map元素个数) / 桶数量(Bucket数量)
  1. 桶数量(Bucket Count)
    hmap结构体的B字段决定,桶数量 = 2^B

    • 例:B=3 → 桶数量 = 8

  2. 元素数量(Element Count)
    即当前map中存储的键值对总数,对应hmap.count

  3. 负载因子阈值
    默认阈值 = 6.5(硬编码在Go源码中)

    • 当 负载因子 > 6.5 时触发增量扩容(桶数量翻倍)

  4. 为什么阈值是6.5?

  5. 平衡空间与性能

    • 过低(如4):扩容频繁 → 内存浪费

    • 过高(如10):哈希冲突激增 → 查找性能下降

  6. 经验值
    经过性能测试,6.5能在冲突率和内存占用间取得最佳平衡。

hash的扩容过程:https://golang.design/go-questions/map/extend/

核心点在于:

扩容流程(增量扩容为例):
  1. 分配新桶数组(大小为原来的2倍)

  2. 逐步迁移旧桶数据到新桶(每次写操作迁移1-2个桶)

  3. 迁移期间读取:优先查旧桶,未找到再查新桶

  4. 迁移完成后释放旧桶

4. sync.Map的实现

type Map struct {
    mu Mutex               // 写操作锁(保护 dirty 字段)
    read atomic.Value      // 只读缓存(atomic 访问,无锁)
    dirty map[any]*entry   // 写操作目标(需 mu 保护)
    misses int             // read 未命中次数(触发 dirty 提升)
}

type entry struct {
    p unsafe.Pointer  // 指向实际值(特殊标记:nil、expunged)
}

最核心的优化点,主要是用来了一个dirty的map和一个只读的map.

read好比整个sync.Map的一个“高速缓存”,当goroutine从sync.Map中读数据时,sync.Map会首先查看read这个缓存层是否有用户需要的数据(key是否命中),如果有(key命中),则通过原子操作将数据读取并返回,这是sync.Map推荐的快路径(fast path),也是sync.Map的读性能极高的原因。

  • 操作:直接写入dirty(负责写的map)
  • 操作:先读read(负责读操作的map),没有再读dirty(负责写操作的map)

sync.Map 的实现原理可概括为:

  1. 通过 read 和 dirty 两个字段实现数据的读写分离,读的数据存在只读字段 read 上,将最新写入的数据则存在 dirty 字段上
  2. 读取时会先查询 read,不存在再查询 dirty,写入时则只写入 dirty
  3. 读取 read 并不需要加锁,而读或写 dirty 则需要加锁
  4. 另外有 misses 字段来统计 read 被穿透的次数(被穿透指需要读 dirty 的情况),超过一定次数则将 dirty 数据更新到 read 中(触发条件:misses=len(dirty))

与 Mutex+Map 的底层差异

特性sync.MapMutex + Map
读性能⭐⭐⭐ 无锁(atomic)⭐ 每次读需加锁
写性能⭐ 需锁升级(可能触发 dirty 提升)⭐⭐ 单一锁保护
内存占用高(双份数据 + entry 包装)低(直接存储)
删除开销低(逻辑删除 + 延迟清理)高(立即删除 + 缩容开销)
适用场景读 >> 写(如配置存储、缓存)读写均衡或写多读少

再回头看一下这些面试题:

1. Golang 标准库中 map 的底层数据结构是什么样子的

 这个上面已经介绍过了,不再介绍

2. Map 的查询时间复杂度如何分析?

  • 平均情况:O(1)

    • 通过哈希值定位桶:O(1)

    • 桶内遍历(最多8个元素+溢出桶):O(1)

  • 最坏情况:O(N)

    • 所有元素哈希冲突到同一桶链

    • 需遍历整个桶链(N为元素总数)

这道问题主要考察的就是Map的底层数据结构,了解的话,其实这个很容易得知。

3. 极端情况下有很多哈希冲突,Golang 标准库如何去避免最坏的查询时间复杂度?

  • 桶分裂策略

    • 每个桶最多存8个键值对,超限创建溢出桶

  • 渐进式扩容

    • 负载因子>6.5时触发扩容(桶数量×2)

  • 哈希随机化

    // 初始化时生成随机种子
    h.hash0 = fastrand()
  • SIMD 优化

    • 使用AVX2指令并行比对tophash(Go 1.20+)

4. Golang map Rehash 的策略是怎样的?什么时机会发生 Rehash?

这个上面也说过了,不再赘述,重要的点,还是理解了底层结构是hash,所以我们就知道必须去做Rehash的原因

5. Rehash 具体会影响什么?哈希结果会受到什么影响?

6. Rehash 过程中存放在旧桶的元素如何迁移?

7. sync.Map 的 Load() 方法流程?

func (m *Map) Load(key any) (value any, ok bool) {// Step 1: 原子读取readOnlyread, _ := m.read.Load().(readOnly)e, ok := read.m[key]if !ok && read.amended { // read不存在且dirty有数据m.mu.Lock()// Step 2: 双检查(避免锁竞争期间read更新)read, _ = m.read.Load().(readOnly)e, ok = read.m[key]if !ok && read.amended {// Step 3: 查询dirty(加锁)e, ok = m.dirty[key]// Step 4: 更新miss计数器m.missLocked() }m.mu.Unlock()}if !ok { return nil, false }// Step 5: 从entry加载实际值return e.load() 
}

8. sync.Map Store() 如何保持缓存层和底层 Map 数据是相同的? 是不是每次执行修改都需要去加锁?

func (m *Map) Store(key, value any) {// 情况1:read中存在可直接更新的条目if e, ok := read.m[key]; ok && e.tryStore(&value) {return // 无锁CAS更新}m.mu.Lock()// 情况2:条目在read中但被标记删除if e.unexpungeLocked() { m.dirty[key] = e // 重新加入dirty}// 情况3:新增条目if !read.amended {m.dirtyLocked() // 惰性初始化dirtym.read.Store(readOnly{m: read.m, amended: true // 标记dirty有额外数据})}m.dirty[key] = newEntry(value) // 写入dirtym.mu.Unlock()
}

总结

作为一名工程师,其实需要对各类数据结构都应该有深入的一些理解,这个算是一个基本功,包括树、图等结构体。但是从大环境来说,一般业务上用到树和图的场景也是比较少,用的比较多的就是slice+map,考察点上,基本也就是上面的问题,把这些弄清楚,面试上基本不会有太大问题。但是日常来说还有一些是需要注意的:
1. slice的范围判断,避免出现panic问题。

2. slice和map有很多常用的使用方式,可以进行统一封装

希望大家面试顺利

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/912187.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/912187.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

能否仅用两台服务器实现集群的高可用性??

我们将问题分为两部分来回答&#xff1a;一是使用 Redis 或 Hazelcast 确保数据一致性后是否仍需 Oracle 或 MySQL 等数据库&#xff1b;二是能否仅用两台服务器实现集群的高可用性。以下是详细探讨&#xff1a; 1. 使用 Redis 或 Hazelcast 确保数据一致性后&#xff0c;还需要…

spring-ai-alibaba DashScopeCloudStore自动装配问题

问题 在学习spring-ai-alibaba时&#xff0c;发现1.0.0.2版本在自动装配DashScopeCloudStore时&#xff0c;会报如下错误&#xff1a; Field dashScopeCloudStore in com.example.spring_ai_alibaba_examples.examples.SpringAiAlibabaExample01 required a bean of type com…

docker-compose部署nacos

1、docker-compose内容 高版本的nacos使用docker启动&#xff0c;需要将所有的端口放开&#xff0c;仅仅开放8848端口&#xff0c;spring-boot客户端获取nacos配置的时候&#xff0c;可能取到的内容为空。 version: 3# 定义自定义网络&#xff0c;确保服务间通信和外部访问 ne…

CSRF 与 SSRF 的关联与区别

CSRF 与 SSRF 的关联与区别 区别 特性CSRF (跨站请求伪造)SSRF (服务器端请求伪造)攻击方向客户端 → 目标网站服务器 → 内部/外部资源攻击目标利用用户身份执行非预期操作利用服务器访问内部资源或发起对外请求受害者已认证的用户存在漏洞的服务器利用条件用户必须已登录目…

Payload-SDK自动升级

Payload-SDK自动升级 前言 自动升级旨在通过无人机更新负载上的软件&#xff0c;包括不限于&#xff1a;Payload-SDK应用、配置文件等。对于文件的传输&#xff0c;大疆的Payload-SDK给我们提供了两种方式&#xff1a;使用FTP协议和使用大疆自研的DCFTP。我们实现的自动升级是…

第五代移动通信新型调制及非正交多址传输技术研究与设计

第五代移动通信新型调制及非正交多址传输技术研究与设计 一、新型调制技术研究与实现 1. FBMC (滤波器组多载波) 调制实现 import numpy as np import matplotlib.pyplot as plt from scipy.fft import fft, ifft, fftshift from scipy.signal import get_window

AI 智能运维,重塑大型企业软件运维:从自动化到智能化的进阶实践​

一、引言&#xff1a;企业软件运维的智能化转型浪潮​ 在数字化转型加速的背景下&#xff0c;大型企业软件架构日益复杂&#xff0c;微服务、多云环境、分布式系统的普及导致传统运维模式面临效率瓶颈。AI 技术的渗透催生了智能运维&#xff08;AIOps&#xff09;的落地&#x…

Apache CXF安装详细教程(Windows)

本章教程,主要介绍,如何在Windows上安装Apache CXF,JDK版本是使用的1.8. 一、下载Apache CXF Apache CXF(Apache Celtix Fireworks)是一个开源的 Web 服务框架,用于 构建和开发服务端与客户端的 Web 服务应用程序。它支持多种 Web 服务标准,尤其是 SOAP(基于 XML 的协议…

逆向入门(22)程序逆向篇-TraceMe

界面看起来很普通 也没有壳&#xff0c;直接搜索字符串找到关键代码处 但是发现这些都是赋值&#xff0c;并没有实现跳转相关的函数。这里通过给弹窗函数下断点&#xff0c;追一下返回函数来找触发点。 再次点击check&#xff0c;触发断点&#xff0c;接着按ctrlF9返回到函数…

中文PDF解析准确率排名

市面上的文档解析工具种类各异&#xff0c;包括更适用于论文解析的&#xff0c;专精于表格数据提取的&#xff0c;针对手写体优化的&#xff0c;适用于技术文档的&#xff0c;擅长处理复杂多语言混排文档的&#xff0c;专门处理政府招标文档表格的&#xff0c;以及擅长金融类表…

Conformal LEC:官方学习教程

相关阅读 Conformal LEChttps://blog.csdn.net/weixin_45791458/category_12993839.html?spm1001.2014.3001.5482 本文是对Conformal Equivalence Checking User Guide中附录实验的翻译&#xff08;有删改&#xff09;&#xff0c;实验文件可见安装目录Conformal/share/cfm/l…

【Torch】nn.Embedding算法详解

1. 定义 nn.Embedding 是 PyTorch 中的 查表式嵌入层&#xff08;lookup‐table&#xff09;&#xff0c;用于将离散的整数索引&#xff08;如词 ID、实体 ID、离散特征类别等&#xff09;映射到一个连续的、可训练的低维向量空间。它通过维护一个形状为 (num_embeddings, emb…

cdq 三维偏序应用 / P4169 [Violet] 天使玩偶/SJY摆棋子

最近学了 cdq 分治想来做做这道题&#xff0c;结果被有些毒瘤的代码恶心到了。 /ll 题目大意&#xff1a;一开始给定一些平面中的点。然后给定一些修改和询问&#xff1a; 修改&#xff1a;增加一个点。询问&#xff1a;给定一个点&#xff0c;求离这个点最近&#xff08;定义…

System.Threading.Tasks 库简介

System.Threading.Tasks 是 .NET 中任务并行库(Task Parallel Library, TPL)的核心组件&#xff0c;它提供了基于任务的异步编程模型&#xff0c;是现代 .NET 并发编程的基础。 设计原理 1. 核心目标 抽象并发工作&#xff1a;将并发操作抽象为"任务"概念 资源高效…

Python爬虫实战:研究jieba相关技术

1. 引言 1.1 研究背景与意义 随着互联网技术的飞速发展,网络新闻已成为人们获取信息的主要渠道之一。每天产生的新闻文本数据量呈爆炸式增长,如何从海量文本中高效提取有价值的信息,成为信息科学领域的重要研究课题。文本分析技术通过对文本内容的结构化处理和语义挖掘,能…

github 淘金技巧

1. 效率&#xff0c;搜索&#xff0c;先不管。后面再说。 2. 分享的话&#xff0c; 其实使用默认的分享功能也行。也是后面再说。此 app &#xff0c; 今天先做到这里。 下面我们再聊点其他东西。其实我还想问&#xff0c;这个事情&#xff0c;其他人是否也做了&#xff0c; ht…

RAG技术发展综述

摘要 检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;技术已成为大语言模型应用的核心技术栈。RAG有效解决了LLM的幻觉问题、知识截止和实时更新挑战&#xff0c;目前正处于全面产业化阶段。本文系统性地分析RAG的全栈技术架构&#xff0c;包括检索…

集群聊天服务器---muduo库(3)

使用muduo网络库进行编译和链接的示例 项目的目录结构 bin: 存放可执行文件。 lib: 存放库文件。 include: 存放头文件。 src: 存放源代码文件。 build: 存放编译生成的中间文件。 example: 存放示例代码。 thirdparty: 存放第三方库。 CMakeLists.txt: CMake构建系统…

双核SOC/5340 应用和网络核间通讯

1&#xff1a; 可以在 nRF Connect SDK 文件夹结构的 samples/ipc/ipc_service 下找到示例&#xff0c;应用和网络核心在由 CONFIG_APP_IPC_SERVICE_SEND_INTERVAL 选项指定的时隙内相互发送数据。可以更改该值并观察每个核心的吞吐量如何变化 nRF5340 DK 可以使用 RPMsg 或 IC…

Spring Cloud Ribbon核心负载均衡算法详解

Ribbon 作为 Spring Cloud 生态中的客户端负载均衡工具&#xff0c;提供多种动态负载均衡算法&#xff0c;根据后端服务状态智能分配请求。其核心算法及适用场景如下&#xff1a; &#x1f9e0; 一、Ribbon 负载均衡算法 算法名称工作原理引用来源轮询 (RoundRobinRule)按服务…