YouTube高擴展之道

YouTube開始定位是在線婚戀網站,但是最後演化成一個視頻分享網站,現在每天視頻的播放數高達40億。經歷過瘋狂的增長過程,Youtube創始團隊的工程師Mike Solomon學到很多東西,在2012年PyCon上分享了YouTube的工程經驗:Scalability at YouTube(雖然是幾年前的演講,但是很多經驗是不會過時的。)

這個演講不是以架構為中心,沒有講解很多模塊互相連接的框圖。Mike完全可以大講架構,他曾經研發YouTube的Servlet框架、視頻索引、視頻轉碼、全文檢索、CDN等等系統。但是,這個演講中他放眼全局,分享了多年技術實踐中總結的的深厚經驗。

對於我來說,從這個演講中學到的最重要的經驗就是:用極簡單的工具完成大部分工作。在其他公司都在向更複雜的生態系統遷移的時候,YouTube一直保持著簡單的技術棧。他們主要用Python編碼,用MySQL做資料庫,用Apache做web伺服器。在這樣一個龐大的網站中,新功能都是從一個簡單的Python腳本開始,慢慢演化的。

這並不意味著YouTube沒有很酷的技術,他們有,但是他們信奉的哲學,或者說行事方式是實用主義的-讓整個系統順利運轉,而不是緊追新奇的技術概念。下面講講YouTube怎樣演化成世界最大網站之一的。

統計

  • 每天40億次播放
  • 每分鐘上傳60小時的視頻
  • 3500萬+個設備安裝有YouTube客戶端
  • 2010年營收翻倍
  • 視頻個數翻了9個數量級的同時,工程師數量只翻了兩個數量級
  • 100萬行Python代碼

技術棧

  • Python-YouTube大部分代碼都是Python代碼,你每次觀看視頻的時候,都在執行一堆Python腳本。
  • Apache - 你認為Apache已經被淘汰了?其實Apache是非常厲害的技術,可以保持技術棧的簡單化。在YouTube,每個來自外部的請求都是經過Apache的。
  • Linux - Linux的好處在於,你總可以深入系統查看運行狀態,不管你的應用表現如何,你都可以用Linux工具(如strace和tcpdump)來查看它的具體情況。
  • MySQL - 處處都用到。當你觀看一段視頻的時候,很多數據都來自於MySQL,有時用作關係資料庫,有時用於存儲二進位BLOB數據。如何調優,如何組織數據對於MySQL非常重要。
  • Vitess - YouTube發布的一個Go語言項目,作為MySQL的前端代理使用。它負責實時地優化查詢,重寫查詢。當前Vitess通過RPC處理YouTube的每個資料庫請求。
  • ZooKeeper - 分散式的配置伺服器,用於存儲配置信息。非常有趣的技術組件,要用好也不太容易。
  • Wiseguy - CGI Servelet伺服器。
  • Spitfire - 一個模版系統。支持虛擬的語法樹,可以快速實現轉換。
  • 串列化格式 - 所有現存格式都太低效。Pickle很糟糕;ProtoBuf也太慢。最後他們自己實現了BSON的編碼解碼器,比所有你能下載到的串列化方案快10-15倍。

經驗教訓

  • YouTube之道:選擇最簡單最通用的解決方案,以保證應對各種問題的靈活性。一旦一個方案過度貼近需求(over-specified),你就把自己堵在牆角了。不要對系統做出太多限定,當你做出各種限定的時候,系統就會自然地變得更加複雜,讓你沒有退路。這就是可擴展性的關鍵所在,可擴展的系統不會阻礙你的任何改動,是透明的。這不是時髦的辭彙而已,是解決複雜問題的道之所在。
  • 大型系統的設計準則:每個系統都要符合特定需求,要根據你的業務量體裁衣。
  • YouTube沒有非同步,所有操作都是同步的。
  • 不要盲從教條,遵循設計哲學-保持簡潔。如果你做Code Review的時候,發現數千行代碼需要改動,涉及很多代碼文件,那麼系統設計應該有更簡單的方案。你的第一個Demo一定要簡單,然後在此基礎上迭代。
  • 再次強調,解決問題之道用一個詞歸納就是簡潔。找到能處理問題的最簡單的技術。需要解決的問題可能會複雜,但是第一個解決方案一定不會很繁瑣,複雜性會隨時間自然而然地產生。YouTube很多系統都是從一個Python文件開始的,經過很多年的演變,慢慢變成了巨大的生態系統。所有的原型都是Python寫的,並且這樣一個原型可以存活很久不被淘汰。
  • 設計Review需要討論的問題:
    • 第一個解決方案是怎樣的?
    • 如何在此基礎上迭代?
    • 這些數據將來會被如何使用?
  • 擁抱變化。YouTube一開始是一個婚戀網站,如果以此為目標做設計,可能就沒有今天的YouTube了。一定要保持靈活性。
  • YouTube CDN - 一開始完全外包。後來發現成本太高,自己實現。如果你有好的硬體工程師,你也可以做一個很不錯的視頻CDN。製造一個很大的機架,塞滿伺服器,裝好Lighttpd,然後複寫404 handler-找到剛才沒有找到的視頻。這個系統花了兩個禮拜完成,上線第一天就扛住了60G的視頻流量。這再次證明了一點:你可以用很簡單的工具完成很多事。
  • 測量(measure)很重要。Vitess開始用HTTP協議實現介面,儘管是C寫的,但是它非常慢。所以,後來他們直接用python的Socket連接替換了HTTP,整體上節省了8%的CPU。HTTP的報文的封裝其實非常昂貴。

