咨詢電話:023-6276-4481
熱門文章
電 話:023-6276-4481
郵箱:broiling@qq.com
地址:重慶市南岸區(qū)亞太商谷6幢25-2
英文原文:StackOverflow Update: 560M Pageviews A Month, 25 Servers, And It's All About Performance
StackOverflow 是一個 IT 技術(shù)問答網(wǎng)站,用戶可以在網(wǎng)站上提交和回答問題。當(dāng)下的 StackOverflow 已擁有 400 萬個用戶,4000 萬個回答,月 PV5.6 億,世界排行第 54。然而值得關(guān)注的是,支撐他們網(wǎng)站的全部服務(wù)器只有 25 臺,并且都保持著非常低的資源使用率,這是一場高有效性、負(fù)載均衡、緩存、數(shù)據(jù)庫、搜索及高效代碼上的較量。近日,High Scalability 創(chuàng)始人 Todd Hoff 根據(jù) Marco Cecconi 的演講視頻“ The architecture of StackOverflow”以及 Nick Craver 的博文“ What it takes to run Stack Overflow”總結(jié)了 StackOverflow 的成功原因。
意料之中,也是意料之外,Stack Overflow 仍然重度使用著微軟的產(chǎn)品。他們認(rèn)為既然微軟的基礎(chǔ)設(shè)施可以滿足需求,又足夠便宜,那么沒有什么理由去做根本上的改變。而在需要的地方,他們同樣使用了 Linux。究其根本,一切都是為了性能。
另一個值得關(guān)注的地方是,Stack Overflow 仍然使用著縱向擴(kuò)展策略,沒有使用云。他們使用了 384GB 的內(nèi)存和 2TB 的 SSD 來支撐 SQL Servers,如果使用 AWS 的話,花費(fèi)可想而知。沒有使用云的另一個原因是 Stack Overflow 認(rèn)為云會一定程度上的降低性能,同時也會給優(yōu)化和排查系統(tǒng)問題增加難度。此外,他們的架構(gòu)也并不需要橫向擴(kuò)展。峰值期間是橫向擴(kuò)展的殺手級應(yīng)用場景,然而他們有著豐富的系統(tǒng)調(diào)整經(jīng)驗(yàn)去應(yīng)對。該公司仍然堅(jiān)持著 Jeff Atwood 的名言——硬件永遠(yuǎn)比程序員便宜。
Marco Ceccon 曾提到,在談及系統(tǒng)時,有一件事情必須首先弄明白——需要解決問題的類型。首先,從簡單方面著手,StackExchange 究竟是用來做什么的——首先是一些主題,然后圍繞這些主題建立社區(qū),最后就形成了這個令人敬佩的問答網(wǎng)站。
其次則是規(guī)模相關(guān)。StackExchange 在飛速增長,需要處理大量的數(shù)據(jù)傳輸,那么這些都是如何完成的,特別是只使用了 25 臺服務(wù)器,下面一起追根揭底:
狀態(tài)
StackExchange 擁有 110 個站點(diǎn),以每個月 3 到 4 個的速度增長。
400 萬用戶
800 萬問題
4000 萬答案
世界排名 54 位
每年增長 100%
月 PV 5.6 億萬
大多數(shù)工作日期間峰值為 2600 到 3000 請求每秒,作為一個編程相關(guān)網(wǎng)站,一般情況下工作日的請求都會高于周末
25 臺服務(wù)器
SSD 中儲存了 2TB 的 SQL 數(shù)據(jù)
每個 web server 都配置了 2 個 320G 的 SSD,使用 RAID 1
每個 ElasticSearch 主機(jī)都配備了 300GB 的機(jī)械硬盤,同時也使用了 SSD
Stack Overflow 的讀寫比是 40:60
DB Server 的平均 CPU 利用率是 10%
11 個 web server,使用 IIS
2 個負(fù)載均衡器,1 個活躍,使用 HAProxy
4 個活躍的數(shù)據(jù)庫節(jié)點(diǎn),使用 MS SQL
3 臺實(shí)現(xiàn)了 tag engine 的應(yīng)用程序服務(wù)器,所有搜索都通過 tag
3 臺服務(wù)器通過 ElasticSearch 做搜索
2 臺使用了 Redis 的服務(wù)器支撐分布式緩存和消息
2 臺 Networks(Nexus 5596 + Fabric Extenders)
2 Cisco 5525-X ASAs
2 Cisco 3945 Routers
主要服務(wù) Stack Exchange API 的 2 個只讀 SQL Servers
VM 用于部署、域控制器、監(jiān)控、運(yùn)維數(shù)據(jù)庫等場合
平臺
ElasticSearch
Redis
HAProxy
MS SQL
Opserver
TeamCity
Jil——Fast .NET JSON Serializer,建立在 Sigil 之上
Dapper——微型的 ORM
UI
UI 擁有一個信息收件箱,用于新徽章獲得、用戶發(fā)送信息、重大事件發(fā)生時的信息收取,使用 WebSockets 實(shí)現(xiàn),并通過 Redis 支撐。
搜索箱通過 ElasticSearch 實(shí)現(xiàn),使用了一個 REST 接口。
因?yàn)橛脩籼岢鰡栴}的頻率很高,因此很難顯示最新問題,每秒都會有新的問題產(chǎn)生,從而這里需要開發(fā)一個關(guān)注用戶行為模式的算法,只給用戶顯示感興趣的問題。它使用了基于 Tag 的復(fù)雜查詢,這也是開發(fā)獨(dú)立 Tag Engine 的原因。
服務(wù)器端模板用于生成頁面。
服務(wù)器
25 臺服務(wù)器并沒有滿載,CPU 使用率并不高,單計(jì)算 SO(Stack Overflow)只需要 5 臺服務(wù)器。
數(shù)據(jù)庫服務(wù)器資源利用率在 10% 左右,除下執(zhí)行備份時。
為什么會這么低?因?yàn)閿?shù)據(jù)庫服務(wù)器足足擁有 384GB 內(nèi)存,同時 web server 的 CPU 利用率也只有 10%-15%。
縱向擴(kuò)展還沒有遇到瓶頸。通常情況下,如此流量使用橫向擴(kuò)展大約需要 100 到 300 臺服務(wù)器。
簡單的系統(tǒng)?;?.Net,只用了 9 個項(xiàng)目,其他系統(tǒng)可能需要 100 個。之所以使用這么少系統(tǒng)是為了追求極限的編譯速度,這點(diǎn)需要從系統(tǒng)開始時就進(jìn)行規(guī)劃,每臺服務(wù)器的編譯時間大約是 10 秒。
11 萬行代碼,對比流量來說非常少。
使用這種極簡的方式主要基于幾個原因。首先,不需要太多測試,因?yàn)?Meta.stackoverflow 本來就是一個問題和 bug 討論社區(qū)。其次,Meta.stackoverflow 還是一個軟件的測試網(wǎng)站,如果用戶發(fā)現(xiàn)問題的話,往往會提出并給予解決方案。
紐約數(shù)據(jù)中心使用的是 Windows 2012,已經(jīng)向 2012 R2 升級(Oregon 已經(jīng)完成了升級),Linux 系統(tǒng)使用的是 Centos 6.4。
SSD
默認(rèn)使用的是 Intel 330(Web 層等)
Intel 520 用于中間層寫入,比如 Elastic Search
數(shù)據(jù)層使用 Intel 710 和 S3700
系統(tǒng)同時使用了 RAID 1 和 RAID 10(任何4+ 以上的磁盤都使用 RAID 10)。不畏懼故障發(fā)生,即使生產(chǎn)環(huán)境中使用了上千塊 2.5 英寸 SSD,還沒碰到過一塊失敗的情景。每個模型都使用了 1 個以上的備件,多個磁盤發(fā)生故障的情景不在考慮之中。
ElasticSearch 在 SSD 上表現(xiàn)的異常出色,因?yàn)?SO writes/re-indexes 的操作非常頻繁。
SSD 改變了搜索的使用方式。因?yàn)殒i的問題,Luncene.net 并不能支撐 SO 的并發(fā)負(fù)載,因此他們轉(zhuǎn)向了 ElasticSearch。在全 SSD 環(huán)境下,并不需要圍繞 Binary Reader 建立鎖。
高可用性
異地備份——主數(shù)據(jù)中心位于紐約,備份數(shù)據(jù)中心在 Oregon。
Redis 有兩個從節(jié)點(diǎn),SQL 有 2 個備份,Tag Engine 有 3 個節(jié)點(diǎn),elastic 有 3 個節(jié)點(diǎn),冗余一切,并在兩個數(shù)據(jù)中心同時存在。
Nginx 是用于 SSL,終止 SSL 時轉(zhuǎn)換使用 HAProxy。
并不是主從所有,一些臨時的數(shù)據(jù)只會放到緩存中
所有 HTTP 流量發(fā)送只占總流量的 77%,還存在 Oregon 數(shù)據(jù)中心的備份及一些其他的 VPN 流量。這些流量主要由 SQL 和 Redis 備份產(chǎn)生。
數(shù)據(jù)庫
MS SQL Server
Stack Exchange 為每個網(wǎng)站都設(shè)置了數(shù)據(jù)庫,因此 Stack Overflow 有一個、Server Fault 有一個,以此類推。
在紐約的主數(shù)據(jù)中心,每個集群通常都使用 1 主和 1 只讀備份的配置,同時還會在 Oregon 數(shù)據(jù)中心也設(shè)置一個備份。如果是運(yùn)行的是 Oregon 集群,那么兩個在紐約數(shù)據(jù)中心的備份都會是只讀和同步的。
為其他內(nèi)容準(zhǔn)備的數(shù)據(jù)庫。這里還存在一個“網(wǎng)絡(luò)范圍”的數(shù)據(jù)庫,用于儲存登陸憑證和聚合數(shù)據(jù)(大部分是 stackexchange.com 用戶文件或者 API)。
Careers Stack Overflow、stackexchange.com 和 Area 51 等都擁有自己獨(dú)立的數(shù)據(jù)庫模式。
模式的變化需要同時提供給所有站點(diǎn)的數(shù)據(jù)庫,它們需要向下兼容,舉個例子,如果需要重命名一個列,那么將非常麻煩,這里需要進(jìn)行多個操作:增加一個新列,添加作用在兩個列上的代碼,給新列寫數(shù)據(jù),改變代碼讓新列有效,移除舊列。
并不需要分片,所有事情通過索引來解決,而且數(shù)據(jù)體積也沒那么大。如果有 filtered indexes 需求,那么為什么不更高效的進(jìn)行?常見模式只在 DeletionDate = Null 上做索引,其他則通過為枚舉指定類型。每項(xiàng) votes 都設(shè)置了 1 個表,比如一張表給 post votes,1 張表給 comment votes。大部分的頁面都可以實(shí)時渲染,只為匿名用戶緩存,因此,不存在緩存更新,只有重查詢。
Scores 是非規(guī)范化的,因此需要經(jīng)常查詢。它只包含 IDs 和 dates,post votes 表格當(dāng)下大約有 56454478 行,使用索引,大部分的查詢都可以在數(shù)毫秒內(nèi)完成。
Tag Engine 是完全獨(dú)立的,這就意味著核心功能并不依賴任何外部應(yīng)用程序。它是一個巨大的內(nèi)存結(jié)構(gòu)數(shù)組結(jié)構(gòu),專為 SO 用例優(yōu)化,并為重負(fù)載組合進(jìn)行預(yù)計(jì)算。Tag Engine 是個簡單的 windows 服務(wù),冗余的運(yùn)行在多個主機(jī)上。CPU 使用率基本上保持在2-5%,3 個主機(jī)專門用于冗余,不負(fù)責(zé)任何負(fù)載。如果所有主機(jī)同時發(fā)生故障,網(wǎng)絡(luò)服務(wù)器將把 Tag Engine 加載到內(nèi)存中持續(xù)運(yùn)行。
關(guān)于 Dapper 無編譯器校驗(yàn)查詢與傳統(tǒng) ORM 的對比。使用編譯器有很多好處,但在運(yùn)行時仍然會存在 fundamental disconnect 問題。同時更重要的是,由于生成 nasty SQL,通常情況還需要去尋找原始代碼,而 Query Hint 和 parameterization 控制等能力的缺乏更讓查詢優(yōu)化變得復(fù)雜。
編碼
流程
大部分程序員都是遠(yuǎn)程工作,自己選擇編碼地點(diǎn)
編譯非???/p>
然后運(yùn)行少量的測試
一旦編譯成功,代碼即轉(zhuǎn)移至開發(fā)交付準(zhǔn)備服務(wù)器
通過功能開關(guān)隱藏新功能
在相同硬件上作為其他站點(diǎn)測試運(yùn)行
然后轉(zhuǎn)移至 Meta.stackoverflow 測試,每天有上千個程序員在使用,一個很好的測試環(huán)境
如果通過則上線,在更廣大的社區(qū)進(jìn)行測試
大量使用靜態(tài)類和方法,為了更簡單及更好的性能
編碼過程非常簡單,因?yàn)閺?fù)雜的部分被打包到庫里,這些庫被開源和維護(hù)。.Net 項(xiàng)目數(shù)量很低,因?yàn)槭褂昧松鐓^(qū)共享的部分代碼。
開發(fā)者同時使用 2 到 3 個顯示器,多個屏幕可以顯著提高生產(chǎn)效率。
緩存
緩存一切
5 個等級的緩存
1 級是網(wǎng)絡(luò)級緩存,緩存在瀏覽器、CDN 以及代理服務(wù)器中。
2 級由 .Net 框架 HttpRuntime.Cache 完成,在每臺服務(wù)器的內(nèi)存中。
3 級 Redis,分布式內(nèi)存鍵值存儲,在多個支撐同一個站點(diǎn)的服務(wù)器上共享緩存項(xiàng)。
4 級 SQL Server Cache,整個數(shù)據(jù)庫,所有數(shù)據(jù)都被放到內(nèi)存中。
5 級 SSD。通常只在 SQL Server 預(yù)熱后才生效。
舉個例子,每個幫助頁面都進(jìn)行了緩存,訪問一個頁面的代碼非常簡單:
使用了靜態(tài)的方法和類。從 OOP 角度來看確實(shí)很糟,但是非??觳⒂欣诤啙嵕幋a。
緩存由 Redis 和 Dapper 支撐,一個微型 ORM
為了解決垃圾收集問題,模板中 1 個類只使用 1 個副本,被建立和保存在緩存中。監(jiān)測一切,包括 GC 操。據(jù)統(tǒng)計(jì)顯示,間接層增加 GC 壓力達(dá)到了某個程度時會顯著的降低性能。
CDN Hit 。鑒于查詢字符串基于文件內(nèi)容進(jìn)行哈希,只在有新建立時才會被再次取出。每天 3000 萬到 5000 萬 Hit,帶寬大約為 300GB 到 600GB。
CDN 不是用來應(yīng)對 CPU 或I/O負(fù)載,而是幫助用戶更快的獲得答案
部署
每天 5 次部署,不去建立過大的應(yīng)用。主要因?yàn)?/p>
可以直接的監(jiān)視性能
盡可能最小化建立,可以工作才是重點(diǎn)
產(chǎn)品建立后再通過強(qiáng)大的腳本拷貝到各個網(wǎng)頁層,每個服務(wù)器的步驟是:
通過 POST 通知 HAProxy 下架某臺服務(wù)器
延遲 IIS 結(jié)束現(xiàn)有請求(大約 5 秒)
停止網(wǎng)站(通過同一個 PSSession 結(jié)束所有下游)
Robocopy 文件
開啟網(wǎng)站
通過另一個 POST 做 HAProxy Re-enable
幾乎所有部署都是通過 puppet 或 DSC,升級通常只是大幅度調(diào)整 RAID 陣列并通過 PXE boot 安裝,這樣做非常快速。
協(xié)作
團(tuán)隊(duì)
SRE (System Reliability Engineering):5 人
Core Dev(Q&A site)6-7 人
Core Dev Mobile:6 人
Careers 團(tuán)隊(duì)專門負(fù)責(zé) SO Careers 產(chǎn)品開發(fā):7 人
Devops 和開發(fā)者結(jié)合的非常緊密
團(tuán)隊(duì)間變化很大
大部分員工遠(yuǎn)程工作
辦公室主要用于銷售,Denver 和 London 除外
一切平等,些許偏向紐約工作者,因?yàn)槊鎸γ嬗兄诠ぷ鹘涣?,但是在線工作影響也并不大
對比可以在同一個辦公室辦公,他們更偏向熱愛產(chǎn)品及有才華的工程師,他們可以很好的衡量利弊
許多人因?yàn)榧彝ザx擇遠(yuǎn)程工作,紐約是不錯,但是生活并不寬松
辦公室設(shè)立在曼哈頓,那是個人才的誕生地。數(shù)據(jù)中心不能太偏,因?yàn)榻?jīng)常會涉及升級
打造一個強(qiáng)大團(tuán)隊(duì),偏愛極客。早期的微軟就聚集了大量極客,因此他們征服了整個世界
Stack Overflow 社區(qū)也是個招聘的地點(diǎn),他們在那尋找熱愛編碼、樂于助人及熱愛交流的人才。
編制預(yù)算
預(yù)算是項(xiàng)目的基礎(chǔ)。錢只花在為新項(xiàng)目建立基礎(chǔ)設(shè)施上,如此低利用率的 web server 還是 3 年前數(shù)據(jù)中心建立時購入。
測試
快速迭代和遺棄
許多測試都是發(fā)布隊(duì)伍完成的。開發(fā)擁有一個同樣的 SQL 服務(wù)器,并且運(yùn)行在相同的 Web 層,因此性能測試并不會糟糕。
非常少的測試。Stack Overflow 并沒有進(jìn)行太多的單元測試,因?yàn)樗麄兪褂昧舜罅康撵o態(tài)代碼,還有一個非?;钴S的社區(qū)。
基礎(chǔ)設(shè)施改變。鑒于所有東西都有雙份,所以每個舊配置都有備份,并使用了一個快速故障恢復(fù)機(jī)制。比如,keepalived 可以在負(fù)載均衡器中快速回退。
對比定期維護(hù),他們更愿意依賴冗余系統(tǒng)。SQL 備份用一個專門的服務(wù)器進(jìn)行測試,只為了可以重存儲。計(jì)劃做每兩個月一次的全數(shù)據(jù)中心故障恢復(fù),或者使用完全只讀的第二數(shù)據(jù)中心。
每次新功能發(fā)布都做單元測試、集成測試盒 UI 測試,這就意味著可以預(yù)知輸入的產(chǎn)品功能測試后就會推送到孵化網(wǎng)站,即 meta.stackexchange(原 meta.stackoverflow)。
監(jiān)視/日志
當(dāng)下正在考慮使用 http://logstash.net/做日志管理,目前使用了一個專門的服務(wù)將 syslog UDP 傳輸?shù)?SQL 數(shù)據(jù)庫中。網(wǎng)頁中為計(jì)時添加 header,這樣就可以通過 HAProxy 來捕獲并且融合到 syslog 傳輸中。
Opserver 和 Realog 用于顯示測量結(jié)果。Realog 是一個日志展示系統(tǒng),由 Kyle Brandt 和 Matt Jibson 使用 Go 建立。
日志通過 HAProxy 負(fù)載均衡器借助 syslog 完成,而不是 IIS,因?yàn)槠涔δ鼙?IIS 更豐富。
關(guān)于云
還是老生常談,硬件永遠(yuǎn)比開發(fā)者和有效率的代碼便宜。基于木桶效應(yīng),速度肯定受限于某個短板,現(xiàn)有的云服務(wù)基本上都存在容量和性能限制。
如果從開始就使用云來建設(shè) SO 說不定也會達(dá)到現(xiàn)在的水準(zhǔn)。但毫無疑問的是,如果達(dá)到同樣的性能,使用云的成本將遠(yuǎn)遠(yuǎn)高于自建數(shù)據(jù)中心。
性能至上
StackOverflow 是個重度的性能控,主頁加載的時間永遠(yuǎn)控制在 50 毫秒內(nèi),當(dāng)下的響應(yīng)時間是 28 毫秒。
程序員熱衷于降低頁面加載時間以及提高用戶體驗(yàn)。
每個獨(dú)立的網(wǎng)絡(luò)提交都予以計(jì)時和記錄,這種計(jì)量可以弄清楚提升性能需要修改的地方。
如此低資源利用率的主要原因就是高效的代碼。web server 的 CPU 平均利用率在5% 到 15% 之間,內(nèi)存使用為 15.5 GB,網(wǎng)絡(luò)傳輸在 20 Mb/s到 40 Mb/s。SQL 服務(wù)器的 CPU 使用率在5% 到 10% 之間,內(nèi)存使用是 365GB,網(wǎng)絡(luò)傳輸為 100 Mb/s到 200 Mb/s。這可以帶來 3 個好處:給升級留下很大的空間;在嚴(yán)重錯誤發(fā)生時可以保持服務(wù)可用;在需要時可以快速回檔。
學(xué)到的知識
1. 為什么使用 MS 產(chǎn)品的同時還使用 Redis?什么好用用什么,不要做無必要的系統(tǒng)之爭,比如 C# 在 Windows 機(jī)器上運(yùn)行最好,我們使用 IIS;Redis 在*nix 機(jī)器上可以得到充分發(fā)揮,我們使用*nix。
2. Overkill 即策略。平常的利用率并不能代表什么,當(dāng)某些特定的事情發(fā)生時,比如備份、重建等完全可以將資源使用拉滿。
3. 堅(jiān)固的 SSD。所有數(shù)據(jù)庫都建立在 SSD 之上,這樣可以獲得 0 延時。
4. 了解你的讀寫負(fù)載。
5. 高效的代碼意味著更少的主機(jī)。只有新項(xiàng)目上線時才會因?yàn)樘厥庑枨笤黾佑布?,通常情況下是添加內(nèi)存,但在此之外,高效的代碼就意味著 0 硬件添加。所以經(jīng)常只討論兩個問題:為存儲增加新的 SSD;為新項(xiàng)目增加硬件。
6. 不要害怕定制化。SO 在 Tag 上使用復(fù)雜查詢,因此專門開發(fā)了所需的 Tag Engine。
7. 只做必須做的事情。之所以不需要測試是因?yàn)橛幸粋€活躍的社區(qū)支撐,比如,開發(fā)者不用擔(dān)心出現(xiàn)“Square Wheel”效應(yīng),如果開發(fā)者可以制作一個更更輕量級的組件,那就替代吧。
8. 注重硬件知識,比如 IL。一些代碼使用 IL 而不是C#。聚焦 SQL 查詢計(jì)劃。使用 web server 的內(nèi)存轉(zhuǎn)儲究竟做了些什么。探索,比如為什么一個 split 會產(chǎn)生 2GB 的垃圾。
9. 切勿官僚作風(fēng)。總有一些新的工具是你需要的,比如,一個編輯器,新版本的 Visual Studio,降低提升過程中的一切阻力。
10. 垃圾回收驅(qū)動編程。SO 在減少垃圾回收成本上做了很多努力,跳過類似 TDD 的實(shí)踐,避免抽象層,使用靜態(tài)方法。雖然極端,但是確實(shí)打造出非常高效的代碼。
11. 高效代碼的價(jià)值遠(yuǎn)遠(yuǎn)超出你想象,它可以讓硬件跑的更快,降低資源使用,切記讓代碼更容易被程序員理解。