原文地址html
這篇文章,採用 Markdown 方式,寫的仍是比較實在的,要是有架構圖就行了。算法
Pinterest 是圖片版的 Twitter,用戶把本身感興趣的東西用圖釘(Pins)釘在釘板(PinBoard)上,採用 Pinterest 瀑布流的形式展示圖片內容,用戶無需翻頁,新圖片不斷地自動加載到頁面底端,讓用戶發現新的圖片。截至2013年9月,該軟件已進入全球最熱門社交網站前十名。在移動互聯網時代,網民在移動設備上更喜歡看圖片。Pinterest、Snapchat、Instagram 等圖片社交平臺受到用戶熱捧,目前市場估值也明顯高於其餘「文本」社交網絡。sql
Pinterest 正經歷了指數級的增加,每個半月就翻番。在這兩年裏,Pinterest,從 每個月PV量0增加到100億,從兩名創始人和一個工程師成長爲四十個工程師,從只有一臺MySQL 服務器,發展到180臺Web 服務器(Web Engine)、240臺接口服務器(API Engine)、88 臺 MySQL 數據庫 (AWS cc2.8xlarge, a Cluster Compute 8XL) ,而且每臺DB有一個備份服務器,110臺Redis 實例服務(Redis Instance)、200臺 Memcache 實例服務(Memcache Instance)。數據庫
這種增加的確是使人歎爲觀止。咱們請來了 Pinterest 的兩位創始人Yashwanth Nelapati 和 Marty Weiner,他們將以 Scaling Pinterest 爲題講述 Pinterest 架構充滿戲劇化的傳奇故事。他們說,若是能在一年半前飛速發展時,能看到有人作相似題材的演講的話,他們就會有更多的選擇,以避免本身在這一年半里作出不少錯誤的決定。後端
這是一個很不錯的演講,充滿了使人驚訝的細節。同時這個演講也很務實,歸根結底,它帶來了可以讓你們選擇的策略。緩存
這篇演講中有兩個我最爲看重的經驗:安全
- 強大的架構在處理增加時經過簡單增長相同的東西(服務器)來應對,同時還能保證系統的正確性。當遇到性能問題時,經過砸錢,簡單增長服務器來擴容。若是你的架構可以作到這一點,那它就如金子般強大而珍貴!
- 當性能快到極限時,大多數技術其實都會失敗,以它們本身的方式。這使得他們在審覈工具時要考慮如下特性:成熟且簡單,用得人多,良好的支持,持續的優異性能,不多失敗,開源。按照這樣的標準,他們選擇了:MySQL, Solr、Memcache 和 Redis,放棄了 Cassandra 、Mongo。
這兩點經驗是相互聯繫的。遵循(2)中提到的能夠在擴容時簡單增長服務器便可。當負載增長了,成熟的產品會更少有問題。當你遇到問題時,至少但願可以在社區中解決。當你使用的工具過於技巧化和講究時,會發現,你遇到一堵沒法逾越的牆。服務器
在這段演講裏,碎片化(sharding)優於集羣(clusterting)的觀點是我認爲最好的一部分。所以,他們選擇以 sharding 方式增加,而不是 clustering。關於他們爲何選擇 sharding 和如何作 sharding 是頗有趣的事,這極可能觸及到你之前未考慮過的場景。網絡
如今,讓咱們看看 Pinterest 如何擴容。數據結構
啓動於2010年3月——自我發現時期
此時此刻,你甚至不知道你在作的這個產品將要作什麼。你有想法,迭代開發更新產品的頻率很高。
早期的一些數字:
- 2個創始人
- 1個工程師
- Rackspace(全球三大雲計算中心之一,是一家全球領先的託管服務器及雲計算提供商,產品包括專用服務器,電子郵件,SharePoint,雲服務器,雲存儲,雲網站等。在服務架構上提供專用託管,公有云,私有云及混合雲)
- 1個小型 Web 引擎
- 1個小型 MySQL DB
2011年1月
仍在潛伏前進中,產品獲得了一些用戶反饋。此時架構圖:
- Amazon EC2 + S3 + CloudFront 雲服務
- 1臺 Nginx,4臺 Web 引擎(做爲冗餘,不是爲了負載)
- 1臺 MySQL DB + 1臺讀 Slave MySQL 服務器(防止 Master 服務器宕機)
- 1個任務隊列 + 2個任務處理
- 1臺 MongoDB(爲了統計計數)
- 2個工程師
至2011年9月——嘗試階段
每個半月翻翻的瘋狂增加階段——毫無理性的增加。
- 當快速發展時,全部的一切都會在每晚每星期崩潰。
- 此時,你閱讀大量白皮書,它們告訴你添加服務器,你也這麼作了。所以,他們(Yashwanth Nelapati 和 Marty Weiner)開始添加不少技術。毫無例外都失敗了。
- 結果,你獲得一個極爲複雜的架構圖。
- Amazon EC2 + S3 + CloudFront
- 2 Nginx、16 Web Engines + 2 API Engines
- 5 Functionally Sharged MySQL DB + 9 Slaves
- 4 Cassandra 節點
- 15 Membase 節點(分紅三個單獨的集羣)
- 8 Memcache 節點
- 10 Redis 節點
- 3 任務路由(Task Routers)+ 4 Task Processors
- 4 Elastic Search 節點
- 3 Mongo 集羣
- 3 工程師
- 5種主要的數據庫技術只爲了應付他們本身的數據。
- 增加極快,以致MySQL負載很高,而其餘全部技術都快到達極限了。
- 當你把某個技術推至極限時,它們會以本身的方式崩潰。
- 他們開始放棄一些技術,並問本身他們到底須要什麼。因而,大規模重構架構。
2012年1月——成熟階段
- 從新設計架構後,系統就變成了下面的樣子。
- Amazon EC2 + S3 + Akamai, ELB
- 90 Web Engines + 50 API Engines
- 66 MySQL DBs(m1.xlarge(AWS 提供 8 CPU,32G 內存)) + 1 slave each
- 59 Redis Instances
- 51 Memcache Instances
- 1 Redis Task Manager + 25 Task Processors
- Sharded Solr
- 6 Engineers
- 使用 Mysql、Redis、Memcache 和 Solr,它們的優點是,至關簡單且成熟的技術。
- Web 流量以某個速度保持增長,而且 iPhone的流量開始增長。
2012年10月12日——穩按期
一月份後,大概就有4倍的增加。
- 系統架構數據以下:
- Amazon EC2 + S3 + Edge Cast,Akamai, Level 3
- 180 Web Engines + 240 API Engines
- 88 MySQL DBs (cc2.8xlarge) + 1 slave each
- 110 Redis Instances
- 200 Memcache Instances
- 4 Redis Task Manager + 80 Task Processors
- Sharded Solr
- 40 Engineers (and growing)
- 注意,此時的架構纔是合理的。增加是經過增長更多的服務器。出問題時,你可以 經過購買服務器來進行擴展。
- 如今,遷移到 SSDs。
爲何是 Amazon EC2/S3
- 可靠性至關好。數據中心也會宕機, 多租戶(Multitenancy)會增長一些風險,但還不算太壞。
- 良好的報告和支持。他們具備至關好的架構,並知道問題出在哪裏。
- 良好的額外服務支持,特別是當你的應用處於增加期時。你不用本身去實現,就能得到緩存、負載均衡、映射化簡、數據庫和其餘全部方面。Amazon 服務特別適合起步階段,以後你能夠招聘工程師來優化。
- 分分鐘得到新的服務實例。這是雲服務的威力。特別是當你只有兩名工程師,不用擔憂容量規劃,或爲了獲得 memcache 而等上兩週時間:添加 10 臺 memcache 服務器也就是幾分鐘的事。
- 反對的理由:有限的選擇。直到最近,你能夠獲得SSD,但不能得到大容量的RAM配置。
- 同意的理由:仍是有限的選擇。你不須要面對一大堆配置迥異的服務器。
爲何是 MySQL?
- 至關成熟。
- 很是耐用。不會宕機,也不會丟失數據。
- 招聘方便。大堆工程師懂 MySQL。
- 響應時間隨請求數量線性增加。當請求數量飆升時,有些技術不能很好的響應。
- 很好的軟件支持——XtraBackup、Innotop、Maatkit。
- 很好的社區,問題總能輕易獲得答案。
- 很好的廠商支持,像 Percona。
- 開源免費。這一點很重要,特別是你剛開始沒有不少資金支持時。
爲何選擇 Memcache?
- 很是成熟。
- 至關簡單。就是一個 socket 的哈希表。
- 保持良好性能。
- 不少人知道並喜歡。
- 從不崩潰。
- 開源免費。
爲何選擇 Redis?
- 還不成熟,但很是好且至關簡單。
- 提供了各類的數據結構。
- 持久化和複製,而且能夠選擇如何實現它們。你能夠用MySQL風格持久化,或者不要持久化,或者只要3小時的持久化。
- 不少人知道並喜歡。
- 保持良好性能。
- 不多故障。你須要瞭解一些小故障,學習並解決它們,使它愈來愈成熟。
- 開源免費。
注意,Memcache 和 Redis 的區別和適應場景。
Solr
- 只須要幾分鐘的安裝時間,就能夠投入使用。
- 不能擴展(最新版本並不是如此)。
- 嘗試彈性搜索,可是以Pinterest的規模來講,可能會由於零碎文件和查詢太多而產生問題。
- 如今使用 Websolr,可是 Pinterest 擁有搜索團隊,未來可能會開發本身的版本。
Solr 是一個高性能,採用Java開發,基於Lucene的全文搜索服務器。同時對其進行了擴展,提供了比Lucene更爲豐富的查詢語言,同時實現了可配置、可擴展並對查詢性能進行了優化,而且提供了一個完善的功能管理界面,是一款很是優秀的全文搜索引擎。
集羣 vs. 分片
- 在迅速擴展的過程當中,Pinterest 認識到每次負載的增長,都須要均勻的傳播他們的數據。
- 針對問題先肯定解決方案的範圍,他們選擇的範圍是集羣和分片之間的一系列解決方案。
集羣 —— 全部事情都是自動化的
- 示例:Cassandra、MemBase、HBase
- 結論: 太可怕了,不是在如今,可能在未來,但如今太複雜了,有很是多的故障點。
- 屬性:
- 自動化數據分佈。
- 數據可移動。
- 可從新進行分佈均衡。
- 節點間可相互通信。大量的通訊。
- 優勢:
- 自動伸縮數據存儲,至少白皮書上是這麼說的。
- 安裝簡單。
- 在空間中分佈存儲你的數據,可在不一樣區域有數據中心。
- 高可用性。
- 負載均衡。
- 沒有單點故障。
- 缺點(來自第一手的經驗):
- 仍是至關年輕不成熟。
- 仍是太複雜,一大堆節點必須對稱的協議,這是一個在生產環境中難以解決的問題。
- 不多的社區支持,有一個沿着不一樣產品線的分裂社區會減小每一個陣營的支持。
- 不多工程師有相關的知識,多是不少工程師都沒用過 Cassandra。
- 複雜和和可怕的升級機制
- 集羣管理算法是一個 SPOF 單點故障,若是有個 bug 影響每一個節點,這可能會宕機 4 次。
- 集羣管理器編碼複雜,有以下一些失敗的模式:
- 數據從新均衡中斷:當一個新機器加入而後數據開始複製,它被卡住了。你作什麼工做?沒有工具來找出到底發生了什麼。沒有社會的幫助,因此他們被困。他們又回到了MySQL。
- 全部節點的數據損壞. What if there’s a bug that sprays badness into the write log across all of them and compaction or some other mechanism stops? Your read latencies increase. All your data is screwed and the data is gone.
- 均衡不當並且很難修復. 很是常見,若是你有10個節點,你會注意到全部節點都在一個節點上,有一個手工處理方式,但會將全部負載分佈到一個單節點上
- 權威數據失效. 集羣方案是很智能的。In one case they bring in a new secondary. At about 80% the secondary says it’s primary and the primary goes to secondary and you’ve lost 20% of the data. Losing 20% of the data is worse than losing all of it because you don’t know what you’ve lost.
分片(sharding)——全憑人手
- 分片是贏家。我以爲他們分片的方案與Flicker架構很是類似。
- 特色:
- 若是去掉集羣方式下全部很差的特色,就獲得了分片。
- 人工對數據進行分佈。
- 不移動數據。
- 經過切分數據來分擔負荷。
- 節點不知道其它節點的存在。某些主節點控制一切。
- 優勢:
- 能夠經過切分數據庫來擴大容量。
- 在空間上分佈數據。
- 高可用。
- 負載均衡。
- 放置數據的算法十分簡單。這是最主要的緣由。雖然存在單點(SPOF),但只是很小的一段代碼,而不是複雜到爆的集羣管理器。過了第一天就知道有沒有問題。
- ID的生成很簡單。
- 缺點:
- 沒法執行大多數鏈接。
- 沒有事務功能。可能會出現寫入某個數據庫失敗、而寫入其它庫成功的狀況。
- 許多約束只能轉移到應用層實現。
- schema的修改須要更多的規劃。
- 若是要出報表,必須在全部分片上分別執行查詢,而後本身把結果合起來。
- 鏈接只能轉移到應用層實現。
- 應用必須應付以上全部的問題。
什麼時候分片?
- 當有幾TB的數據時,應該儘快分片。
- 當Pin錶行數達到幾十億,索引超出內存容量,被交換到磁盤時。
- 他們選出一個最大的表,放入單獨的數據庫。
- 單個數據庫耗盡了空間。
- 而後,只能分片。
分片過渡
- 過渡從一個特性的凍結開始。
- 確認分片該達到什麼樣的效果——但願盡少的執行查詢以及最少數量的數據庫去呈現一個頁面。
- 剔除全部的MySQL join,將要作join的表格加載到一個單獨的分片去作查詢。
- 添加大量的緩存,基本上每一個查詢都須要被緩存。
- 這個步驟看起來像:
- 1 DB + Foreign Keys + Joins
- 1 DB + Denormalized + Cache
- 1 DB + Read Slaves + Cache
- Several functionally sharded DBs+Read Slaves+Cache
- ID sharded DBs + Backup slaves + cache
- 早期的只讀奴節點一直都存在問題,由於存在slave lag。讀任務分配給了奴節點,然而主節點並無作任何的備份記錄,這樣就像一條記錄丟失。以後Pinterest使用緩存解決了這個問題。
- Pinterest擁有後臺腳本,數據庫使用它來作備份。檢查完整性約束、引用。
- 用戶表並不進行分片。Pinterest只是使用了一個大型的數據庫,並在電子郵件和用戶名上作了相關的一致性約束。若是插入重複用戶,會返回失敗。而後他們對分片的數據庫作大量的寫操做。
如何分片?
- 能夠參考Cassandra的ring模型、Membase以及Twitter的Gizzard。
- 節點間數據傳輸的越少,你的架構越穩定。
- Cassandra存在數據平衡和全部權問題,由於節點們不知道哪一個節點保存了另外一部分數據。Pinterest認爲應用程序須要決定數據該分配到哪一個節點,那麼將永遠不會存在問題。
- 預計5年內的增加,而且對其進行預分片思考。
- 初期能夠創建一些虛擬分片。8個物理服務器,每一個512DB。全部的數據庫都裝滿表格。
- 爲了高有效性,他們一直都運行着多主節點冗餘模式。每一個主節點都會分配給一個不一樣的可用性區域。在故障時,該主節點上的任務會分配給其它的主節點,而且從新部署一個主節點用以代替。
- 當數據庫上的負載加劇時:
- 先着眼節點的任務交付速度,能夠清楚是否有問題發生,好比:新特性,緩存等帶來的問題。
- 若是屬於單純的負載增長,Pinterest會分割數據庫,並告訴應用程序該在何處尋找新的節點。
- 在分割數據庫以前,Pinterest會給這些主節點加入一些奴節點。而後置換應用程序代碼以匹配新的數據庫,在過渡的幾分鐘以內,數據會同時寫入到新舊節點,過渡結束後將切斷節點之間的通道。
ID 結構
- 64位
- 分片ID 爲16位
- Type爲10位—— Board、User或者其它對象類型
- 本地ID——餘下的位數用於表中ID,使用MySQL自動遞增。
- Twitter使用一個映射表來爲物理主機映射ID,這將須要備份;鑑於Pinterest使用AWS和MySQL查詢,這個過程大約須要3毫秒。Pinterest並無讓這個額外的中間層參與工做,而是將位置信息構建在ID裏。
- 用戶被隨機分配在分片中間。
- 每一個用戶的全部數據(pin、board等)都存放在同一個分片中,這將帶來巨大的好處,避免了跨分片的查詢能夠顯著的增長查詢速度。
- 每一個board都與用戶並列,這樣board能夠經過一個數據庫處理。
- 分片ID足夠65536個分片使用,可是開始Pinterest只使用了4096個,這容許他們輕易的進行橫向擴展。一旦用戶數據庫被填滿,他們只須要增長額外的分片,而後讓新用戶寫入新的分片就能夠了。
查找
- 舉個例子,若是存在50個查找,他們將ID分割且並行的運行查詢,那麼延時將達到最低。
- 每一個應用程序都有一個配置文件,它將給物理主機映射一個分片範圍。
- 「sharddb001a」: : (1, 512)
- 「sharddb001b」: : (513, 1024)——主要備份主節點
- 若是你想查找一個ID坐落在sharddb003a上的用戶:
- 將ID進行分解
- 在分片映射中執行查找
- 鏈接分片,在數據庫中搜尋類型。並使用本地ID去尋找這個用戶,而後返回序列化數據。
對象和映射
- 全部數據都是對象(pin、board、user、comment)或者映射(用戶由borad,pin有like)。
- 針對對象,每一個本地ID都映射成MySQL Blob。開始時Blob使用的是JSON格式,以後會給轉換成序列化的Thrift。
- 對於映射來講,這裏有一個映射表。你能夠爲用戶讀取board,ID包含了是時間戳,這樣就能夠體現事件的順序。
- 一樣還存在反向映射,多表對多表,用於查詢有哪些用戶喜歡某個pin這樣的操做。
- 模式的命名方案是:noun_verb_noun: user_likes_pins, pins_like_user。
- 只能使用主鍵或者是索引查找(沒有join)。
- 數據不會向集羣中那樣跨數據的移動,舉個例子:若是某個用戶坐落在20分片上,全部他數據都會並列存儲,永遠不會移動。64位ID包含了分片ID,因此它不可能被移動。你能夠移動物理數據到另外一個數據庫,可是它仍然與相同分片關聯。
- 全部的表都存放在分片上,沒有特殊的分片,固然用於檢測用戶名衝突的巨型表除外。
- 不須要改變模式,一個新的索引須要一個新的表。
- 由於鍵對應的值是blob,因此你不須要破壞模式就能夠添加字段。由於blob有不一樣的版本,因此應用程序將檢測它的版本號而且將新記錄轉換成相應的格式,而後寫入。全部的數據不須要馬上的作格式改變,能夠在讀的時候進行更新。
- 巨大的勝利,由於改變表格須要在上面加幾個小時甚至是幾天的鎖。若是你須要一個新的索引,你只須要創建一張新的表格,並填入內容;在不須要的時候,丟棄就好。
呈現一個用戶我的資料頁面
- 從URL中取得用戶名,而後到單獨的巨型數據庫中查詢用戶的ID。
- 獲取用戶ID,並進行拆分
- 選擇分片,並進入
- SELECT body from users WHERE id = <local_user_id>
- SELECT board_id FROM user_has_boards WHERE user_id=<user_id>
- SELECT body FROM boards WHERE id IN (<boards_ids>)
- SELECT pin_id FROM board_has_pins WHERE board_id=<board_id>
- SELECT body FROM pins WHERE id IN (pin_ids)
- 全部調用都在緩存中進行(Memcache或者Redis),因此在實踐中並無太多鏈接數據庫的後端操做。
開發
-
Initially tried to give developers a slice of the system. Each having their own MySQL server, etc, but things changed so fast this didn’t work.
-
Went to Facebook’s model where everyone has access to everything. So you have to be really careful.
腳本化
- 當你過渡到一個分片架構,你擁有兩個不一樣的基礎設施——沒有進行分片的舊系統和進行分片的新系統。腳本成爲了新舊系統之間數據傳輸的橋樑。
- 移動5億的pin、16億的follower行等。
- 不要輕視項目中的這一部分,Pinterest原認爲只須要2個月就能夠完成數據的安置,然而他們足足花了4至5個月時間,別忘了期間他們還凍結了一項特性。
- 應用程序必須同時對兩個系統插入數據。
- 一旦確認全部的數據都在新系統中就位,就能夠適當的增長負載來測試新後端。
- 創建一個腳本農場,僱傭更多的工程師去加速任務的完成。讓他們作這些表格的轉移工做。
- 設計一個Pyres副本,一個到GitHub Resque隊列的Python的接口,這個隊列創建在Redis之上。支持優先級和重試,使用Pyres取代Celery和RabbitMQ更是讓他們受益良多。
- 處理中會產生大量的錯誤,用戶可能會發現相似丟失board的錯誤;必須重複的運行任務,以保證在數據的處理過程當中不會出現暫時性的錯誤。
將來方向
- 基於服務的架構。
- 當他們開始看到了不少的數據庫負載,便像產卵同樣,致使了不少的應用服務器和其餘服務器堆在一塊兒。全部這些服務器鏈接到MySQL和Memcache。這意味着有30K上的memcache鏈接了一對夫婦演出的RAM引發的memcache守護進程交換。
- 做爲一個修補程序,這些都是移動的服務架構。有一個跟隨服務,例如,將只回答跟隨查詢。此隔離的機器數目至30訪問數據庫和高速緩存,從而減小了鏈接。
- 幫助隔離功能。幫助組織,解決和支持這些服務的團隊。幫助開發人員,爲了安全開發人員不能訪問其餘服務。
獲得教訓
- 爲了應對將來的問題,讓其保持簡單。
- 讓其變的有趣。只要應用程序還在使用,就會有不少的工程師加入,過於複雜的系統將會讓工做失去樂趣。讓架構保持簡單就是大的勝利,新的工程師從入職的第一週起就能夠對項目有所貢獻。
- 當你把事物用至極限時,這些技術都會以各自不一樣的方式發生故障。
- 若是你的架構應對增加所帶來的問題時,只須要簡單的投入更多的主機,那麼你的架構含金量十足。
- 集羣管理算法自己就用於處理SPOF,若是存在漏洞的話可能就會影響到每一個節點。
- 爲了快速的增加,你須要爲每次負載增長的數據進行均勻分配。
- 在節點間傳輸的數據越少,你的架構越穩定。這也是他們棄集羣而選擇分片的緣由
- 一個面向服務的架構規則。拆分功能,能夠幫助減小鏈接、組織團隊、組織支持以及提高安全性。
- 搞明白本身究竟須要什麼。爲了匹配願景,不要怕丟棄某些技術,甚至是整個系統的重構。
- 不要懼怕丟失一點數據。將用戶數據放入內存,按期的進行持久化。失去的只是幾個小時的數據,可是換來的倒是更簡單、更強健的系統!
更多信息,查看 high scalability。