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