Pinterest已經能夠駕馭每2.5個月流量就翻一倍的指數增加,他們實現了在2年內每個月pv從0到10億的結果。從2個創始人加1位工程師到超過40名工程師,從只有1臺MySQL服務器到180臺web服務器、240 API引擎、88個MYSQL DB和1個從庫、110個Redis實例、200個Memcache實例。html
使人吃驚的增加!他們是如何作到的呢?1年半年Yashwanth Nelapati 和 Marty Weiner演講過這個富有戲劇性的架構進化故事,當初爲求快,選擇又多,他們也作了不少錯誤的選擇(接下來是教訓與經驗總結)。mysql
這是一個偉大的演講,很是詳情,但同時很接地氣,架構的策略適合每一個普通的你我,強烈推薦!git
我從演講中主要學到兩條原則:github
1.架構是當業務增加時能夠方便的添加一樣的東西就搞定。若是你認爲擴展架構是隻要扔更多的錢、加更多服務器(Boxs)的話,但若是你的架構能作到,那就是金錢。web
2.當到達極限時全部的技術都會以自已特有的方式失敗,這就引出咱們選型時的原則:成熟的、簡單實用的、廣爲人知和喜歡的、社區支持友好的、性能向來表現優異的、儘量不會出現失效且是免費的。根據這些原則,他們選擇了:MySQL、Solr、Memcache和Redis。Cassandra和MongoDB丟掉了。redis
這兩條原則是互相關聯的。依照原則2工具能夠添加更多空間實現擴展,負載增加時成熟的工具將會有較少的問題,即便有問題也有社區幫助一塊兒解決,若是你選的工具太稀少人用那就你可能就會撞到南牆了。算法
這也中我認爲整個演講中最好的之一,關於分片(sharding)比集羣更好的討論,體現了業務增加時只要添加更多資源、較少失效、成熟、簡單、好的支持等特徵。注意下全部的工具他們都選擇可分片而不是集羣,關於這部分的討論頗有趣可能不少你以前也沒考慮過。sql
好了,是否是火燒眉毛了,Let's go:)mongodb
喜歡的圖片(pins):是一張帶信息圖片,包含了描述、重要性、跟蹤連接等。
數據庫
Pinterest:是圖片SNS,包括關注的人和板塊功能
數據庫Database:包括用戶庫-用戶的板塊和板塊的大頭針;關注和轉貼(repin)關係、認證信息等。
這時你甚至不知要建設一個什麼樣的產品,你有不少想法,但常常很快改變,最後只留下一些很奇怪的MySQL查詢。
一些早期數據:
2個創始人
一個工程師
託管在Rackspace
一臺小的web服務引擎
一臺小的MySQL數據庫
仍在祕密開發,根據一些用戶的反饋進行迭代。一些數據:
Amazon EC2 + S3 + CloudFront
1 NGinX, 4 Web Engines (用來冗餘, 並未在線使用)
1 MySQL DB + 1只讀庫(防止主庫DOWN掉)
1 任務隊列 + 2任務處理器
1 MongoDB (用來作計數器)
2 個工程師
瘋狂的增加,每2個半月翻一倍。
當你達到這個增加速度時,全部東東每一個晚上、每週都會掉鏈子
這時,網上查了不少架構方案都只會說加服務器吧,加就加吧。同時又加了不少其它技術,但所有失效了。
同時你看到你組更復雜的數據:
Amazon EC2 + S3 + CloudFront
2 NGinX, 16 Web 引擎 + 2 API 引擎
5 功能性 分片 MySQL DB + 9 只讀從庫
4 Cassandra 節點
15 Membase 節點 (3 獨立集羣)
8 Memcache 節點
10 Redis 節點
3 任務路由 + 4 任務處理器
4 Elastic Search 節點
3 Mongo 集羣
3 工程師
5個主要數據庫單獨存放本身的數據
因增加太快MySQL也快頂不住了,全部技術都用到了極限
當全部技術用到極限時都以本身的方式失效了
開始擯棄一些技術,問問本身到底咱們要什麼。而後是重構。。。重構。。。
2012年1月 - 成熟期
所有重構後的系統是這樣的:
Amazon EC2 + S3 + Akamai, ELB
90 Web 引擎 + 50 API 引擎
66 MySQL DBs (m1.xlarge) + 每臺 1 從庫
59 Redis 實例
51 Memcache 實例
1 Redis 任務管理器 + 25 任務處理器
分片的 Solr
6 工程師
如今都運行在分片的MySQL、Redis、Memcache和Solr上,就是這樣,好處是技術簡單又成熟。
網站流量以一樣的速度在增加,同是,iPhone的流量也開始增加了。
大概是1月份的4倍,數據是這樣的:
Amazon EC2 + S3 + Edge Cast,Akamai, Level 3
180 Web 引擎 + 240 API 引擎
88 MySQL DBs (cc2.8xlarge) + 每一個配1 從庫
110 Redis 實例
200 Memcache 實例
4 Redis 任務管理器 + 80 任務處理器
分片的 Solr
40 工程師 (還在增加)
注意到沒有,架構很好用,擴展時只須要添加同樣的東西,你想砸更多錢來擴展,仍是僅僅是添加空間容量?
硬盤用SSD了
穩定性好。數據中雖也出過問題,添加了些風險,但總體還不錯;
很好的報告和支持。他們很懂架構知識問題在哪。
很好的外圍支持。當你快速增加時,你能夠快速組織起緩存、負載均衡、map reduce、數據庫等等你想要的,沒必要什麼都本身寫,能夠快速用他們的服務起動起來,當你有工程師再跟自已玩;
新的實例只要幾秒就可啓動起來。這是雲服務的力量。特別是當你只有兩個工程師時,你沒必要小心流量增加計劃而耗費2周時間,10臺緩存服務只要幾分鐘就搞定!
弊端:選擇有限。只到最近纔可用SSD,尚未大內存可配置;
優點:選擇有限。你沒必要終結於不一樣服務器不一樣的配置上
*國內貌似阿里雲是個不錯的選擇(譯者)。
真的很成熟。
很堅固。從沒當過機和丟失過數據。
不少人可招募。
響應時間和請求數是線性增加的。有些技術路線並非這樣增加。
很好的軟件支持。XtraBackup,innotop,maatkit
很好的社區支持。得到問題回答很容易。
得到公司的支持也很容易,如 percona
免費。剛開始時若是沒有投資就很重要。
很成熟。
很簡單。它相似一個帶socket的哈希表。
性能一向很好
廣爲人知且喜歡
不會崩潰
免費
雖還不成熟,但很好用且簡單。
提供了多樣的數據結構。
有持久層和可複製,且能夠選擇如何實現。如你想要像MySQL同樣保存也能夠,若是你不想保存也能夠,若是你想只保存3個小時也能夠。說明:1.Redis的根種子(home seed)是每隔3小時保存一次,並無3小時複製功能,它只是每3時備份一次;2.若是你的服務器(box)保存不下數據了,那它只會備份幾小時。這不是十分可靠,但還算簡單。沒必要把持久化和複製搞複雜了,這只是更簡單和很廉價的架構方法。
廣爲人知和喜歡。
一向性能良好。
不多失效。只有小數幾個微妙的失效須要瞭解一下。這也是它成熟的緣由之一,只要學會了就能夠搞定。
崛起中的偉大產品。安裝只要幾分鐘,你就有一個搜索引擎了。
不能跨服務器擴展(至少目前爲止)。
試過Elastic search搜索引擎,但擴展上有問題,特別是處理小文檔上,並且查詢太多。
如今用的是Websolr,Pinterest有個搜索團隊在支撐。
隨着業務的快速增加他們意識到須要把數據分散在不一樣的區域保障將來的增加壓力可控;
關於集羣好仍是分片好有一大堆討論
集羣 - 全部數據都是原子態的
選型結果:太可怕了,可能未來會用,但不是如今,他們太複雜,並且太多失效的狀況。
特性:數據自動分佈式;數據可移動;從新均衡可分佈負載;各節點可互相通信,不少交叉通信、協調。
優劣:
自動擴展數據庫,至少白皮收是這樣說的。
容易安裝。
地理分佈數據,你的數據中心能夠在不一樣地區。
高可用性。
負載均衡。
無單點失效。
劣勢(來自第一手經驗):
仍十分年輕。
基本上很複雜。一堆節點要協調,在生產環境很難排錯。
社區不多能夠獲得支持。因產品分割每一個陣營只有少許支持。
困難和可怕的升級機制。
集羣管理的算法是:SPOF,若是某個節點掛了會影響全部節點,曾經掛過4次。
集羣管理複雜的代碼透過全部節點,可能有如下幾種失效的狀況:1.數據再均衡中斷。新裝一個節點開始同步時卡住了怎麼辦?沒人告訴你,就卡那了,最後回去用MySQL了。2.破壞的數據傳染到全部節點。3.不正確的平衡也不容易修正。4.數據受權失敗。
選型結果:分片方案獲勝!說明下,分片用得太廣泛了,如 Flickr 的架構。
特性:去掉了全部你不喜歡的集羣的特性;數據分佈是手工設的;數據不會遷移,雖然有人這麼作,但爭議很大。分割數據來分散負載;各節點之間不須要溝通,主節點控制了一切。
優點:
能夠分割數據庫來增長容量
可地理分佈與協調數據
負載均衡
數據的算法很簡單。主要緣由,雖然也用了SPOF,但比複雜的集羣方案它只有一半複雜,第1次用上就知道它是能工做仍是不能工做。
ID生成很簡單。
劣勢:
不能執行大多數的join操做
不支持事務。寫A庫可能成功,寫B庫可能失敗。
不少約束限制要移到應用去實現。
(schema表)結構修改要更慎重
查詢報告須要在全部分片上運行,而後手工聚合
聚合是在應用層完成的
你的應用必須搞定上述這些問題:D
若是你的項目只有幾TB數據時,趕忙開始分片;
當主要業務錶行數達到幾億行時,建索引都內存溢出,或用上了交換分區時;
須要把最大的表單獨拿出來放到獨立的數據庫時;
單個庫已經把你的空間都用光時;
那,就要開始享受分片的好時光了:)
開始分片時先不要上新功能;
再肯定如何分片。原則是,用最少些裏查詢及最小遍歷幾個數據庫便可生成一個頁面;
去掉全部的Join操做。因表可能從不一樣分區返回,join將沒法工做;
加了很是多的緩存。基本上每一個查詢都有緩存。
步驟就像:
1 DB + Foreign Keys + Joins
1 DB + 反範式Denormalized + 緩存
1 DB + 只讀從庫 + 緩存
幾個分片庫 +只讀從庫 + 緩存
ID 分片的數據庫 + 備份從庫 + 緩存
提前從只讀庫獲取數據可難會由於延時致使問題,一個對從庫的讀請求可能主庫還沒完成複製,看起來就像數據丟失了,繞開這個問題要用到緩存。
用戶表沒有分片。只用到了一個超大的數據庫而且name是惟一的,重複用戶名將致使失敗。大多寫請求都寫到分片的數據庫裏。
看看Cassandra的環模式(Ring Model),還有Membase和Twitter的Gizzard(已經廢棄 -譯者)。
策略:最少的數據漫延等於最大的穩定性。
Cassadra有個數據均衡和受權問題:它並不知道誰擁有哪塊數據。Pinterest的作法是在應用層決定數據去向何方,因此這永遠不是一個問題。
對未來5次分片已經計劃好了。
一開始就建立了不少虛擬分片。8臺服務器、每臺有512個數據庫,每一個數據庫擁有全部的表。
爲了高可用性,他們架構了多主庫複製模式。每一個主庫分配到不一樣地區,一個失效能夠切換到全新的替換庫上。
當數據庫壓力增加時:
查看提交的新代碼是否有新功能、緩存問題或其它問題發生。
若是隻是負載增加,他們只是分割數據而後告訴應用層去一個新的數據中心獲取數據。
分割數據前,先啓動這些主庫的從庫,而後切換應用層代碼訪問新庫,有幾分鐘的時間數據仍寫到舊庫(同時會複製到新的從庫了),最後撤掉舊庫(應用層代碼此時只訪問新的從庫--這時變成主庫了)。
64位
分片ID:16位
類型:10位-大頭針(pin)、版塊、用戶和其它對象
本地ID:其它 位 用於本地表自增id。
Twitter用了一個映射表來映射ID到物理主機,這就須要一次查詢,由於Pinterest運行在AWS上,MySQL查詢一次須要3爲毫秒,他們這種額外間接的開銷接受不了,全部把位置(庫、表所在的服務器地址信息)也定義到ID裏了。
新註冊的用戶是隨機分配了分片的服務器上。
全部的數據(包括Pin、版本等)都分配到同一個分片上。這點帶來極大的好處,例如生成一個用戶信息頁,就不須要跨越多個不一樣的分片,這樣更快。
ID的設計足夠用到65536個分片使用。但開始只用到了4096,之後還能夠水平擴展,當用戶庫快滿了,再開放更多的分片,而後新用戶就會分配到新的分片上。
假設若是有50個查詢,Pinterest會根據id進行分割,全部併發的查詢,延時便是最長一個的等待時間。
每一個應用都有一個配置文件,映射分片區間到一個物理主機
「sharddb001a」: : (1, 512)(譯者:貌似他們是按字節來計算,一個字節8位最大值是256,兩字節x2=512 ;也有多是寫死映射關係的)
「sharddb001b」: : (513, 1024) - 備份熱主庫
若是要查的某用戶ID在sharddb003a:
分解ID
在分片映射中查詢
鏈接分片所在的服務器,根據類型選擇數據庫,用本地ID再查正確的用戶,返回序列化的數據
全部的數據或者是一個對象(Pin、版塊、用戶、評論)或是一個映射(用戶的版塊、喜歡的圖片Pin)
是對象的話,一個本地ID映射到MySQL的blob(譯者:或Longtext),blob塊格式是json但返回昌序列化的thrift(參考Fackbook thrift)。
若是是映射,就是一個映射表,能夠查詢用戶的版塊,ID包含了時間戳,能夠據此按事件順序排序。
Pinterest還維護了一個反射映射表,多對多的表,用來回答這類型的查詢問題:關注個人用戶。
表名是這樣紙滴:名詞動詞名詞,如userlikepings,pinslikeuser。
查詢都是按主鍵或索引鍵查詢(沒有join)
數據不會像集羣同樣處處飛。一旦一個用戶分配到第20個分片,那他全部的數據都會在這臺分片上,永遠不會飛到別的地方去。64位ID包括了分片id,全部不會變化了。你能夠移動物理數據到另一臺數據庫,但他們仍然關連着同一個分片。
全部的表在全部分片都同樣(數據是不一樣的)。沒有特殊的分片。不會遇到像用戶表這個大表檢測用戶名衝突的問題。
不須要改表結構和新索引(最神奇的地方!看他們是怎麼作到的)
由於表結構大可能是key=>value形式,value只是一個blob,能夠隨意添加字段而不用改表結構了。還有個version字段,代表blob的版本,這樣應用層能夠檢測是否要修改blob的結構,全部的數據不須要當即更新,會在用戶查詢時自動升級。
由於修改大表的結構有時會花費數小時甚至是成天,這樣的表結構頗有優點。若是你想加個新的索引,你只要建立一個新表,而後遷移數據,當你不要時時人幹掉它就好了(沒提到這種更新是否事務安全)。
從ULR獲得用戶名,從巨大的單個用戶表查表用戶id
分析(分割)用戶ID
選擇分片,鏈接分片
SELECT body from users WHERE id = userid>
SELECT boardid FROM userhasboards WHERE userid=id>
SELECT body FROM boards WHERE id IN (ids>)
SELECT pinid FROM boardhaspins WHERE boardid=id>
SELECT body FROM pins WHERE id IN (pinids)
大多請求是從緩存返回 (memcache or redis), 實踐中沒有多少流量去掉數據庫.
當遷移到一個分片的架構時,你此時有新、舊兩套架構,腳本是用來遷移到新架構的代碼
Pinterest遷移過5億條Pins和1.6億關注等
低估了遷移的成本,開始他們認爲須要2個月,事實上花了4-5個月,注意哦,是在凍結新功能上線的時候。
應用層必須同時往新、舊兩套架構寫數據
一旦確認全部的數據已經轉到新架構了,而後就逐步切割請求到新的架構,逐步增長同時測試後臺
多搞一些腳本,多投些人力來完成切割。
搞個Pyres,一個用Python寫的訪問Github的Resque隊列的工具,一個創建在Redis的隊列服務,支持優先及和重試,比起Celery和RabbitMQ好多了。
割接中犯了不少錯誤。像用戶沒有了版塊,只有重複搞了幾遍確保沒錯。
開始嘗試給每位開始配了一個系統的基本環境,都有獨立的MySQL,但業務發展太快,沒有做用;
學Facebook的方法,都在生產環境開發,但必須很是當心;
SOA
他們注意到,當數據庫負載升高時,若是加應用服務和一堆服務器,全部的服務都連着MySQL和Memcache,意味着3萬個鏈接到memcache時內存將達到上G,這時memcache會使用交換分區。
轉到SOA架構是一個辦法。如,搞一個「關注」服務,只作「關注」的查詢,這樣能夠隔離鏈接到30個,這樣壓力就能夠接受。
SOA還能夠幫助隔離功能。根據服務配置項目組及支持組和安全組。
什麼事均可能會失敗。必定要注意簡單。
讓事情頗有趣。
用到極限什麼東東都會掛掉。
構架是隻要添加複製同樣的東東就能擴展容量。好的架構也是金錢。
集羣管理算法是SPOF,若是有一個BUG將與每一個節點衝突,就這,掛過4次。
分散數據來分散負載
越少分散數據你的架構就越穩定,這也是爲什麼pinterest選擇分片而不是集羣的緣由。
SOA的守則:功能隔離。能夠幫助減小連結、組織團隊、組織支持、提高安全。
問問自已到底要什麼?不要管技術也要符合你的有所願景(或叫商業模式),即便你要所有重構。
不要懼怕丟掉一點點數據。pinterest保存用戶數據在內存而後週期性的寫到數據庫,丟失只意味了幾個小時的數據沒了,但系統會很簡單高效,這纔是更重要的。
英文原文 http://highscalability.com/blog/2013/4/15/scaling-pinterest-from-0-to-10s-of-billions-of-page-views-a.html
Pinterest Architecture Update - 18 Million Visitors, 10x Growth,12 Employees, 410 TB Of Data
A Short On The Pinterest Stack For Handling 3+ Million Users
Pinterest Cut Costs From $54 To $20 Per Hour By Automatically Shutting Down Systems
7 Scaling Strategies Facebook Used To Grow To 500 Million Users and The Four Meta Secrets Of Scaling At Facebook - I think you'll find some similarities in their approaches.
<翻譯:朱淦 350050183@qq.com 2015.7.27>