我扛住字節面試了,太干了!
大家好,我是小林。
春招進展快 2 個月,大家都找到暑期實習了嗎?今年相比往年我感覺比較卷,很少見到offer收割機的選手。
不管環境如何,持續學習這個是不能放棄的,心態也要穩一穩,坦然面對失敗,失敗才是常態,成功可能才是偶然的。
好了,雞湯喝完了,那我來一點干貨吧,又干又澀,毫無感情,但是又很有用。
今天分享一位讀者字節后端面經,讀者技術棧是C++和GO。
語言
C ++, Python 哪一個更快?
讀者答:這個我不知道從哪方面說,就是 C + + 的話,它其實能夠提供開發者非常多的權限,就是說它能涉及到一些操作系統級別的一些操作,速度應該挺快。然后 Python 實現功能還是蠻快的。
小林補充:
一般而言,C++更快一些,因為它是一種編譯型語言,可以直接編譯成機器碼,在執行時不需要解釋器的介入,因此執行效率較高。
Python是一種解釋型語言,需要在運行時通過解釋器將代碼轉換為機器碼來執行,因此相對于C++而言,執行效率較低。但是Python具有很多優秀的庫和框架,這些庫和框架可以幫助開發人員快速開發出高效的應用程序,從而提高開發效率。
編譯型語言和解釋型語言有沒有了解過
讀者答:可能 C + + 它提供的更偏機器語言一樣,就是只需要進行相關的編譯查找就可以。但是 Python 的話它可能是更像腳本語言,所以說在進行執行的時候還需要再進行嗯,一方面的句子語法處理,所以執行速度上會慢一些。
堆內存和棧內存有什么區別
讀者答:go里面的堆分配的主要是一些比較大的對象,棧里面的話可能就是分配一些臨時的對象,或者是比較小的數據變量。然后如果分配的就是比如說如果申請了一個channel,或者是一個非常大的對象的話,那么它就會從默認的從棧上的空間分配到堆上,因為堆上的這空堆上的空間進行分配之后,它能保留的時間會更長一些,這大概就是棧和堆之間的一個區別。
小林補充:
- 管理方式不同:棧內存由系統自動分配和釋放,而堆內存則需要程序員手動分配和釋放。
- 分配方式不同:棧內存是一種連續的內存空間,系統會自動為每個線程分配一定大小的棧空間,函數的參數和局部變量都會在棧上分配內存。而堆內存是不連續的內存空間,程序員需要通過動態分配內存來獲得堆內存空間。
- 大小限制不同:棧內存的大小是固定的,并且比堆內存小得多。程序運行時,每個線程的棧空間通常只有幾MB到幾十MB,而堆內存的大小則取決于系統的剩余內存大小和程序員的動態分配。
- 訪問速度不同:棧內存的訪問速度比堆內存快得多,因為棧內存是連續的,訪問局部變量和函數參數時可以直接讀取棧指針的偏移量。而堆內存是不連續的,訪問速度較慢。
局部變量是放在堆還是放在棧里面?
讀者答:局部變量的話,因為它只在一個函數里面進行相關的生命周期的存在,所以應該是在棧上面的。
鏈表跟數組有哪一些區別?
讀者答:鏈表的長度是不固定的,對它進行相關的插入或者是刪除操作是非常快的。查找需要從頭到尾遍歷值,效率低。
數組來說的話,數組的話是預先分配了一段固定長度的連續的內存空間,通過數組的下標索引來查找和賦值。但是如果要進行插入刪除操作的話,那可能會需要就是將數據中你要插入那個位置之后的所有的數組來進行挪位,才能進行相關的插入操作,所以說它這個插入和刪除的操作就會相比鏈表會麻煩。
小林補充:
可以提一下數組因為內存地址是連續,可以增加cpu緩存命中率,而鏈表的內存地址并不是連續的,cpu緩存命中率會很低。
數組怎么動態擴容?
讀者答:go 的話它其實提供了一個 slice 這樣子的結構,也就是說它底層維護了一個指向數組的指針,然后還維護了一個數組的長度和一個它的空間預存的一個 CAP 的容量值。然后如果將這個當前的數組 space 的大小它是小于 1024 的話,那基本上都是 2 倍的擴容,然后如果超過 1. 2 次的大小的話,基本上是 1. 25 倍的擴容。
協程與我們普通的線程有什么區別?
讀者答:協程可以理解為用戶級別的線程,所以說在大小方面和調度方面都是比進程要更加的方便和簡便的。
小林補充:
- 調度方式不同:線程是由操作系統調度的,而協程則是由程序員控制的。當一個線程被調度時,它會被操作系統掛起,等待下一次調度。而協程則是由程序員在代碼中主動調用的,可以在不同的任務之間切換,而不需要等待操作系統的調度。
- 系統資源占用不同:線程是操作系統管理的實體,它占用系統資源比較大,包括內存、線程棧、CPU 時間片等。而協程則是在用戶空間中實現的,不需要操作系統的支持,因此占用的資源比較少。
- 切換成本不同:線程的切換需要保存和恢復線程上下文,需要耗費一定的時間和資源。而協程的切換只需要保存和恢復棧幀等少量數據,因此切換成本比線程低。
- 編程模型不同:線程是面向操作系統的,而協程是面向任務的。線程需要使用操作系統提供的 API 進行線程間通信和同步,而協程則可以使用語言級別的協程庫實現協作式多任務。
協程的的通訊有哪些方式?
讀者答:不清楚,go 使用時,可能會配合 channel 來進行使用,起一個死循環,監聽不同的信號量進行處理。還是說是指那個 GMP 的模型?
小林補充:
- 共享內存:協程通過共享內存來交換數據,這種方式簡單直接,但需要考慮同步和互斥問題,否則會出現數據競爭等問題。
- 消息傳遞:協程通過消息隊列等方式來傳遞數據,這種方式可以避免數據競爭等問題,但需要考慮消息的發送和接收順序等問題。
- 信號量:協程通過信號量等方式來實現同步和互斥,這種方式需要考慮好信號量的數量和使用順序,否則會出現死鎖等問題。
常規的多線程開發需要注意哪一些問題?
讀者答:場景考慮,你這個線程是不可以就是無限制的創建的嗎?而且創建線程是需要開銷的,所以一般是會使用線程池這樣的方式,先預先創建好一些已經分配好的線程資源,然后有需要用的話就先進行相關的使用,然后這個線程池也負責了一些GC 的處理。
線程池里面大概要開多少的線程數量,這個線程數量的話就會跟你的任務相關。CPU 密集型根據CPU 的核數或者和任務執行的有關的時間來進行這個線程數量的考慮。
讀寫共享變量會遇到什么問題?
讀者答:并發訪問
什么是死鎖?
讀者答:兩個或多個進程之間他們都在等待一些資源,然后沒有辦法完全釋放現在已經占有的資源,但是他們需要的資源的話又被其他的進程所占有著,同時這一整個過程中你又沒有辦法去搶占,造成了一個循環等待的情況(請求保持,互斥,不可剝奪,循環等待)
如何避免出現死鎖,怎么排查?
讀者答:加鎖或者channel,打斷點,加日志信息。
小林補充:
避免死鎖:
- 按照固定的順序獲取鎖:按照固定的順序獲取鎖可以避免死鎖的發生。例如,如果A線程先獲取了鎖1,再獲取鎖2,那么B線程就應該先獲取鎖2,再獲取鎖1。
- 設置超時時間:在獲取鎖的過程中,可以設置超時時間,如果超過一定時間還沒有獲取到鎖,就放棄獲取鎖,避免因等待鎖而導致的死鎖。
排查方式:
- 使用工具:可以使用一些工具來幫助檢測和定位死鎖問題,例如jstack和jconsole等。
- 分析日志:可以分析系統日志和線程日志,查看是否有線程在等待某個鎖,從而找出可能導致死鎖的原因。
- 代碼檢查:可以檢查代碼中是否存在多個線程競爭同一個鎖的情況,是否存在鎖的嵌套等問題,從而找出可能導致死鎖的原因。
密碼學和計網
用到過哪些加密算法?
讀者答:
- Sha256 或者是 MD5 這樣子的加密算法。不能還原成原來的數據,用來比較數據是否一致。
- 對稱和非對稱的加密算法RSA
小林補充:
- 對稱加密算法:如DES、3DES、AES等,使用相同的秘鑰加密和解密數據,加解密速度快,但秘鑰管理困難。
- 非對稱加密算法:如RSA、ECC等,使用公鑰加密數據,私鑰解密數據,安全性高,但加解密速度較慢。
- 哈希算法:如MD5、SHA-1、SHA-256等,將任意長度的數據映射成固定長度的哈希值,不可逆、不可篡改,主要用于數據完整性校驗。
- 消息認證碼(MAC):如HMAC、CMAC等,將消息和秘鑰混合處理生成固定長度的認證碼,用于防止數據被篡改。
- 數字簽名算法:如RSA、DSA等,將消息和私鑰混合處理生成數字簽名,用于驗證消息的來源和完整性。
對稱和非對稱的加密算法的區別,具體講講非對稱
讀者答:對稱的話它是有一個公鑰和一個私鑰的,然后私鑰的話是只有自己持有的,這樣子的話那他別人其實不會獲取到你的CL,然后你通過一個嗯可信的第三方來進行相關的妙加密。這樣子的話別人只需要通過你的公鑰來進行一下獲取你的公鑰來進行你傳輸內容的加解密就可以,然后來保證這樣子的一個數據的安全性。
HTTP和HTTPS有什么區別?
讀者答:因為HTTP是明文傳輸的,然后 HTTPS 的話就是在這樣的基礎上增加SSL的一些加密的保護,來保證它傳輸消息的安全性。
小林補充:
- HTTP 是超文本傳輸協議,信息是明文傳輸,存在安全風險的問題。HTTPS 則解決 HTTP 不安全的缺陷,在 TCP 和 HTTP 網絡層之間加入了 SSL/TLS 安全協議,使得報文能夠加密傳輸。
- HTTP 連接建立相對簡單, TCP 三次握手之后便可進行 HTTP 的報文傳輸。而 HTTPS 在 TCP 三次握手之后,還需進行 SSL/TLS 的握手過程,才可進入加密報文傳輸。
- 兩者的默認端口不一樣,HTTP 默認端口號是 80,HTTPS 默認端口號是 443。
- HTTPS 協議需要向 CA(證書權威機構)申請數字證書,來保證服務器的身份是可信的。
怎么去使用這些加密算法?
讀者答:混合使用對稱和非對稱加密算法,會話密鑰是對稱的,獲取的過程是非對稱的。
小林補充:
通過混合加密的方式可以保證信息的機密性,解決了竊聽的風險。
混合加密
HTTPS 采用的是對稱加密和非對稱加密結合的「混合加密」方式:
- 在通信建立前采用非對稱加密的方式交換「會話秘鑰」,后續就不再使用非對稱加密。
- 在通信過程中全部使用對稱加密的「會話秘鑰」的方式加密明文數據。
采用「混合加密」的方式的原因:
- 對稱加密只使用一個密鑰,運算速度快,密鑰必須保密,無法做到安全的密鑰交換。
- 非對稱加密使用兩個密鑰:公鑰和私鑰,公鑰可以任意分發而私鑰保密,解決了密鑰交換問題但速度慢。
TCP 跟 UDP有什么區別?
讀者答:TCP 的話它提供了一系列的可靠傳輸機制來保證它這個傳輸是可靠的,相較而言的話,那它的傳輸速度就是慢的,而UDP 的話,它沒有做這個可靠的控制,它只是盡力而為,所以說它的傳輸速度是快的,而且占用的資源也會更小一些。具體使用的話要看不同的那個業務場景來進行相關的使用。
UDP 怎么改造變為可靠傳輸?
讀者答:應用層加序列號和ACK。然后本地會緩存一些自己已經發過的消息,然后同時也要求對方在收到消息之后返回 ACK 來確認這個消息已經收到,如果沒有收到 ACK 的話,可能會設置一些定時重傳的這樣的一些方式,來確保這個消息能夠成功的發送到對方。
小林補充:
還要在應用層實現滑動窗口,實現流量控制,根據接收方的接收能力才發送數據,還有擁塞控制,當網絡中有大量數據包同時傳輸時,會導致網絡擁塞,從而影響數據傳輸的質量和效率。TCP擁塞控制通過動態調整數據傳輸速率,避免了網絡擁塞的發生,從而保證了數據傳輸的可靠性和高效性。
數據庫
MySQL常見的性能優化方式
讀者答:索引和分庫分表
加了索引之后,插入速度跟那個讀的速度有什么區別?
讀者答:b +樹索引結構它其實跟實際的磁盤結構是比較一致的,使得隨機寫變成相關的順序寫,插入速度變快的。將所有的數據結數據信息的話都存在葉子節點,能夠提高范圍查找的速度。
Redis在架構中會起到什么作用?
讀者答:當緩存使用的
可以直接存到進程內存,現在為什么要專門做Redis?
讀者答:因為Redis快(面試官說不是,因為還存在進程通訊,不如直接內存快;自己去實現一份緩存不一定有Redis好,在分布式架構中Redis能做同步)
算法題
- 打印圣誕樹
- 每隔1s調用一個會panic然后之后會recover的函數
項目問題:
忽略....