面試技巧:如何就軟件規模化進行討論

本文最初發表於 Denise Yu 個人博客,經原作者 Denise Yu 授權,InfoQ 中文站翻譯並分享。

我最近注意到,在開放式、“無代碼”技術面試中如何脫穎而出,有關這方面的指導並不多。通常情況下,應聘者在面試高級職位時,常被問及的主題之一就是軟件架構的規模化。並沒有多少公司能夠像 Netflix、Amazon、Shopify、GitHub 等那樣規模化運營軟件。如果你從來沒有做過類似規模的項目,你又該如何準備回答這樣的問題呢?

儘管我在架構、部署和支持龐大的生產系統方面並沒有太多的個人經驗,但這篇博文是我試圖闡述我是如何通過這類面試的經歷。

補充:如果你知道你面試的公司的用戶數不到 100 萬,而且增長速度相當穩,在未來 1~3 年內不太可能出現“曲棍球棒效應”(譯註:指數據在長時間的平穩後急劇上升或下降)的增長,如果他們在面試中問我很多關於“規模化”的問題,我就會認爲這是一個危險信號。對我來說,這表明該公司的招聘決定是不成熟的,對規模的優化也是不成熟的,他們可能會將工程資源消耗在錯誤的優先級上,而此時他們需要的是通過特徵集開發來投資於市場差異化。規模化不是重頭戲。反正現在還不是。

我偶爾會引用一個真實面試問題,這個問題來自一家我不願透露名稱的公司。曾有一次,面試官問我:“如果你現在要打造 Twitter 的話,你將會如何構建它?圍繞一個日常活躍用戶高達數億的平臺的構建和擴展,你認爲主要的挑戰是什麼?

關於我的目標受衆

我想指出的是,本文並不是針對初級面試者而寫的。我假設閱讀本文的是這樣的讀者:擁有幾年的專業全棧或後端開發經驗,且沒有涉及任何大型產品的工作經驗,有理論知識但缺乏實踐經驗,現在想在一家大公司謀求更有挑戰性的職位,而這家公司的主打產品有 100 多萬用戶。這就是很可能會被問到這類問題的面試場景。

理想情況下,如果不是應聘架構師或領導職位的話,你至少要謀求一個高級工程師的職位。出於這一原因,本文有一些高級術語我沒有給出定義——我假設閱讀本文的讀者以前遇到過像數據庫複製這樣的概念。

如果你已經開始了你的旅程,我建議你邁出以下幾步:

1. 想一想你要如何存儲數據

我們應該選擇哪個數據庫?

我經常問自己的第一個問題是:我們將主要使用什麼數據庫來存儲用戶數據?我相信,在 2020 年,除非你有非常非常非常令人信服的理由,否則,正確答案應該是 MySQL 或 PostgresQL。當然,對於 Twitter 的克隆,或者任何本質上是“大規模 CRUD”的應用程序來說,都是如此。兩者之間一些細微的差別,也許值得提前研究一下。

譯註:CRUD,指 Create, read, update and delete,即數據庫操作創建、讀取、更新和刪除。

這裏的原因是,MuSQL 和 PostgresQL 在巨大的生產規模上都有着數十億小時的實戰經驗,這是許多以前走過這條道路的公司所採用的,它們都是爲了快速存儲和處理大量數據而設計的。幾乎所有的開發人員都知道如何與 SQL 數據庫交互,使用任意形狀的文檔存儲、數據湖、NoSQL 和其他類型的數據庫都有充分的理由,但是對於主要用戶數據存儲,我會選擇更合適的方式。

在數據運行規模化時,我們還需要考慮哪些?

當你開始擁有幾萬個活躍用戶,他們每天產生數百萬行數據的時候,你最終需要一個數據分區策略。從某種程度上說,將整個數據庫存儲在一臺主機上是不可行的。你必須以某種邏輯的方式將數據進行分塊,這樣多個主機就可以同步,並確保讀寫請求被轉發到正確的地方。順便說一下,這就是數據庫分片(database sharding)。

數據庫分片

