美團面試,問的都是基礎啊!

架構師修行手冊發表於2023-03-29

來源:小林coding

大家好,我是小林。

今天分享一位讀者的春招面經,美團基礎架構的面經。

問的全是基礎,一個程式語言的問都沒有。

問題記錄

MySQL-MVCC

讀者答:InooDB是透過 MVCC 實現可重複讀的隔離級別的,MVCC 就是多版本併發控制,它其實記錄了歷史版本的資料,解決了讀寫併發衝突問題。有一個版本編碼,然後它進入了各種操作下的資料狀態,能夠根據當前這個指令的狀態來讀取不同時期的資料快照。主要實現方法的話就是透過事務版本號,讀取檢視還有undo日誌進行完善的。

小林補充:具體的實現原理過程,可以去 xiaolincoding.com 網站->圖解MySQL->事務隔離級別是怎麼實現的?這篇文章學習。

MySQL-原子性怎麼實現的

說錯了,說成redoLog了。應該是undoLog。

讀者答:原子性的話會在寫資料之前有一個,就是WAL的過程,就是寫一個 redo log,然後如果資料沒有寫完或者是執行操作失敗的話,可以恢復所有已提交的事務或者回滾。

小林補充:

事務的原子性是透過 undo log 實現的。

undo log 是一種用於撤銷回退的日誌。在事務沒提交之前,MySQL 會先記錄更新前的資料到 undo log 日誌檔案裡面,當事務回滾時,可以利用 undo log 來進行回滾。如下圖:

美團面試,問的都是基礎啊!

每當 InnoDB 引擎對一條記錄進行操作(修改、刪除、新增)時,要把回滾時需要的資訊都記錄到 undo log 裡,比如:

  • 插入一條記錄時,要把這條記錄的主鍵值記下來,這樣之後回滾時只需要把這個主鍵值對應的記錄刪掉就好了;
  • 刪除一條記錄時,要把這條記錄中的內容都記下來,這樣之後回滾時再把由這些內容組成的記錄插入到表中就好了;
  • 更新一條記錄時,要把被更新的列的舊值記下來,這樣之後回滾時再把這些列更新為舊值就好了。

在發生回滾時,就讀取 undo log 裡的資料,然後做原先相反操作。比如當 delete 一條記錄時,undo log 中會把記錄中的內容都記下來,然後執行回滾操作的時候,就讀取 undo log 裡的資料,然後進行 insert 操作。

不同的操作,需要記錄的內容也是不同的,所以不同型別的操作(修改、刪除、新增)產生的 undo log 的格式也是不同的,具體的每一個操作的 undo log 的格式我就不詳細介紹了,感興趣的可以自己去查查。

MySQL-永續性是怎麼實現的

讀者答:透過 redo log 保證持久化。buffer pool 中有 undo 頁,對 undo 頁的修改也都會記錄到 redo log。redo log 會每秒刷盤,提交事務時也會刷盤,資料頁和 undo 頁都是靠這個機制保證持久化的。

透過兩次寫來實現,當緩衝池的髒頁重新整理時,並不直接寫磁碟,而是會透過memcpy函式將髒頁先複製到記憶體中的doublewrite buffer,之後透過doublewrite buffer再分兩次,每次寫入1MB到共享表空間的物理磁碟上,然後馬上呼叫fsync函式,同步磁碟,進行資料持久化。

小林補充:

事務的永續性是透過  redo log  實現的。

我們修改某條記錄,其實該記錄並不是馬上刷入磁碟的,而是將 Innodb 的 Buffer Pool  標記為髒頁,等待後續的非同步刷盤。

Buffer Pool 是提高了讀寫效率沒錯,但是問題來了,Buffer Pool 是基於記憶體的,而記憶體總是不可靠,萬一斷電重啟,還沒來得及落盤的髒頁資料就會丟失。

為了防止斷電導致資料丟失的問題,當有一條記錄需要更新的時候,InnoDB 引擎就會先更新記憶體(同時標記為髒頁),然後將本次對這個頁的修改以 redo log 的形式記錄下來,這個時候更新就算完成了

後續,InnoDB 引擎會在適當的時候,由後臺執行緒將快取在 Buffer Pool 的髒頁重新整理到磁碟裡,這就是 WAL (Write-Ahead Logging)技術

WAL 技術指的是, MySQL 的寫操作並不是立刻寫到磁碟上,而是先寫日誌,然後在合適的時間再寫到磁碟上

過程如下圖:

美團面試,問的都是基礎啊!

redo log 是物理日誌,記錄了某個資料頁做了什麼修改,比如對 XXX 表空間中的 YYY 資料頁 ZZZ 偏移量的地方做了AAA 更新,每當執行一個事務就會產生這樣的一條或者多條物理日誌。

在事務提交時,只要先將 redo log 持久化到磁碟即可,可以不需要等到將快取在 Buffer Pool 裡的髒頁資料持久化到磁碟。

