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
英文地址,請注明出處:http://en.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)按服務…