系統設計:如何設計Youtube?
Youtube 是世界上最受歡迎的影片分享網站之一。該服務的使用者可以上傳、檢視、分享、評價和報告影片以及新增對影片的評論。
系統的要求和目標
為了這個練習,我們計劃設計一個更簡單的 Youtube 版本,具有以下要求:
功能要求:
- 使用者應該能夠上傳影片。
- 使用者應該能夠分享和觀看影片。
- 使用者可以根據影片標題進行搜尋。
- 我們的服務應該能夠記錄影片的統計資料,例如喜歡/不喜歡、總觀看次數等。
- 使用者應該能夠新增和檢視影片評論。
非功能性要求:
- 該系統應該是高度可靠的,任何上傳的影片都不會丟失。
- 系統應該是高度可用的。一致性可能會受到影響(為了可用性),如果使用者有一段時間沒有看到影片,那應該沒問題。
- 使用者在觀看影片時應該有實時體驗,並且不應該感到任何延遲。
不在範圍內:影片推薦、最受歡迎的影片、頻道和訂閱、稍後觀看、收藏等。
容量估計和約束
假設我們有 15 億總使用者,其中 8 億是每日活躍使用者。如果平均而言,使用者每天觀看五個影片,則每秒總影片觀看次數為:
800M * 5 / 86400 sec => 46K videos/sec
假設我們的上傳:觀看比率為 1:200,即對於每個影片上傳,我們有 200 個影片觀看,我們每秒上傳 230 個影片。
500 hours * 60 min * 50MB => 1500 GB/min (25 GB/sec)
儲存估算:假設每分鐘有 500 小時的影片上傳到 Youtube。如果平均而言,一分鐘的影片需要 50MB 的儲存空間(影片需要以多種格式儲存),則一分鐘內上傳的影片所需的總儲存空間為:
500 hours * 60 min * 50MB => 1500 GB/min (25 GB/sec)
這些數字是估計的,忽略影片壓縮和複製,這將改變我們的估計。
頻寬估計:每分鐘上傳 500 小時的影片,假設每個影片上傳需要 10MB/分鐘的頻寬,我們每分鐘將獲得 300GB 的上傳量。
500 hours * 60 mins * 10MB => 300GB/min (5GB/sec)
假設上傳:觀看比率為 1:200,我們將需要 1TB/s 的傳出頻寬。
系統API
我們可以使用 SOAP 或 REST API 來公開我們服務的功能。以下可能是用於上傳和搜尋影片的 API 的定義:
uploadVideo(api_dev_key, video_title, vide_description, tags[], category_id, default_language, recording_details, video_contents) Parameters: api_dev_key (string): The API developer key of a registered account. This will be used to, among other things, throttle users based on their allocated quota. video_title (string): Title of the video. vide_description (string): Optional description of the video. tags (string[]): Optional tags for the video. category_id (string): Category of the video, e.g., Film, Song, People, etc. default_language (string): For example English, Mandarin, Hindi, etc. recording_details (string): Location where the video was recorded. video_contents (stream): Video to be uploaded. Returns: (string) 一個成功的上傳將返回HTTP 202(請求被接受),一旦影片編碼完成。 一旦影片編碼完成,就會透過電子郵件通知使用者,並提供一個訪問影片的連結。我們還可以公開一個可查詢的API,讓使用者知道他們上傳影片的當前狀態。 讓使用者知道他們上傳影片的當前狀態。 searchVideo(api_dev_key, search_query, user_location, maximum_videos_to_return, page_token) Parameters: api_dev_key (string): The API developer key of a registered account of our service. search_query (string): A string containing the search terms. user_location (string): Optional location of the user performing the search. maximum_videos_to_return (number): Maximum number of results returned in one request. page_token (string): This token will specify a page in the result set that should be returned. Returns: (JSON) 一個包含符合搜尋查詢的影片資源列表資訊的JSON。 每個影片資源將有一個影片標題、一個縮圖、一個影片建立日期和它的瀏覽次數。 |
高階設計
在高層次上,我們需要以下元件:
處理佇列:
- 每個上傳的影片都會被推送到一個處理佇列中,稍後從佇列中取出以進行編碼、縮圖生成和儲存。
- 編碼器: 將每個上傳的影片編碼為多種格式。
- 縮圖生成器: 我們需要為每個影片製作一些縮圖。
- 影片和縮圖儲存: 我們需要將影片和縮圖檔案儲存在一些分散式檔案儲存中。
- 使用者資料庫: 我們需要一些資料庫來儲存使用者的資訊,例如姓名、電子郵件、地址等。
- 影片後設資料儲存: 後設資料資料庫將儲存有關影片的所有資訊,如標題、系統中的檔案路徑、上傳使用者、總觀看次數、喜歡、不喜歡等。此外,它還將用於儲存所有影片評論。
資料庫模式
影片後設資料儲存 - MySql影片後設資料可以儲存在 SQL 資料庫中。每個影片都應儲存以下資訊:
- 影片ID
- 標題
- 描述
- 尺寸
- 縮圖
- 上傳者/使用者
- 總贊數
- 不喜歡的總數
- 總觀看次數
對於每條影片評論,我們需要儲存以下資訊:
- 評論ID
- 影片ID
- 使用者身份
- 評論
- 創作時間
使用者資料儲存——MySql
- 使用者 ID、姓名、電子郵件、地址、年齡、註冊詳細資訊等。
詳細的元件設計
該服務的讀取量很大,因此我們將專注於構建一個可以快速檢索影片的系統。我們可以預期我們的讀寫比率為 200:1,這意味著每個影片上傳有 200 個影片觀看。
影片將儲存在哪裡?影片可以儲存在HDFS或GlusterFS等分散式檔案儲存系統中。
我們應該如何有效地管理讀取流量?我們應該將讀取流量與寫入流量分開。由於我們將擁有每個影片的多個副本,因此我們可以將讀取流量分佈在不同的伺服器上。對於後設資料,我們可以有主從配置,其中寫入將首先傳送到主伺服器,然後在所有從伺服器上重放。這樣的配置可能會導致資料過時,例如當新增一個新影片時,它的後設資料將首先插入到主伺服器中,並且在它被從伺服器重放之前,我們的從伺服器將無法看到它,因此將返回過時的結果給使用者。這種陳舊性在我們的系統中可能是可以接受的,因為它的壽命很短,使用者將能夠在幾毫秒後看到新影片。
縮圖將儲存在哪裡?縮圖將比影片多得多。如果我們假設每個影片都有五個縮圖,那麼我們需要一個非常高效的儲存系統來服務於巨大的讀取流量。在決定將哪個儲存系統用於縮圖之前,有兩個考慮因素:
- 縮圖是小檔案,每個最大 5KB。
- 與影片相比,縮圖的閱讀流量將是巨大的。使用者將一次觀看一個影片,但他們可能正在檢視一個包含 20 個其他影片縮圖的頁面。
讓我們評估將所有縮圖儲存在磁碟上。鑑於我們有大量檔案;要讀取這些檔案,我們必須對磁碟上的不同位置執行大量搜尋。這是非常低效的,並且會導致更高的延遲。
Bigtable在這裡可能是一個合理的選擇,因為它將多個檔案組合成一個塊儲存在磁碟上,並且在讀取少量資料時非常有效。這兩者都是我們服務的兩個最大要求。將熱縮圖儲存在快取中也將有助於改善延遲,並且鑑於縮圖檔案的大小很小,我們可以輕鬆地將大量此類檔案快取在記憶體中。
影片上傳:由於影片可能很大,如果在上傳時連線斷開,我們應該支援從同一點恢復。
影片編碼:將新上傳的影片儲存在伺服器上,並在處理佇列中新增一個新任務,將影片編碼為多種格式。一旦所有編碼完成;上傳者收到通知,影片可供檢視/共享。
後設資料分片
由於我們每天都有大量的新影片,並且我們的讀取負載也非常高,因此我們需要將資料分佈到多臺機器上,以便我們可以高效地執行讀/寫操作。我們有很多選擇來分片我們的資料。讓我們逐一介紹對這些資料進行分片的不同策略:
基於 UserID 的分片:我們可以嘗試將特定使用者的所有資料儲存在一臺伺服器上。在儲存時,我們可以將 UserID 傳遞給我們的雜湊函式,該函式會將使用者對映到資料庫伺服器,我們將在其中儲存該使用者影片的所有後設資料。在查詢使用者的影片時,我們可以要求我們的雜湊函式找到儲存使用者資料的伺服器,然後從那裡讀取它。要按標題搜尋影片,我們必須查詢所有伺服器,每個伺服器都會返回一組影片。然後,集中式伺服器將彙總這些結果並對其進行排名,然後再將它們返回給使用者。
這種方法有幾個問題:
- 如果使用者變得流行怎麼辦?持有該使用者的伺服器上可能會有很多查詢,從而造成效能瓶頸。這將影響我們服務的整體表現。
- 隨著時間的推移,與其他使用者相比,一些使用者最終可能會儲存大量影片。保持不斷增長的使用者資料的均勻分佈是相當困難的。
為了從這些情況中恢復,我們必須重新分割槽/重新分配我們的資料,或者使用一致的雜湊來平衡伺服器之間的負載。
基於 VideoID 的分片:我們的雜湊函式會將每個 VideoID 對映到一個隨機伺服器,我們將在其中儲存該影片的後設資料。要查詢使用者的影片,我們將查詢所有伺服器,每個伺服器將返回一組影片。集中式伺服器將在將這些結果返回給使用者之前對其進行彙總和排名。這種方法解決了我們的熱門使用者問題,但將其轉移到了熱門影片。
我們可以透過在資料庫伺服器前引入快取來儲存熱門影片來進一步提高我們的效能。
影片去重
擁有大量使用者,上傳大量影片資料,我們的服務將不得不處理廣泛的影片複製。重複的影片通常在寬高比或編碼方面有所不同,可能包含疊加層或額外的邊框,或者可能是較長的原始影片的摘錄。重複影片的泛濫可能會在多個層面產生影響:
- 資料儲存:我們可能會透過保留同一影片的多個副本來浪費儲存空間。
- 快取:重複的影片會佔用可用於獨特內容的空間,從而導致快取效率下降。
- 網路使用:增加必須透過網路傳送到網路內快取系統的資料量。
- 能源消耗:更高的儲存、低效的快取和網路使用會導致能源浪費。
對於終端使用者而言,這些低效率將以重複搜尋結果、更長的影片啟動時間和流式傳輸中斷的形式實現。
對於我們的服務,重複資料刪除在使用者上傳影片的早期最有意義;與對它進行後期處理以稍後查詢重複影片相比。內聯重複資料刪除將為我們節省大量可用於編碼、傳輸和儲存影片副本的資源。一旦任何使用者開始上傳影片,我們的服務就可以執行影片匹配演算法(例如,塊匹配、相位相關等)來查詢重複項。如果我們已經有正在上傳的影片的副本,我們可以停止上傳並使用現有的副本,或者使用新上傳的影片(如果它的質量更高)。如果新上傳的影片是現有影片的子部分,反之亦然,我們可以智慧地將影片分成更小的塊,這樣我們只上傳那些丟失的部分。
負載均衡
我們應該在快取伺服器之間使用一致性雜湊,這也有助於平衡快取伺服器之間的負載。由於我們將使用基於靜態雜湊的方案將影片對映到主機名,因此由於每個影片的受歡迎程度不同,可能會導致邏輯副本上的負載不均。例如,如果一個影片變得流行,則與該影片對應的邏輯副本將比其他伺服器經歷更多的流量。然後,邏輯副本的這些不均勻負載可以轉化為相應物理伺服器上的不均勻負載分佈。要解決此問題,一個位置的任何繁忙伺服器都可以將客戶端重定向到同一快取位置中不太繁忙的伺服器。對於這種情況,我們可以使用動態 HTTP 重定向。
然而,使用重定向也有它的缺點。首先,由於我們的服務嘗試在本地進行負載平衡,如果接收重定向的主機無法提供影片,則會導致多次重定向。此外,每次重定向都需要客戶端發出額外的 HTTP 請求;在影片開始播放之前,它還會導致更高的延遲。此外,層間(或跨資料中心)重定向會將客戶端引導到較遠的快取位置,因為較高層快取僅存在於少數位置。
快取
為了服務全球分佈的使用者,我們的服務需要一個大規模的影片傳輸系統。我們的服務應該使用大量地理分佈的影片快取伺服器將其內容推向更靠近使用者的地方。我們需要有一個策略來最大化使用者效能,並在其快取伺服器上平均分配負載。
我們可以為後設資料伺服器引入快取來快取熱資料庫行。在訪問資料庫之前使用 Memcache 快取資料和應用程式伺服器可以快速檢查快取是否有所需的行。最近最少使用(LRU)可能是我們系統的合理快取驅逐策略。根據此政策,我們首先丟棄最近最少檢視的行。
我們如何構建更智慧的快取?如果我們遵循 80-20 規則,即 20% 的影片每日閱讀量產生 80% 的流量,這意味著某些影片非常受歡迎,以至於大多數人觀看它們;因此,我們可以嘗試快取 20% 的每日影片和後設資料讀取量。
內容交付網路 (CDN)
CDN 是一個分散式伺服器系統,它根據使用者的地理位置、網頁的來源和內容交付伺服器向使用者交付 Web 內容。看看我們快取章節中的“CDN”部分。
我們的服務可以將最受歡迎的影片移至 CDN:
CDN 在多個位置複製內容。影片更有可能更接近使用者並且跳數更少,影片將從更友好的網路流式傳輸。
CDN 機器大量使用快取,並且主要可以提供記憶體不足的影片。
不被 CDN 快取的不太受歡迎的影片(每天 1-20 次觀看)可以由我們在各個資料中心的伺服器提供。
容錯
我們應該使用一致性雜湊在資料庫伺服器之間進行分配。一致的雜湊不僅有助於替換死伺服器,還有助於在伺服器之間分配負載。
點選標題
相關文章
- 系統設計:設計Spotify
- 如何設計一個微博系統?- 4招教你搞定系統設計
- 計數系統設計
- 如何設計一個RPC系統?RPC
- 如何設計一個 RPC 系統RPC
- 如何設計短網址系統?
- 系統設計面試參考-設計Spotify系統面試
- 【系統設計】設計一個限流元件元件
- 系統程式設計程式設計
- 如何構建設計語言系統
- 如何進行系統分析與設計
- 千萬級DAU系統該如何設計
- 如何設計一個秒殺系統?
- 如何設計一個秒殺系統
- [系統設計]秒殺系統
- 系統架構設計之-任務排程系統的設計架構
- (Python程式設計 | 系統程式設計 | 並行系統工具 | 程式退出)Python程式設計並行
- 系統設計:如何設計一個分散式作業排程器 ?- Rakshesh分散式
- 優惠券系統應該如何設計?
- 支付系統訂單模型該如何設計?模型
- 面試必考:秒殺系統如何設計?面試
- 如何設計一個搶紅包系統
- 系統設計與普通設計思考的區別
- 系統設計中法則
- 系統冪等設計
- 結算系統設計
- 秒殺系統設計
- 系統設計原則
- 票據系統設計
- UI設計培訓之UI設計系統知識UI
- 自由經濟系統的設計(二):生態設計
- 遊戲分享系統設計第二步:分享系統的設計遊戲
- 微機原理與系統設計筆記6 | 儲存器系統設計筆記
- 《暗黑3》是如何重新設計技能系統的?
- 揭秘!閒魚拉新投放系統如何設計
- 億級系統的Redis快取如何設計???Redis快取
- 促銷系統的設計
- 分散式系統程式設計分散式程式設計