當系統崩潰時,雖然髒頁資料沒有持久化,但是 redo log 已經持久化,接著 MySQL 重啟後,可以根據 redo log 的內容,將所有資料恢復到最新的狀態。

作業系統-死鎖怎麼產生的

讀者答:死鎖會產生的話一般會出現就是嗯資源就是互相佔用,但是沒有辦法解鎖,形成迴圈這樣的情況,比如說 a 執行緒有一部分 b 執行緒需要的資源, b 執行緒有一部分 a 需要的資源,那他兩個人互相的互斥等待形成了死鎖,兩個執行緒都沒有辦法完成任務。

小林補充:

死鎖問題的產生是由兩個或者以上執行緒並行執行的時候,爭奪資源而互相等待造成的。

死鎖只有同時滿足互斥、持有並等待、不可剝奪、環路等待這四個條件的時候才會發生。

所以要避免死鎖問題,就是要破壞其中一個條件即可,最常用的方法就是使用資源有序分配法來破壞環路等待條件。

作業系統-怎麼避免死鎖

讀者答:

  • 避免死鎖的話可以手動的 kill 掉某一個程式來結束當前的死鎖狀態。
  • 也可以說設定一些搶佔的規則。如果這個程式佔用的時間非常長的話,透過上下文切換給另外一個程式執行的機會,
  • 也可以在分配資源的時候進行預先的設計,就是有一個銀行家演算法來進行一個不會發生死鎖的程式執行的排程

作業系統-pageCache是什麼

讀者答:快取一些比較常訪問的檔案到快取中,這樣子的話它就能減少兩次從核心空間複製的過程,就是來減少查詢這個內容的時間。

小林補充:

為了提升對檔案的讀寫效率,Linux 核心會以頁大小(4KB)為單位,將檔案劃分為多資料塊。當使用者對檔案中的某個資料塊進行讀寫操作時,核心首先會申請一個記憶體頁(稱為 頁快取)與檔案中的資料塊進行繫結。如下圖所示:

美團面試,問的都是基礎啊!

如上圖所示,當使用者對檔案進行讀寫時,實際上是對檔案的 頁快取 進行讀寫。所以對檔案進行讀寫操作時,會分以下兩種情況進行處理:

  • 當從檔案中讀取資料時,如果要讀取的資料所在的頁快取已經存在,那麼就直接把頁快取的資料複製給使用者即可。否則,核心首先會申請一個空閒的記憶體頁(頁快取),然後從檔案中讀取資料到頁快取,並且把頁快取的資料複製給使用者。
  • 當向檔案中寫入資料時,如果要寫入的資料所在的頁快取已經存在,那麼直接把新資料寫入到頁快取即可。否則,核心首先會申請一個空閒的記憶體頁(頁快取),然後從檔案中讀取資料到頁快取,並且把新資料寫入到頁快取中。對於被修改的頁快取,核心會定時把這些頁快取重新整理到檔案中。

計算機網路-TCP的可靠性和順序性怎麼實現的

讀者答:TCP 它實現可靠性和有序性的操作的話,是透過快重傳或者是回退 n 這樣子的設計來實現。如果報文在傳遞的過程中丟失之後能夠進行重傳。而會怎麼能發現這個報文丟失呢?主要是根據一些序列號和 ACK的配合來幫助兩個服務之間知道當前傳遞的資訊會丟失。

計算機網路-怎麼進行流量控制的?

回答成擁塞控制了;

讀者答:內部維護了一個能接收訊息的一個視窗的大小,如果他出現就是訊息丟失的情況,然後這個訊息視窗的大小會減半。啟動的時候採用慢啟動的方式,從0開始指數級增加視窗大小,直到到達閥值之後線性增加視窗大小。

小林補充:

流量控制主要是可以讓「傳送方」根據「接收方」的實際接收能力控制傳送的資料量。

實現的方式,接收方會有一個接收緩衝區,如果核心接收到了資料,沒有被應用讀取的話,接收視窗就會收縮,然後會在tcp報文攜帶接收視窗的大小,傳送發收到後,就會控制的傳送流量。

下面舉個例子,為了簡單起見,假設以下場景:

  • 客戶端是接收方,服務端是傳送方
  • 假設接收視窗和傳送視窗相同,都為 200
  • 假設兩個裝置在整個傳輸過程中都保持相同的視窗大小,不受外界影響
美團面試,問的都是基礎啊!

