緩存是新的內存
英文原文:Cache is the new RAM
這是一次在 defrag 2014的演講。
這是經過長時間地多次技術變革后的(多個)技術優勢之一。你看到了實際上突破。如果你只是看到了其中的一部分,很難正確推斷。你要么短期有進展,要么落后很遠。令人驚訝的不是事物變化的速度,而是一點一滴長期工程實踐的突破。這是史端喬交換機,一個自動連接電話線路設備,在1891年發明的。
1951年,正是轉向數字交換技術之時,一個典型的集中式交換中心基本上還是維多利亞時期的技術的放大版。每個轉接過來的電話都有自己單獨的strowger交換機。
當時來看,這已經是最牛逼的技術。當然我們看來,這只不過是當時世界上最大的蒸汽朋克(Steampunk,背景設在19世紀的科幻小說)風格的裝置藝術(art installations)。
對此感到優越感可能是不對的。雖然集成電路(integrated circuit)已經面世65年了,仍然有數億計的這種設備嗡嗡咔咔地運行著。直到現在,我們才真正地處在完全電子計算(solid-state computing, solid-state與機械相對,指基于半導體的)的轉折點。
最令人興奮的技術轉變,一個是新的模型成為可行,另一個是舊的限制不再存在。在我們的工業界,這兩種類型的轉變都在上演。
分布式計算(distributed computing)現在是貫穿整個軟件棧的主導性的編程模型。所謂的中央處理單元(central processing unit)不再是中心化的,甚至都不是一個單元了。它僅僅算是數據之山(a mountain of data)上爬行的一群蟲子(Bugs)中的一個。數據庫是最后的堡壘。
同時,內存與硬盤存儲間的延遲正在變得無關緊要。30年來,數據庫性能的主要關心點,在于訪問內存與硬盤存儲上的隨機數據的巨大差別。既然現在我們可以把數據全部放在內存中,這些煩惱統統不用考慮了。當然不是這么簡單,你不能用一個B樹,mmap一下,然后就能搞定。在完全基于內存的設計方案推出之前還有很多相關的東西要解決。
這兩種新趨勢產生了完全嶄新的方式來思考、設計、構建應用。現在我們來談下我們怎么達到,我們怎么做的,未來給我們的啟示。
(史前時代,從下文看應該是2000年前,用戶被描述成恐龍,作者的小幽默)
那時候,架構圖里的每個組件都有一個確定的描述與之相關。每個組件都是一個單獨的功能:數據庫、web服務器,都成為一屋之劇中的不同角色(一屋指的是機房或data center)。 順帶提一下,這就是“the cloud”這個詞的來源。一個輕軟/毛茸茸的云是WAN的標準符號,而WAN的細節我們完全不用操心。
(2000年,負載均衡解決一切)
容易實現的分布式計算得到了主流的親睞。多個完全相同的應用程序服務器藏在一個負載均衡器(load balancer)之后,這個均衡器把負載差不多平均地分配到應用程序服務器上去。只負載均衡那些架構中狀態無關的部分回避了很多哲學上的問題(理論上的情況?)。當系統擴展時,這些組件從側翼包抄,最后包圍了“the” database。我們告訴自己,給數據庫換上更快的磁盤、更快的CPU很正常,畢竟還是只需要一臺機器。硬件提供商很高興地賺著我們的錢。
(2002:備份解決一切)
最后,數據庫備份成為合情合理的,加了一個熱備份數據庫(hot spare database)后,我們的良心得到些許寬慰。然后我們告訴自己,不會再有任何故障了。當然,這種正確性只存在了幾分鐘。
當然,熱備份經常是空閑的(sitting idle)。一旦商業分析員意識到,他們可以在不觸及生產數據的情況下,也能對生產數據進行大規模查詢,那么所謂的熱備份也幾乎跟生產數據一樣開始忙碌并且至關重要了。我們又告訴自己,在緊急情況下把熱備份暫時拿出來也還好。但這就如同說,我們完全沒必要帶備胎,因為我們可以從其他車上偷一個過來!
(2004:memcached/緩存解決一切)
然后,Brad Fitzpatrick發布了memcached,一個可以在內存中緩存數據的守護程序(因此叫memcached, memory cached)。這是個簡化版的分布式哈希表,而且確實非常實用,因而之后就在學術界流行起來。它擁有很多特性:備份(a form of replication?),水平分區,負載均衡,簡單數學運算等。 我們再次告訴自己,既然大部分的負載都是讀,我們為什么還要催促數據庫從磁盤一遍一遍做相同的查詢?你只需要一組內存很大的小規模(small-calibre,小口徑)服務器,當然硬件提供商也高興地賺我們(買內存)的錢。
也許需要你寫些緩存失效(cache invalidation)代碼,這聽起來不難,對吧。
(2004:memcached解決一切,添加了緩存失效)
確如其聲稱,memcached的方案使我們受益很長時間。它把硬盤的隨機IO操作替換為內存的隨機IO操作。盡管如此,那臺數據庫機器還是越來越大,越來越忙。我們意識到,緩存的內存開銷至少會跟工作集一樣大(不然就是無效了),再加上讓人不能忍的緩存持久化。我們告訴自己,這就是網絡級規模(web scale?)的開銷。
(2006:水平分區/切分解決一切)
更令人擔心的是應用越來越復雜,越來越聊天化(chattier,可能聊天程序對數據庫寫的次數很多)。幾乎每次都會進行多次數據庫寫操作。現在,寫,而不是讀,成為了瓶頸。這時我們才最終認真對待數據庫切分。Facebook最初是根據university字段來切分其用戶數據,然后做成了"哈佛數據庫(The Harvard Database)",并且維持了很長一段時間。Flickr是另一個好例子。他們使用PHP手動建立了一個切分系統,這個系統使用用戶ID的哈希值來切分數據庫,跟memcached根據key來切分很像。在技術交流會上,他們透露,不得不對數據表去規范化(denormalize),以及對一些對象(比如評論、消息、喜歡)進行兩次寫(doule-write)。
要解決無限伸縮(infinite scaling)總要付出點代價,對吧。
(2008:NoSQL解決一切)
手動切分關系型數據庫的問題是,你的關系型數據庫已經沒了。切分API實際上成為你的查詢語言了。你對操作的頭疼還沒好,而修改一組模式(schema)更加痛苦。
這就需要大家深呼一口氣,列出大家選用的SQL實現的所有不足和瑕疵,然后因此責怪SQL。一波潮人似的NoSQL,難民似的XML數據庫出現了,并且都作出了根本辦不到的承諾。它們提供了自動切分,靈活的模式,一些冗余,...,一開始也就這么多。但是總比自己寫要好多了。
你知道,“不用自己寫”成為主要賣點的東西總是令人絕望。
(2010:Map/Reduce解決一切)
轉移到NoSQL并不比使用手動切分差,因為我們已經放棄了使用常用的客戶端工具控制和分析數據的希望。但這沒好多少。之前由商業人員(business folks)編寫的SQL查詢變成了開發人員維護的報表代碼。
還記得用于備份和分析的熱備份數據庫吧?現在它變身為Hadoop filestores以及上層的Hive查詢而卷土重來了。既然奏效,商業人員再也不來煩我們了。但一個大問題是,這些系統的操作復雜性。就像航天飛機一樣,它們是作為可靠且幾乎不用維護的產品出售的,但是最后還是需要大量的手動操作。另一個大問題是,數據的存入和取出:花費一整天的時間已經相當不錯了。第三個大問題是IO同時成為網絡和磁盤的瓶頸。我們告訴自己,這就是從大數據(big data)畢業的代價。
不管怎樣,Google就是這樣做的,對吧。
(2012:NoSQL再次解決一切)
隨著一些NoSQL數據庫的逐漸成熟,它們的API發生了詭異的變化:它們開始長得像SQL一樣。這是因為SQL是關系型集合理論(relational set theory)的相當直接的實現,而數學不是那么好愚弄的。
我重述下Paul Graham對Lisp那難以忍受、并自鳴得意的評論:一旦你添加了group by, filter, join,你也不能聲稱發明了新的查詢語言,因為這僅僅算是SQL的一個新方言。而且語法很差,還沒有優化器。
由于我們繞過了SQL,大部分系統都缺少了一些很重要的東西,比如存儲引擎、查詢優化器,而這些都是基于關系型集合理論設計的。拖延到后期去實現導致了嚴重的性能問題。即使對解決了性能問題的那些(或者通過停駐在內存中來掩蓋此問題),也缺少了其他東西,如合適的備份。
我知道一個非常成功的互聯網初創公司(你肯定也聽過)使用了4個(!!)不同的NoSQL系統來解決問題。
(2014:現在需要什么來解決一切?)
現在已經相當明顯,我們不會回到單數據庫以及10毫秒一次的隨機定位(10-million-nanosecond random seek,上文幻燈有提到,讀一次硬盤要10毫秒)的那個從前了。在尋找一勞永逸解決所有問題的炒作周期(hype cycle, 也叫技術成熟度曲線)的過程中,有個有趣的模式:聰明的方法在減輕一個痛點的同時會引入新的痛點。
所以下一個添到這張圖上的復雜工具是什么呢?也許真正的方法是能簡化事情的。
例如內存:在數據庫機器上有很多內存,用做緩沖和計算;Memcached機器上也有很多內存。這些系統中的內存總和至少跟你的工作數據集一樣大。如果不是,你就賺到了(under-bought,低階買到好貨)。而且,我非常懷疑你的緩存層是否100%高效。我打賭你有大量數據在被替換掉之前沒有被讀取過,我還打賭你從來沒跟蹤過。這不意味著你是個壞孩子,而意味著緩存比起其所值,更是個麻煩。
這些組件共有的很多特性看起來,是可以相互組合,并且互補的。只要它們被安排得合理。
一旦你采用下面的公理:系統應該是分布式的,而數據應該是數字化的(solid-state是純電氣的,而不是mechanical機械式的),有意思的事情出現了:模型更簡單了。在查詢觸發時才會用到的臨時內存數據結構是僅有的結構。隨機訪問不再是大罪,而是商業的正常過程。你不必擔心分頁,或者再均衡(rebalancing),或者數據的位置。
(2014:SQL內存集群解決一切)
這是個優美、簡單的架構。就像負載均衡器抽象了應用程序服務器,SQL聚合器(aggregators)抽象了讀寫的組織細節。把數據存放策略的核心放在穩定的API之下,可以在少量中斷的情況下允許兩邊變化。
現在,一切都好了,我們最終到達了歷史最后的美好之地,對吧?
不管你在何時,對計算藝術狀態的自滿都是錯誤的。總會有其他瓶頸。
這是AMD的Barcelona芯片,相當現代化的設計。它有4個核,但是大部分表面都被緩存和核心(core)周圍的I/O區域占據,就像WalMart周圍的大型停車場一樣。奔騰時代,緩存區域只占晶圓(die)的15%。第三次計算領域的革命在于,CPU相對于內存快了多少。因此晶圓上大片昂貴的區域都為緩存保留著。
過去,數據庫性能的主要關注點在內存和硬盤的延遲,現在我們打趣CPU和內存的延遲不是同樣的問題,但是它確實是。
而且我們裝作共享內存是存在的,但卻不是。有那么多核心和內存,總會有些核心離部分內存很近。
當你仔細想下,計算機確實只做了兩件事:讀符號,寫符號。性能是個計算機有多少數據要移動,數據要到哪里去的功能問題。最好的可能情形是大量的順序數據流被讀取一次,很快地處理后,就不再被用到。GPU是個很好的例子。但是最有意思的負載不是這樣的。
(吞吐量和延遲總會笑到最后)
每個隨機指針都會保證緩存一次不中,每個對同一塊內存區域(比如寫鎖)的競爭都會引起大量的協調延遲。即使你的CPU緩存命中率達到99%(事實上不可能),等待內存的時間也會是主導性的。
或者這樣說吧:如果磁盤是新的磁帶,內存就是新的硬盤,CPU緩存是新的內存。位置仍然有關系。
所以,什么會解決這個問題?看起來這就是那個相同的古老的矛盾:我們優化隨機訪問,還是優化串行?我們接納寫,還是讀的性能問題?還是我們干坐著等硬件速度跟上來?也許記憶電阻器(memristor)或者其他技術會使這些問題無關緊要。當然,我也需要些錢(pony, 小馬?)。
好消息是分布式數據庫的總體物理架構基本成型。數據客戶端不再需要處理4或5個不同的子系統的內部細節。這還不完美,也不是主流。但是突破總歸需要一段時間來傳播。
但如果瓶頸還是在存儲位置,這意味著其他部分都成熟了。創新可能在數據結構和算法領域發生。也很少會有清理架構的改動,來承諾一次解決全部問題。如果我們幸運,接下來的15年,SQL數據庫會慢慢變得更快更高效,而API是相同的。
但是在此之前,工業界將不會平靜。