我通常以百科全書的例子作爲分片的真實例子。百科全書並不是作爲一本鉅著出版的:你不可能在物理上把一本書裝訂成那麼大的書,這與數據吞吐量和存儲存在物理上的限制何其相似!在開發團隊中,你可能也不會真的將一張表分塊(至少在很長時間內不會這麼做)。MySQL 和 PostgresQL 表可以存儲數百萬行(也許可以存儲幾十億?)。更實際的做法是,你將表劃分爲邏輯組,並將每個組放在不同的主機上。那要如何定義這些組呢?這完全取決於團隊。有些團隊喜歡按領域來組織它們:如果你的代碼庫中有明確的領域,比如社區模式和企業模式,那麼它們相關聯的表可能位於不同的主機上。還需要考慮如何跨這些組進行連接:SQL 連接已經很昂貴了,要是頻繁地跨模式域進行連接的話可能會……嗯,如果操作不當,會造成生產停機。

數據複製

你還需要考慮數據複製策略。這需要對你的預期用戶羣有一定的瞭解。如果你正在構建 Twitter,可以肯定的是,你的用戶分佈在全球範圍內,可能會高度集中在北美和歐洲,所以你也許會考慮在這些地區的數據中心中保留副本。複製數據和保持數據同步都是有成本的,因此,要將它作爲一個投資決策來對待,就像本文中的其他決策一樣。

一致性和高可用性的權衡

我認爲在面試中無需解釋 RAFT、Paxos 或其他共識算法,但是,如果對一些常見的分佈式讀寫策略有所瞭解的話,在數據一致性要求的方面,你將能夠做出更爲全面的回答。例如,如果你在銀行面試,數據完整性就非常重要,你可能希望使用事務來阻止競爭性的寫操作。否則,就會有人可以從 200 美元的餘額中提取 100 美元三次!

對於像社交媒體平臺這樣的東西,如果用戶在發推後幾秒鐘內沒有看到 Chrissy Teigen 的最新推文,那也不是什麼太重要的事,但網上通常是在線的,這是非常重要的,所以我們優先考慮的是可用性而不是一致性,讓“最終一致性”來處理大部分用戶數據。

這裏的關鍵在於,理解這個領域,並弄清楚業務需求到底是哪些,然後再將自己鎖定在技術決策上,而這些技術決策對以後的遷移來說成本很昂貴。

租用託管數據庫,還是自己託管?

就我個人而言,我還沒有被問及過這個問題,我懷疑你也不會被明確要求回答這一問題,除非你要面試的職位涉及到解決方案架構師,或者公司有商業理由需要對數據存儲位置保持警惕(如銀行、律師事務所、或者醫療數據存儲業務等)。但對於一些大型公司來說,如 Facebook、Amazon,運營自己的數據中心是有意義的。重要的是,這些公司也有高度專業化的員工來管理這些數據中心。因爲對於大多數公司來說,這樣的人員配置在現實中既不真實,也不可行。所以,通常情況下,請別人來擔任你的數據庫管理員是具有商業意義的。那個“別人”很可能就是 AWS,或者是 Google Cloud、Azure 等,如果它們有合適產品的話。

2. 在較高層次上理解分佈式系統中的故障場景

如果面試要求你在白板上繪製方框和線條,那麼很有可能是讓你表示出服務以及兩者之間的通信模式。對於關鍵的服務到服務通信路徑,人們通常使用消息隊列,如 RabbitMQ。而對於跨域消息發送,人們通常會使用發佈/訂閱(Pub-Sub)消息傳遞,如 Kafaka。我認爲,你選擇的具體技術並不比你想使用它們的原因更重要,至少在面試中是這樣。所有這些工具都是解決方案,要想明智地談論解決方案,你需要了解它們解決了什麼問題。

還有,爲什麼是分佈式系統?每個系統都是分佈式系統,而且在很長一段時間內都是如此。

在工作中,你要做的決定不是“解決這個問題的最佳工具是什麼?”,而是“什麼是最好的工具,是經過安全審計批准的、財務部簽字認可的、我們的供應商提供的、我的團隊知道如何使用或能快速學習的、能解決這個問題的最佳工具?”——而這個具體的答案是不可知的,直到你深入瞭解。