根據上圖的流量控制,說明下每個過程:

  1. 客戶端向服務端傳送請求資料包文。這裡要說明下,本次例子是把服務端作為傳送方,所以沒有畫出服務端的接收視窗。
  2. 服務端收到請求報文後,傳送確認報文和 80 位元組的資料,於是可用視窗 Usable 減少為 120 位元組,同時 SND.NXT 指標也向右偏移 80 位元組後,指向 321,這意味著下次傳送資料的時候,序列號是 321。
  3. 客戶端收到 80 位元組資料後,於是接收視窗往右移動 80 位元組,RCV.NXT 也就指向 321,這意味著客戶端期望的下一個報文的序列號是 321,接著傳送確認報文給服務端。
  4. 服務端再次傳送了 120 位元組資料,於是可用視窗耗盡為 0,服務端無法再繼續傳送資料。
  5. 客戶端收到 120 位元組的資料後,於是接收視窗往右移動 120 位元組,RCV.NXT 也就指向 441,接著傳送確認報文給服務端。
  6. 服務端收到對 80 位元組資料的確認報文後,SND.UNA 指標往右偏移後指向 321,於是可用視窗 Usable 增大到 80。
  7. 服務端收到對 120 位元組資料的確認報文後,SND.UNA 指標往右偏移後指向 441,於是可用視窗 Usable 增大到 200。
  8. 服務端可以繼續傳送了,於是傳送了 160 位元組的資料後,SND.NXT 指向 601,於是可用視窗 Usable 減少到 40。
  9. 客戶端收到 160 位元組後,接收視窗往右移動了 160 位元組,RCV.NXT 也就是指向了 601,接著傳送確認報文給服務端。
  10. 服務端收到對 160 位元組資料的確認報文後,傳送視窗往右移動了 160 位元組,於是 SND.UNA 指標偏移了 160 後指向 601,可用視窗 Usable 也就增大至了 200。

Redis-怎麼持久化的資料

讀者答:Redis 的話,它其實提供了兩種持久化資料的方法,一種是AOF,一種是RDB。然後 AOF 的話它是一種,就是說每一條操作資訊它都會進行追加記錄這樣的一種持久化的方式。當那個資料庫重新啟動的時候,它就會根據 AOF 裡面記錄的資料操作,然後來進行一個資料庫內容的重建。而 RDB 的話,它是做快照,也就是說在資料庫執行的過程中,它可能會另開一個 IO 的執行緒來進行資料庫的快照記錄,這樣子的話來記錄它某一個時間段的資料情況,這樣子它進行恢復,資料庫再次啟動的時候就可以直接根據 RDB 檔案來進行恢復這兩個操作。

這樣一執行的話就可以看出來, AOF 的話,它雖然就是在執行的過程中效能的損耗是小的,但是如果資料庫要進行重新啟動的話,那它需要的耗時是比較長的。而 RDB 的話,它雖然重新啟動的耗時小,但是說它在過程中會有一定的效能損耗。而且如果是在兩個快照建立的中間就是資料庫當機,或者是這樣子沒有做成快照的話,會造成一部分資料的缺失。

Redis-叢集是怎麼做的?就是資料怎麼分片的,然後它的叢集的高可用是什麼?怎麼部署的,這個有沒有了解過?

讀者答:我瞭解到它是有一個主從模型的,從它從模型的話就是複製一份主節點的備份,然後如果主節點當機的情況下,從節點是可以成為主節點來提供服務的,別的就沒有什麼瞭解的。

後續查資料補充add:在應對資料量擴容時,雖然增加記憶體這種縱向擴充套件的方法簡單直接,但是會造成資料庫的記憶體過大,導致效能變慢。Redis 切片叢集提供了橫向擴充套件的模式,也就是使用多個例項,並給每個例項配置一定數量的雜湊槽,資料可以透過鍵的雜湊值對映到雜湊槽,再透過雜湊槽分散儲存到不同的例項上。這樣做的好處是擴充套件性好,不管有多少資料,切片叢集都能應對。

分散式-分散式事務是什麼

讀者答:我只知道分散式事務中的 2 階段提交和 3 階段提交這樣一個概念

  • 2 階段提交:準備階段和提交階段
  • 3 階段提交:比2PC多了一個階段,即把原來2PC的準備階段拆分成CanCommit和PreCommit兩個階段,同時引入超時機制來解決2PC的同步阻塞問題。

後續查資料補充add:實際使用都是使用訊息佇列+本地訊息表保證最終一致性,2PC這種強一致性用在一些金融業務中,實現很麻煩。

分散式-paxos和raft的區別

讀者答:Paxos在一個節點當選為就是 leader 節點之後,其他的從節點如果不滿主節點的那個投票策略的話,是可以對主節點的投票就是進行否決的。Paxos就是三階段提交。但是 raft 的話就是隻要叢集中存在 leader 節點的話,從節點就是會按照主節點的策略來進行一致性的執行。

分散式-為什麼就是分散式的共識演算法都需要要求多數派提交才能完成它的分散式一致性?

讀者答:有作惡節點,訊息可能到的順序不一樣,扯了拜占庭問題

場景題

  • 設計一個執行緒池

  • LRU  快取設計

面試總結

感覺

面試官想多要些人填志願,基礎知識沒有深挖,所有的知識點都考察了一下

不足之處

  1. 基礎知識還要再紮實
  2. 場景題需要結合實際開發,有些過於書本知識了,實際不用

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027824/viewspace-2942319/,如需轉載,請註明出處,否則將追究法律責任。

相關文章