作者 | Leonardo Creed
翻譯 | 蘇宓
出品 | CSDN(ID:CSDNnews)
在眾多科技巨頭中,相比 Google、微軟、Meta 等公司,蘋果無論是在 iOS 系統(tǒng),還是很多新興技術研發(fā)方面,都保持著一定的神秘感,鮮少有人知道蘋果公司的基礎設施是如何構建出來的?
近日,軟件工程師 Leonardo Creed 在查閱了大量的論文以及整理了多個技術細節(jié)之后,向眾人分享了蘋果是如何構建在線同步存儲服務和云端計算服務 iCloud 的,也揭曉了它如何實現(xiàn)存儲數(shù)十億個數(shù)據庫的。通過本篇文章,也希望給相關的從業(yè)者帶來更多的思考與幫助。
以下為譯文:
永恒的現(xiàn)實世界課程
蘋果在 iCloud 和 CloudKit(他們的云計算后端服務)中使用了 FoundationDB 和 Cassandra。是的,標題沒有錯: 蘋果確實在其極端多租戶架構中存儲了數(shù)十億個數(shù)據庫。
在正式解析其架構之前,我想分享自己發(fā)現(xiàn)的一些亮點,本篇文章以及蘋果公司的許多經驗與我之前寫過的“Meta 無服務器平臺架構”的在某些技術點上有些相似(https://read.engineerscodex.com/p/meta-xfaas-serverless-functions-explained)。
兩家都巧妙地使用了異步處理,使得用戶功能用起來更加流暢。Meta 將其無服務器堆棧用于實現(xiàn)非面向用戶的功能。蘋果對幾乎所有記錄層(Record Layer)的功能都使用異步處理(下文將深入解釋),以便向用戶隱藏延遲。
兩者都知道自己有廣泛的可擴展性需求,因此都大量使用無狀態(tài)架構( stateless architecture)。
兩者都從邏輯上隔離資源,以確保系統(tǒng)可靠性和可用性。
兩者都能簡單處理各種需求。蘋果公司提到了「如何為存儲“小數(shù)據”和“大數(shù)據”分別配置和運行不同的系統(tǒng)」。然而,這樣做會增加操作的復雜性,相反,他們用一個抽象概念來處理所有類型的數(shù)據需求。Meta 也是這樣做的,他們的無服務器平臺為所有類型的功能負載提供一種抽象。
兩者都建立了抽象層,以便讓開發(fā)人員獲得更好的體驗。應用程序開發(fā)人員不必擔心可擴展性需求,這將由分布式系統(tǒng)工程師在更深的堆棧中處理。
了解你的用戶。無論是應用程序開發(fā)團隊還是可觀測性團隊,Meta 和 Apple 提供的每個層、API 和設計決策都是在明確了解特定技術用戶的情況下做出的。
Cassandra 數(shù)據庫
Cassandra 是一種開源的 NoSQL 數(shù)據庫管理系統(tǒng)。它最初由 Facebook 開發(fā),用于支持 Facebook 收件箱搜索功能。有趣的是,在 2021 年,Meta 自己開始用 ZippyDB 取代了大部分 Cassandra 的使用。
iCloud 部分采用了 Cassandra。據一家位于加利福尼亞州圣克拉拉的實時人工智能數(shù)據公司 DataStax(旗下主要產品基于 Apache Cassandra)稱,蘋果運行著世界上最大的 Cassandra 部署之一。
在他們發(fā)布的報告中,透露了蘋果在 Cassandra 方面有:
對此,此前也有不少蘋果前員工透露,iCloud 服務中 Cassandra 所管理的數(shù)據量高達數(shù)億字節(jié)。每臺服務器有多個 Cassandra 節(jié)點,這確保了 iCloud 數(shù)據的可用性接近 100%。
至此,蘋果公司內部團隊仍在積極改進 Cassandra。上個月,蘋果公司的 Cassandra 存儲工程經理 Scott Andreas 在一次會議上就 Cassandra 的未來發(fā)表了演講。在蘋果公司的招聘頁面上,他們也在招聘分布式系統(tǒng)工程師時通常會提到 Cassandra。
然而,CloudKit + Cassandra 的解決方案在實際應用中遇到了兩個可擴展性限制,因此蘋果工程團隊后來引入并采用了 FoundationDB:
FoundationDB 和記錄層解決了這兩個問題。
FoundationDB 數(shù)據庫
蘋果公司對 FoundationDB 的態(tài)度更為公開。他們在 2015 年收購了數(shù)據庫公司 FoundationDB,并在此后發(fā)表了多篇論文,詳細介紹了他們對 FoundationDB 的使用。
FoundationDB 是一種開源、分布式、事務性鍵值存儲。它專為處理大量數(shù)據而設計,并且非常適合讀/寫工作負載和寫入密集型工作負載。它還符合 ACID 標準。
Apple 在 CloudKit(Apple 的云計算后端服務)中廣泛使用 FoundationDB 記錄層。
在 FoundationDB 記錄層的 GitHub 頁面上,該團隊詳細介紹道:
記錄層(Record Layer)是一個 Java API,它在 FoundationDB 的基礎上提供了一個面向記錄的存儲,(非常)大致相當于一個簡單的關系數(shù)據庫,具有以下特點:
結構化類型--記錄以 protobuf(協(xié)議緩沖區(qū))消息的形式定義和存儲。protobuf 最早是由谷歌設計的。
索引--記錄層支持各種不同的索引類型,包括值索引(大多數(shù)數(shù)據庫提供的那種)、排名索引和聚合索引??梢酝ㄟ^ protobuf 選項或編程方式定義索引和主鍵。
復雜類型--支持復雜類型,如列表和嵌套記錄,包括針對此類嵌套結構定義索引的功能。
查詢--記錄層不提供查詢語言,但提供了查詢應用程序接口(API),可對一種或多種記錄類型進行掃描、過濾和排序,還提供了可自動選擇索引的查詢規(guī)劃器。
許多記錄存儲,共享模式--記錄層提供了支持許多離散記錄存儲實例的能力,所有這些實例都有一個共享(和不斷發(fā)展的)模式。例如,每個用戶都可以擁有自己的記錄存儲空間,或許可以在不同的 FDB 集群實例之間進行分片,而不是建立一個單一的數(shù)據庫來存儲所有用戶的數(shù)據。
重量輕--記錄層設計用于大型分布式無狀態(tài)環(huán)境。從打開存儲到首次查詢之間的時間以毫秒為單位。
可擴展性--新的索引類型和自定義索引鍵表達式可以動態(tài)地集成到記錄存儲中。
在 FoundationDB 記錄層論文中,他們寫道:“[FoundationDB 記錄層用于]為服務于數(shù)億用戶的應用程序提供強大的抽象。CloudKit 使用記錄層來托管數(shù)十億個獨立數(shù)據庫,其中許多數(shù)據庫具有共同的模式。”
為什么使用 FoundationDB 記錄層?
FoundationDB、Record Layer 和 CloudKit 的結構如下所示:
FoundationDB 負責所有分布式系統(tǒng)和并發(fā)控制工作。
記錄層充當關系數(shù)據庫,使 FoundationDB 更易于使用。
CloudKit 是最上層,為應用開發(fā)人員提供功能和 API。CloudKit 并不是唯一構建在記錄層之上的東西,頂部還有其他內部構建的層,用于需要結構化存儲的東西,如 JSON 文檔存儲。
記錄層允許蘋果大規(guī)模支持多租戶。
事實上,這樣說有點言過其實。
記錄層用于極端多租戶,其中每個應用程序的每個用戶都能獲得獨立的記錄存儲。這意味著記錄層可托管數(shù)十億個獨立數(shù)據庫,共享數(shù)千個模式。
這樣更好!而且更令人印象深刻。
記錄層之所以能夠處理如此大規(guī)模的多租戶問題,主要得益于兩個基本的架構決策:
1. 該層以無狀態(tài)方式運行,只需添加更多無狀態(tài)實例,即可輕松擴展計算資源。
2. 該層使用記錄存儲抽象來有效管理資源分配和可擴展性。這種抽象代表了邏輯數(shù)據庫的全部內容,包括序列化數(shù)據、索引和運行狀態(tài)。
至此,我們可以粗略地了解一下蘋果公司是如何構建 iCloud 的。
CloudKit 如何使用 FoundationDB 和記錄層?
在 CloudKit 中,應用程序由“邏輯容器”(logical container)表示,該容器遵循定義的模式。該架構概述了實現(xiàn)高效數(shù)據檢索和查詢所需的記錄類型、字段和索引。應用程序將其數(shù)據組織到 CloudKit 中的“區(qū)域”中,這樣就可以對記錄進行邏輯分組,以便有選擇地與客戶端設備同步。
對于每個用戶,CloudKit 都會在 FoundationDB 中指定一個唯一的子空間。在這個子空間中,它會為用戶與之交互的每個應用程序創(chuàng)建一個記錄存儲空間。從本質上講,CloudKit 管理著大量邏輯數(shù)據庫(用戶數(shù)量乘以應用程序數(shù)量),每個數(shù)據庫都包含自己的記錄、索引和元數(shù)據集,總計數(shù)十億個數(shù)據庫。
當 CloudKit 收到來自客戶端設備的請求時,它會通過負載均衡將該請求定向到可用的 CloudKit 服務進程。然后,該進程與特定的記錄層記錄存儲進行交互,以滿足請求。
CloudKit 會將已定義的應用程序模式轉換為記錄層中的元數(shù)據定義,并將其存儲在單獨的元數(shù)據存儲區(qū)中。該元數(shù)據由 CloudKit 特定的系統(tǒng)字段進行擴充,這些字段用于跟蹤記錄的創(chuàng)建、修改時間和記錄存儲的區(qū)域。區(qū)域名稱以主鍵為前綴,以便能夠高效訪問每個區(qū)域內的記錄。除了用戶定義的索引外,CloudKit 還管理用于內部目的的“系統(tǒng)索引”,例如通過保留按類型跟蹤記錄大小的索引來管理存儲配額。
FoundationDB 和記錄層共同為蘋果公司解決了 4 個關鍵問題,這是單獨使用 Cassandra 或單獨使用 FoundationDB 都無法解決的。
問題一:個性化全文搜索
FoundationDB 幫助用戶解決個性化全文搜索問題,以便快速訪問數(shù)據。
蘋果的系統(tǒng)利用 FoundationDB 的排序鍵順序,可以快速搜索文本開頭(前綴匹配),也可以進行更復雜的搜索(如查找相鄰或按特定順序排列的單詞——近似搜索和短語搜索),而無需額外的開銷。
在傳統(tǒng)的搜索系統(tǒng)中,你通常需要在后臺運行額外的進程來保持搜索索引的更新,但蘋果的系統(tǒng)可以實時完成所有工作,這意味著一旦數(shù)據發(fā)生變化,搜索索引就會立即更新,無需額外的步驟。
問題二:高并發(fā)情況
有了 FoundationDB,CloudKit 可以流暢地處理同時發(fā)生的許多更新。
以前,在使用 Cassandra 時,CloudKit 需要依靠一個特殊的索引來跟蹤每個區(qū)域的變化,從而實現(xiàn)跨設備的數(shù)據同步。當設備需要更新其數(shù)據時,它會檢查該索引以查看新內容。但這種系統(tǒng)有一個缺點:當多個更新同時發(fā)生時,可能會造成沖突。
但在 FoundationDB 中,CloudKit 使用了一種特殊的索引,它能準確記錄每次更改的順序,而不會造成沖突。具體做法是為每次更改分配一個唯一的“版本”,當 CloudKit 需要同步時,它會查看這些版本,以確定設備錯過了哪些更新。
然而,當 CloudKit 需要在不同的存儲集群之間移動數(shù)據時(也許是為了更均勻地分配負載),事情就變得棘手了,因為每個集群都有自己的版本號,而這些版本號并不一致。
為了解決這個問題,CloudKit 為每個用戶的數(shù)據提供了一個“移動計數(shù)”(稱為 “化身”),每次將其數(shù)據傳輸?shù)叫碌募簳r,該計數(shù)都會增加。每次記錄更新都包含用戶當前的“化身”編號,確保即使在移動后,CloudKit 仍能通過查看化身編號和版本編號來確定正確的更新順序。
在轉到新系統(tǒng)時,CloudKit 面臨著處理沒有版本號的舊數(shù)據的挑戰(zhàn)。蘋果工程團隊巧妙地克服了這一難題,他們通過使用一個特殊的功能,在新系統(tǒng)之前使用以前的系統(tǒng)對舊更新進行排序。這意味著無需對應用程序進行復雜的更改,也不會留下過時的代碼。該函數(shù)考慮了版本、版本號和舊的更新計數(shù)器值,以保持記錄的正確順序。
問題三:高延遲查詢
FoundationDB 是為高并發(fā)而不是低延遲而設計的。這意味著它可以同時處理大量任務,而不是專注于單個任務的速度。
為了充分利用這種設計,記錄層的許多工作都是“異步”進行的——它將將來要完成的任務排隊,允許同時完成其他工作。這種方法有助于掩蓋這些任務中可能出現(xiàn)的任何延遲。
不過,F(xiàn)oundationDB 用來與其數(shù)據庫通信的工具,是設計為使用單線程聯(lián)網,一次只做一件事。在早期版本中,這種設置造成了系統(tǒng)中的交通堵塞,因為所有的事情都在等待輪到這個網絡線程。記錄層一直使用這種單線程方法,這導致了瓶頸。
為了改善這一問題,蘋果減少了網絡線程的工作量?,F(xiàn)在,復雜的任務看起來速度更快了,因為系統(tǒng)同時在幾個方面與數(shù)據庫合作,而不是形成一個隊列。這樣,延遲或表面上的緩慢就被掩蓋了,因為系統(tǒng)不會等一個任務完成后再開始另一個任務。
問題四:事務沖突
在 FoundationDB 中,如果一個事務正在讀取某些鍵,而另一個事務同時修改了這些鍵,就會導致“事務沖突”。FoundationDB 通過提供對控制讀取或寫入時可能導致沖突的鍵集,來精確管理這些沖突。
一種避免不必要沖突的常用方法是,在鍵的范圍內執(zhí)行一種不會導致沖突的特殊讀取,稱為“快照”讀取。如果這種讀取發(fā)現(xiàn)了重要的鍵,事務將只標記那些可能發(fā)生沖突的特定鍵,而不是整個范圍。這樣可以確保事務只受對其結果有實際影響的變化的影響。
記錄層使用這種策略來有效管理被稱之為跳過列表的數(shù)據結構,這是其排序索引系統(tǒng)的一部分。不過,手動設置這些沖突范圍可能比較麻煩,而且可能導致難以識別的錯誤,尤其是當它們與應用程序的主要邏輯混合在一起時。
因此,建議在 FoundationDB 基礎上構建的系統(tǒng)創(chuàng)建更高級別的工具(如自定義索引)來處理這些模式。這種方法有助于避免將放寬沖突規(guī)則的責任留給每個客戶端應用程序,從而導致錯誤和不一致。
對于 Leonardo Creed 的分享,不少開發(fā)者對此也有了更深度的思考:
亞馬遜就是這么做 Aurora 的。將所有狀態(tài)轉移到對象存儲層,而對象存儲層也是處理過程的末端(經過 lb、前端、后端、數(shù)據庫,最后到磁盤)。無狀態(tài)基本上就是把所有東西都移到后端。
我敢肯定,Google 也在做同樣的事情/已經開始做了。同時,這也使其“易于”橫向擴展:只要你能在對象層面上進行抽象,你就可以擴展你的底層基礎架構,以便只處理“對象”。
也有一位蘋果前員工評論道:
遺憾的是,我在蘋果公司工作時從未參與過這方面的工作(不過我參加過這方面的面試?。?,但幾年前聽到的這件事讓我意識到了一些本該顯而易見的事情:數(shù)據庫和文件系統(tǒng)之間其實并沒有什么區(qū)別。
從根本上說,它們做的是同一件事,只是針對特定問題集進行了優(yōu)化。數(shù)據庫適用于有適當索引的數(shù)據,而文件系統(tǒng)則適用于更為隨意的數(shù)據。
如果你是一個足夠聰明的工程師,你可以用數(shù)據庫來定義文件系統(tǒng),iCloud 就是一個很好的例子。就我個人而言,我利用這些知識使用 Cassandra 為 HLS 流存儲視頻數(shù)據塊。這讓我獲得了很多 Cassandra 的分布式優(yōu)勢,但代價是不得不重新發(fā)明一些文件系統(tǒng)的東西。
參考鏈接:
https://read.engineerscodex.com/p/how-apple-built-icloud-to-store-billions
https://news.ycombinator.com/item?id=39028672
————————————————
版權聲明:本文為CSDN博主「CSDN資訊」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權協(xié)議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/csdnnews/article/details/135687614
該文章在 2024/1/23 17:03:26 編輯過