當我在 Pivotal 工作時,我做的最有價值的一件事就是,花了一些時間與支持工程師合作,以便更多地瞭解龐大的、雜亂無序的分佈式系統在實踐中失敗的所有方式(即使 UML 圖是原始的)。如果這是你目前可以做的事情,我強烈建議你這樣做:要求到客戶支持部門進行爲期一週的輪崗,見證客戶在使用你所構建的系統時的各種困難。這不僅是一個很好的培養同理心的練習,會讓你在任何高級職位上都更有效率;它還是一種非常安全的獲得經驗的方式,可以讓你目睹系統是如何失敗的。一些公司有專門的 SRE 團隊,他們隨身攜帶傳呼機,如果可以跟蹤某人一個星期,當他們醒來時你也跟着醒來,實時觀察事件是如何發展的,並參與事後評估,這也是一種安全的學習方式。

事情怎麼會出錯了呢?

我從事 SRE 的朋友會告訴你,沒有兩個事件是完全相同的。特別是如果他們正確地完成了他們的工作:隨着時間的推移,產生警報的事件應該越來越奇怪,而且很難用自動化來補救。(這意味着他們正在有效地構建自動化修復工具。)

但是,我仍然認爲,爲了給面試官留下深刻印象,有幾種類型的失敗場景你可以大致談一談:

  • 內存不足、停止垃圾回收和其他應用程序級故障。見鬼,如果你正在編寫 JavaScript API 的話,甚至“undefined is not a function”這種錯誤也算。
  • 配置更改錯誤。
  • 外部攻擊(尤其是由惡意或無知的第三方發起的 DDoS 攻擊)。
  • 合法使用量的意外激增,看起來像是 DDoS 攻擊。
  • 基於集羣的一致性問題。
    • 幾年前我見過一個臭名昭著的問題:RabbitMQ 節點會被網絡分區導致離線,然後嘗試重新加入它們的舊集羣,但是節點加入的順序對於建立法定人數(Quorum)非常重要。當然,它們試圖以錯誤的順序重新加入,這就導致了一個無限循環的恢復嘗試。

還有很多!如果你想以一種愉快的方式閱讀有關實際故障的信息,我建議你看一下 Incident Labs 出的《事後回顧》(Post-Incident Review)雜誌:https://zine.incidentlabs.io/

3. 表明你可以在性能和可維護性之間進行權衡

拋開胡裏花哨的白板圖不談,我認爲有一件事可以令你脫穎而出,那就是讓面試官知道你清楚軟件系統的長期成功,是完全依賴於構建和維護它們的人。我認爲這個討論有兩個層次,大致可以歸納爲“性能(速度)與可維護性”:涉及你日常代碼行的“底層”討論;以及“500 英尺以上”層次的討論,它更具有前瞻性,可能更符合高級工程師所期望的操作高度。這裏的基本假設是,大型組織對構建快速可靠的系統感興趣,並且他們希望僱傭懂得如何實現這一點的人。

你在系統中添加的每一個組件,都會增加認知複雜性。如果你在使用一個工具的方式稍微有點偏離常規,則在認知複雜性的債務上再加一分。如果要複製或分區,或者使用非主流的負載均衡策略,那就多加一點。以此類推。重點是,有很多設計選擇,可以提高系統的整體穩定性、速度,或者兩者兼而有之,但對於那些必須學習它們、在它們內部或周圍構建、支持它們的人們來說,它們帶來了實實在在的成本,也許有一天會棄用它們。別忘了團隊是會發生變化的,人們會離開或加入組織,而你今天的決定會在很大程度影響新員工入職曲線的陡峭程度。這就是這次談話的“可維護性”部分。

還有一點:如果面試我的人對我用另一個 RabbitMQ 進行網絡調用而無動於衷,並且,也沒有讓我來闡述選擇每一個設計的理由,那麼我想知道這家公司對可維護性的要求該有多苛刻。我想知道那些隨時待命的開發人員做得怎麼樣,是否需要給他們一個擁抱鼓勵安慰下。

“靠近底層”的級別

我每天大概會做出 5~10 個這樣的決定。這些關於性能的小決定通常是用幾行代碼表示的,將來相對容易改變:我只需打開一個新的 PR,或者,如果可能會產生級聯影響的話,可以編寫一個 RFC 並獲得一些反饋,然後進行更改。從瞭解問題、提出解決方案到實施解決方案之間的時間大約是幾天或幾周,也可能是幾小時或幾分鐘。