擴展性技巧

這裡沒有什麼新理論,但是你會發現有些核心理念可以應用於很多不同的場景中。

  • 分而治之

    很多擴展性的問題不過是把工作分成小塊而已,這個原則可以應用在系統的各個層次。在web層,你可以有很多完全相同而又相互獨立的web伺服器,可以水平擴容,這是分治。在資料庫層分片(Sharding)的核心,也是分治。你需要儘早考慮的是,怎樣把工作分成小塊並且在各塊之間確保通信,這將會影響今後系統擴展的能力。在這裡,Python語言動態特性的優勢就體現出來了。不管你的API設計得多麼糟糕,你都可以用替換、修改、修飾(stub/modify/decorate)等方法來規避問題。
  • 大致正確即可(可以稍微作弊)

    系統的狀態就是它報告給用戶的狀態。如果用戶不能看出系統的偏差,那它就沒有偏差。舉個例子,如果用戶A發布一個評論,與此同時另外一個用戶B載入了這個頁面,他在300-400毫秒內可能看不到這條評論。用戶B不會在乎,但是用戶A在乎自己的評論已經發布。所以你可以作弊一下,在用戶A的頁面顯示這條評論。你的系統不必保持全局一致的事務性,因為實現這樣的系統非常昂貴,而且毫無必要。因為評論不是財務交易,作弊無傷大雅。
  • 因地制宜

    你了解系統需要的一致性模型嗎?對於評論來說,最終一致性(Eventually Consistent)應該足夠,但是租賃一部影片可不行。租賃涉及到金錢交易,所以我們必須儘力保證一致。可見,不同數據的一致性要求是不一樣的。

  • 抖動(在系統內引入熵)

    在YouTube技術團隊內部,熵是一個常用詞。如果系統沒有抖動,你就會遇到踩踏事件(Thundering Herds)。分散式系統很像氣象系統,在這樣的系統裡面debug就像是天氣預報一樣毫無確定性可言。抖動帶來的隨機性可以避免一些東西在系統中累積。

    以緩存失效為例。對於熱點視頻,我們儘可能的多用緩存。最火的視頻可能緩存24小時,但是如果所有緩存的失效時長相同,那麼所有緩存伺服器將在同一時刻失效,這會造成回源的踩踏事件。

    加入抖動就是讓這些緩存在18-30個小時之間的隨機時間失效。這樣可以避免回源的流量疊加。這樣的技巧在整個系統中隨處可見。分散式系統本身有一種「自同步」的傾向,很多操作會自動對齊(line up), 而這對系統自身是毀滅性的。這種現象很有趣,比如一台機器的磁碟變慢,這時候所有主機都在等待某個請求的返回,機器收到的其它請求就會被同步(對齊)。這種現象在系統里有很多機器、很多事件的時候就會發生。每發生一次,系統的熵就會減少一些,你必須把這些熵加回去才能保證系統健康運行。
  • 作弊(怎樣製造假數據)

    這是很有用的技巧。最快的函數調用是不調用任何函數。如果你有一個單調上升的計數器,比如視頻播放次數或者用戶頁面查看次數,你可以每次訪問都更新計數器。或者,你也可以每隔一段時間更新一次,增加一個隨機數,只要它是從奇數變成偶數,用戶很有可能相信這個數字是真的。這是數據造假的技巧。(原來YouTube數據也會造假......)
  • 模塊化的重要性

    在YouTube中的Python程序,會逐漸被RPC隔離開。代碼結構的優劣往往依賴於程序員的自我約束,所以必須確立良好的代碼規範,但另一方面,如果自我約束的效果不好,總可以用RPC把模塊強制隔離開,來確保介面輸入輸出的清晰、規範。

    不是每個模塊的實現都是完美的。模塊可能存活一個月或者半年,這很難說。如果嚴格定義模塊邊界,你就有很大的靈活性,你可以把模塊替換掉(比如用C重寫),或者乾脆拋棄。

    模塊之間交互的數據需要定義嚴格的數據規範。
  • 效率有時需要讓步於可擴展性

    效率經常要讓步於擴展性。最高效的程序是用C寫,把所有功能塞在一個進程裡面,但是這樣沒法擴展。

    著眼於宏觀,看看你的各個組件是如何分離的。是用RPC比較好還是內聯調用比較好?可能將來這個問題的答案和今天不一樣。

    注重演算法。Python並沒有花力氣優化演算法的實現,和C相比很多演算法很低效,但是方便使用。可以用C來優化核心演算法。

    度量。在Python裡面很多度量是違背直覺的,比如垃圾回收的代價。在YouTube的應用中很大一部分時間是用於串列化的,串列化的調優就非常重要。
  • Python實用建議

    盡量寫無腦的代碼,這樣更容易查找,更容易維護。代碼越高深莫測,就越難搞清楚它是如何工作的。

    不要用很多OO的特性。我們使用了很多namespace, 也用Class來組織數據,但是幾乎不用OO的特性。

    代碼樹應該什麼樣?要簡單、實用、優雅、正交、易於組合。

參考資料

highscalability.com/blo

youtube.com/watch?

highscalability.com/you

slideshare.net/didip/su

https://youtube.googleblog.com


推薦閱讀:

互聯網簡訊-20171111
如何看待Coursera在2016年對旁聽課程者的政策變更?
我心中的互聯網的下一個時代。
5個月內DAU翻了9倍,突破2千萬日活,鬼知道火山小視頻做了些什麼?

TAG:互联网 | 软件开发 | 系统架构 |