例如,如果我想出一個優化 SQL 查找的好方法,也許是通過添加一些額外的索引,添加一個子查詢,也許是進行一些巧妙的分頁,那麼我就可以節省幾毫秒的響應時間。但是,如果我的代碼包含了一個 Bug,當它在生產環境中出現時,而我卻不在待命狀態,那該怎麼辦?不僅要知道如何使代碼更快,還要質疑聰明代碼的可維護性,並找到一些認知上的債務緩解策略(比如:額外的單元測試,包含命名良好的變量和清晰的描述,額外的跟蹤問題中的文檔,描述性的提交信息等等),這纔是高級職位的基線。我還希望強大的中級開發人員能夠合理地做出這些決策。

“500 英尺以上”的級別

同樣的原則也適用於系統級變更中的更大的決策或投資。但是,與可以刪除的 SQL 查詢不同,選擇添加新組件,或者遷移到新框架、語言或託管平臺,以後更改起來就要困難得多。我已經提到過,你不能在沒有正常理由的情況下就輕率地將 RabbitMQ 扔到架構圖的中間。作爲這個理由的一部分,有必要討論一下你將達到的抽象層規模,這個抽象層在本質上是系統級的。

要繼續使用這個 RabbitMQ 示例,你還可以進一步討論從業務角度來看,爲什麼圍繞消息發送引入更強的保證是有意義的。你是否需要審覈通過 Web 表單發送的請求的能力?是否因爲下游某處的已知穩定性的問題而重試失敗的發送?我認爲,表現出對業務需求的深入理解(如果沒有,那就問問題,直到你確實理解爲止),當以後更改成本較高時,他們可以相信你能夠始終如一地做出慎重的、明智地選擇。

4. 瞭解系統可見性和故障排除的基本支持

這已經比我原本打算要寫的內容多了幾千字,因此,我將簡單地講講如何監控系統。系統健康是一個非常全面的概念,特別是當你將“系統”的定義擴展到包括人類時,因此對於一個假設的系統,要求你當場構建,是很難制定出一套標準的指導方針來說明應該監控哪些方面。但是有幾件事人們通常還是會說的。

首先,你的核心業務價值存在於你的應用程序中,因此,從監控可能會拖垮你的應用程序的事情開始:過多的內存使用、過多的 CPU 使用、非 200 狀態碼問題、數據庫響應過慢、應用程序級錯誤率。《SRE 手冊》中的“監控”一章很好地解釋了要跟蹤什麼,以及爲什麼要跟蹤。

其次,我會發表一個簡短的演講,說明如果開發人員在儀表板上的一條線越過圖中可怕的紅色部分時不能立即知道該怎麼做時,那麼監控任何東西都是沒有用處的。所有的警報,無論是在工作時間內還是在下班時間,都需要具有可操作性,並且該操作需要立即顯而易見,最好是作爲警報通知的消息正文的一部分。而這通常需要一些培訓,在面試時,你不一定非要自願去做。有專門的諮詢公司可以進行這種培訓。

關於 SLO 的熱議話題

如果擱在一兩年前,你被問到 SLO 和 SLA 之類的問題,我會說:“我們應該爭取儘可能多的 9。”但現在,我的感覺已經變了:你的正常運行時間要求應與產品經理、設計室和支持團隊密切合作來確定,因爲他們可能對客戶的需求有更深入的瞭解。即使在一個公司內部,SLO 在團隊之間也可能有所不同:並非所有團隊都在關鍵路徑上工作。後退一步也是可以的,我認爲通過承認正常運行時間要求是由客戶滿意度決定的,而不是反過來說,稍微迴避一下 SLO 的問題,可能會給人留下深刻的印象。

無論如何,我希望本文對你有用。我主要是爲《我,但在 2018 年》(me, but in 2018)寫的。我已經盡力將我希望當時知道的單詞都包括在內了。希望這能幫助你找到正確的方向,開始在 Google 上進行搜索,並更深入地挖掘一些概念。我希望你會發現,像我一樣,系統設計和學習分佈式系統,可以是一個真正有趣、富有挑戰性的追求。

作者介紹:

Denise Yu,華裔,女性軟件工程師,現居加拿大多倫多,專注於 GitHub 社區與安全。女權主義者,愛貓人士,喜歡塗鴉和創作漫畫作品,有自己的繪畫工作室。

原文鏈接:

https://deniseyu.io/2020/05/21/talking-about-software-at-scale.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章