問(wèn)答社區(qū)網(wǎng)絡(luò) StackExchange 由 100 多個(gè)網(wǎng)站構(gòu)成,其中包括了 Alexa 排名第 54 的 StackOverflow。StackExchang 有 400 萬(wàn)用戶(hù),每月 5.6 億 PV,但只用 25 臺(tái)服務(wù)器,并且 CPU 負(fù)荷并不高。
它沒(méi)有使用云計(jì)算,因?yàn)樵朴?jì)算可能會(huì)拖慢速度,更難優(yōu)化和更難排除系統(tǒng)故障。
StackOverflow 仍然使用微軟的架構(gòu),它非常實(shí)際,微軟的基礎(chǔ)設(shè)施能有效工作,又足夠廉價(jià),沒(méi)有令人信服的理由需要做出改變。但這并不表示它不使用 Linux,它將 Linux 用在有意義的地方。
它的 Windows 服務(wù)器運(yùn)行的操作系統(tǒng)版本是 Windows 2012 R2,Linux 服務(wù)器運(yùn)行 Centos 6.4。
網(wǎng)站數(shù)據(jù)庫(kù) MS SQL 大小 2TB,全都儲(chǔ)存在 SSD 上,它有 11 臺(tái)運(yùn)行 IIS 的 Web 服務(wù)器,2 臺(tái)運(yùn)行 HAProxy 的負(fù)載均衡服務(wù)器,2 臺(tái)運(yùn)行 Redis 的緩存服務(wù)器。

StackOverflow 是一個(gè) IT 技術(shù)問(wèn)答網(wǎng)站,用戶(hù)可以在網(wǎng)站上提交和回答問(wèn)題。當(dāng)下的 StackOverflow 已擁有 400 萬(wàn)個(gè)用戶(hù),4000 萬(wàn)個(gè)回答,月 PV5.6 億,世界排行第 54。然而值得關(guān)注的是,支撐他們網(wǎng)站的全部服務(wù)器只有 25 臺(tái),并且都保持著非常低的資源使用率,這是一場(chǎng)高有效性、負(fù)載均衡、緩存、數(shù)據(jù)庫(kù)、搜索及高效代碼上的較量。近日,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è)施可以滿(mǎn)足需求,又足夠便宜,那么沒(méi)有什么理由去做根本上的改變。而在需要的地方,他們同樣使用了 Linux。究其根本,一切都是為了性能。
另一個(gè)值得關(guān)注的地方是,Stack Overflow 仍然使用著縱向擴(kuò)展策略,沒(méi)有使用云。他們使用了 384GB 的內(nèi)存和 2TB 的 SSD 來(lái)支撐 SQL Servers,如果使用 AWS 的話,花費(fèi)可想而知。沒(méi)有使用云的另一個(gè)原因是 Stack Overflow 認(rèn)為云會(huì)一定程度上的降低性能,同時(shí)也會(huì)給優(yōu)化和排查系統(tǒng)問(wèn)題增加難度。此外,他們的架構(gòu)也并不需要橫向擴(kuò)展。峰值期間是橫向擴(kuò)展的殺手級(jí)應(yīng)用場(chǎng)景,然而他們有著豐富的系統(tǒng)調(diào)整經(jīng)驗(yàn)去應(yīng)對(duì)。該公司仍然堅(jiān)持著 Jeff Atwood 的名言——硬件永遠(yuǎn)比程序員便宜。
Marco Ceccon 曾提到,在談及系統(tǒng)時(shí),有一件事情必須首先弄明白——需要解決問(wèn)題的類(lèi)型。首先,從簡(jiǎn)單方面著手,StackExchange 究竟是用來(lái)做什么的——首先是一些主題,然后圍繞這些主題建立社區(qū),最后就形成了這個(gè)令人敬佩的問(wèn)答網(wǎng)站。
其次則是規(guī)模相關(guān)。StackExchange 在飛速增長(zhǎng),需要處理大量的數(shù)據(jù)傳輸,那么這些都是如何完成的,特別是只使用了 25 臺(tái)服務(wù)器,下面一起追根揭底:
狀態(tài)
StackExchange 擁有 110 個(gè)站點(diǎn),以每個(gè)月 3 到 4 個(gè)的速度增長(zhǎng)。
400 萬(wàn)用戶(hù)
800 萬(wàn)問(wèn)題
4000 萬(wàn)答案
世界排名 54 位
每年增長(zhǎng) 100%
月 PV 5.6 億萬(wàn)
大多數(shù)工作日期間峰值為 2600 到 3000 請(qǐng)求每秒,作為一個(gè)編程相關(guān)網(wǎng)站,一般情況下工作日的請(qǐng)求都會(huì)高于周末
25 臺(tái)服務(wù)器
SSD 中儲(chǔ)存了 2TB 的 SQL 數(shù)據(jù)
每個(gè) web server 都配置了 2 個(gè) 320G 的 SSD,使用 RAID 1
每個(gè) ElasticSearch 主機(jī)都配備了 300GB 的機(jī)械硬盤(pán),同時(shí)也使用了 SSD
Stack Overflow 的讀寫(xiě)比是 40:60
DB Server 的平均 CPU 利用率是 10%
11 個(gè) web server,使用 IIS
2 個(gè)負(fù)載均衡器,1 個(gè)活躍,使用 HAProxy
4 個(gè)活躍的數(shù)據(jù)庫(kù)節(jié)點(diǎn),使用 MS SQL
3 臺(tái)實(shí)現(xiàn)了 tag engine 的應(yīng)用程序服務(wù)器,所有搜索都通過(guò) tag
3 臺(tái)服務(wù)器通過(guò) ElasticSearch 做搜索
2 臺(tái)使用了 Redis 的服務(wù)器支撐分布式緩存和消息
2 臺(tái) Networks(Nexus 5596 + Fabric Extenders)
2 Cisco 5525-X ASAs
2 Cisco 3945 Routers
主要服務(wù) Stack Exchange API 的 2 個(gè)只讀 SQL Servers
VM 用于部署、域控制器、監(jiān)控、運(yùn)維數(shù)據(jù)庫(kù)等場(chǎng)合
平臺(tái)
ElasticSearch
Redis
HAProxy
MS SQL
Opserver
TeamCity
Jil——Fast .NET JSON Serializer,建立在 Sigil 之上
Dapper——微型的 ORM
UI
UI 擁有一個(gè)信息收件箱,用于新徽章獲得、用戶(hù)發(fā)送信息、重大事件發(fā)生時(shí)的信息收取,使用 WebSockets 實(shí)現(xiàn),并通過(guò) Redis 支撐。
搜索箱通過(guò) ElasticSearch 實(shí)現(xiàn),使用了一個(gè) REST 接口。
因?yàn)橛脩?hù)提出問(wèn)題的頻率很高,因此很難顯示最新問(wèn)題,每秒都會(huì)有新的問(wèn)題產(chǎn)生,從而這里需要開(kāi)發(fā)一個(gè)關(guān)注用戶(hù)行為模式的算法,只給用戶(hù)顯示感興趣的問(wèn)題。它使用了基于 Tag 的復(fù)雜查詢(xún),這也是開(kāi)發(fā)獨(dú)立 Tag Engine 的原因。
服務(wù)器端模板用于生成頁(yè)面。
服務(wù)器
25 臺(tái)服務(wù)器并沒(méi)有滿(mǎn)載,CPU 使用率并不高,單計(jì)算 SO(Stack Overflow)只需要 5 臺(tái)服務(wù)器。
數(shù)據(jù)庫(kù)服務(wù)器資源利用率在 10% 左右,除下執(zhí)行備份時(shí)。
為什么會(huì)這么低?因?yàn)閿?shù)據(jù)庫(kù)服務(wù)器足足擁有 384GB 內(nèi)存,同時(shí) web server 的 CPU 利用率也只有 10%-15%。
縱向擴(kuò)展還沒(méi)有遇到瓶頸。通常情況下,如此流量使用橫向擴(kuò)展大約需要 100 到 300 臺(tái)服務(wù)器。
簡(jiǎn)單的系統(tǒng)?;?.Net,只用了 9 個(gè)項(xiàng)目,其他系統(tǒng)可能需要 100 個(gè)。之所以使用這么少系統(tǒng)是為了追求極限的編譯速度,這點(diǎn)需要從系統(tǒng)開(kāi)始時(shí)就進(jìn)行規(guī)劃,每臺(tái)服務(wù)器的編譯時(shí)間大約是 10 秒。
11 萬(wàn)行代碼,對(duì)比流量來(lái)說(shuō)非常少。
使用這種極簡(jiǎn)的方式主要基于幾個(gè)原因。首先,不需要太多測(cè)試,因?yàn)?Meta.stackoverflow 本來(lái)就是一個(gè)問(wèn)題和 bug 討論社區(qū)。其次,Meta.stackoverflow 還是一個(gè)軟件的測(cè)試網(wǎng)站,如果用戶(hù)發(fā)現(xiàn)問(wèn)題的話,往往會(huì)提出并給予解決方案。
紐約數(shù)據(jù)中心使用的是 Windows 2012,已經(jīng)向 2012 R2 升級(jí)(Oregon 已經(jīng)完成了升級(jí)),Linux 系統(tǒng)使用的是 Centos 6.4。
SSD
默認(rèn)使用的是 Intel 330(Web 層等)
Intel 520 用于中間層寫(xiě)入,比如 Elastic Search
數(shù)據(jù)層使用 Intel 710 和 S3700
系統(tǒng)同時(shí)使用了 RAID 1 和 RAID 10(任何4+ 以上的磁盤(pán)都使用 RAID 10)。不畏懼故障發(fā)生,即使生產(chǎn)環(huán)境中使用了上千塊 2.5 英寸 SSD,還沒(méi)碰到過(guò)一塊失敗的情景。每個(gè)模型都使用了 1 個(gè)以上的備件,多個(gè)磁盤(pán)發(fā)生故障的情景不在考慮之中。
ElasticSearch 在 SSD 上表現(xiàn)的異常出色,因?yàn)?SO writes/re-indexes 的操作非常頻繁。
SSD 改變了搜索的使用方式。因?yàn)殒i的問(wèn)題,Luncene.net 并不能支撐 SO 的并發(fā)負(fù)載,因此他們轉(zhuǎn)向了 ElasticSearch。在全 SSD 環(huán)境下,并不需要圍繞 Binary Reader 建立鎖。
高可用性
異地備份——主數(shù)據(jù)中心位于紐約,備份數(shù)據(jù)中心在 Oregon。
Redis 有兩個(gè)從節(jié)點(diǎn),SQL 有 2 個(gè)備份,Tag Engine 有 3 個(gè)節(jié)點(diǎn),elastic 有 3 個(gè)節(jié)點(diǎn),冗余一切,并在兩個(gè)數(shù)據(jù)中心同時(shí)存在。
Nginx 是用于 SSL,終止 SSL 時(shí)轉(zhuǎn)換使用 HAProxy。
并不是主從所有,一些臨時(shí)的數(shù)據(jù)只會(huì)放到緩存中
所有 HTTP 流量發(fā)送只占總流量的 77%,還存在 Oregon 數(shù)據(jù)中心的備份及一些其他的 VPN 流量。這些流量主要由 SQL 和 Redis 備份產(chǎn)生。
數(shù)據(jù)庫(kù)
MS SQL Server
Stack Exchange 為每個(gè)網(wǎng)站都設(shè)置了數(shù)據(jù)庫(kù),因此 Stack Overflow 有一個(gè)、Server Fault 有一個(gè),以此類(lèi)推。
在紐約的主數(shù)據(jù)中心,每個(gè)集群通常都使用 1 主和 1 只讀備份的配置,同時(shí)還會(huì)在 Oregon 數(shù)據(jù)中心也設(shè)置一個(gè)備份。如果是運(yùn)行的是 Oregon 集群,那么兩個(gè)在紐約數(shù)據(jù)中心的備份都會(huì)是只讀和同步的。
為其他內(nèi)容準(zhǔn)備的數(shù)據(jù)庫(kù)。這里還存在一個(gè)“網(wǎng)絡(luò)范圍”的數(shù)據(jù)庫(kù),用于儲(chǔ)存登陸憑證和聚合數(shù)據(jù)(大部分是 stackexchange.com 用戶(hù)文件或者 API)。
Careers Stack Overflow、stackexchange.com 和 Area 51 等都擁有自己獨(dú)立的數(shù)據(jù)庫(kù)模式。
模式的變化需要同時(shí)提供給所有站點(diǎn)的數(shù)據(jù)庫(kù),它們需要向下兼容,舉個(gè)例子,如果需要重命名一個(gè)列,那么將非常麻煩,這里需要進(jìn)行多個(gè)操作:增加一個(gè)新列,添加作用在兩個(gè)列上的代碼,給新列寫(xiě)數(shù)據(jù),改變代碼讓新列有效,移除舊列。
并不需要分片,所有事情通過(guò)索引來(lái)解決,而且數(shù)據(jù)體積也沒(méi)那么大。如果有 filtered indexes 需求,那么為什么不更高效的進(jìn)行?常見(jiàn)模式只在 DeletionDate = Null 上做索引,其他則通過(guò)為枚舉指定類(lèi)型。每項(xiàng) votes 都設(shè)置了 1 個(gè)表,比如一張表給 post votes,1 張表給 comment votes。大部分的頁(yè)面都可以實(shí)時(shí)渲染,只為匿名用戶(hù)緩存,因此,不存在緩存更新,只有重查詢(xún)。
Scores 是非規(guī)范化的,因此需要經(jīng)常查詢(xún)。它只包含 IDs 和 dates,post votes 表格當(dāng)下大約有 56454478 行,使用索引,大部分的查詢(xún)都可以在數(shù)毫秒內(nèi)完成。
Tag Engine 是完全獨(dú)立的,這就意味著核心功能并不依賴(lài)任何外部應(yīng)用程序。它是一個(gè)巨大的內(nèi)存結(jié)構(gòu)數(shù)組結(jié)構(gòu),專(zhuān)為 SO 用例優(yōu)化,并為重負(fù)載組合進(jìn)行預(yù)計(jì)算。Tag Engine 是個(gè)簡(jiǎn)單的 windows 服務(wù),冗余的運(yùn)行在多個(gè)主機(jī)上。CPU 使用率基本上保持在2-5%,3 個(gè)主機(jī)專(zhuān)門(mén)用于冗余,不負(fù)責(zé)任何負(fù)載。如果所有主機(jī)同時(shí)發(fā)生故障,網(wǎng)絡(luò)服務(wù)器將把 Tag Engine 加載到內(nèi)存中持續(xù)運(yùn)行。
關(guān)于 Dapper 無(wú)編譯器校驗(yàn)查詢(xún)與傳統(tǒng) ORM 的對(duì)比。使用編譯器有很多好處,但在運(yùn)行時(shí)仍然會(huì)存在 fundamental disconnect 問(wèn)題。同時(shí)更重要的是,由于生成 nasty SQL,通常情況還需要去尋找原始代碼,而 Query Hint 和 parameterization 控制等能力的缺乏更讓查詢(xún)優(yōu)化變得復(fù)雜。
編碼
流程
大部分程序員都是遠(yuǎn)程工作,自己選擇編碼地點(diǎn)
編譯非???br />然后運(yùn)行少量的測(cè)試
一旦編譯成功,代碼即轉(zhuǎn)移至開(kāi)發(fā)交付準(zhǔn)備服務(wù)器
通過(guò)功能開(kāi)關(guān)隱藏新功能
在相同硬件上作為其他站點(diǎn)測(cè)試運(yùn)行
然后轉(zhuǎn)移至 Meta.stackoverflow 測(cè)試,每天有上千個(gè)程序員在使用,一個(gè)很好的測(cè)試環(huán)境
如果通過(guò)則上線,在更廣大的社區(qū)進(jìn)行測(cè)試
大量使用靜態(tài)類(lèi)和方法,為了更簡(jiǎn)單及更好的性能
編碼過(guò)程非常簡(jiǎn)單,因?yàn)閺?fù)雜的部分被打包到庫(kù)里,這些庫(kù)被開(kāi)源和維護(hù)。.Net 項(xiàng)目數(shù)量很低,因?yàn)槭褂昧松鐓^(qū)共享的部分代碼。
開(kāi)發(fā)者同時(shí)使用 2 到 3 個(gè)顯示器,多個(gè)屏幕可以顯著提高生產(chǎn)效率。
緩存
緩存一切
5 個(gè)等級(jí)的緩存
1 級(jí)是網(wǎng)絡(luò)級(jí)緩存,緩存在瀏覽器、CDN 以及代理服務(wù)器中。
2 級(jí)由 .Net 框架 HttpRuntime.Cache 完成,在每臺(tái)服務(wù)器的內(nèi)存中。
3 級(jí) Redis,分布式內(nèi)存鍵值存儲(chǔ),在多個(gè)支撐同一個(gè)站點(diǎn)的服務(wù)器上共享緩存項(xiàng)。
4 級(jí) SQL Server Cache,整個(gè)數(shù)據(jù)庫(kù),所有數(shù)據(jù)都被放到內(nèi)存中。
5 級(jí) SSD。通常只在 SQL Server 預(yù)熱后才生效。
舉個(gè)例子,每個(gè)幫助頁(yè)面都進(jìn)行了緩存,訪問(wèn)一個(gè)頁(yè)面的代碼非常簡(jiǎn)單:
使用了靜態(tài)的方法和類(lèi)。從 OOP 角度來(lái)看確實(shí)很糟,但是非??觳⒂欣诤?jiǎn)潔編碼。
緩存由 Redis 和 Dapper 支撐,一個(gè)微型 ORM
為了解決垃圾收集問(wèn)題,模板中 1 個(gè)類(lèi)只使用 1 個(gè)副本,被建立和保存在緩存中。監(jiān)測(cè)一切,包括 GC 操。據(jù)統(tǒng)計(jì)顯示,間接層增加 GC 壓力達(dá)到了某個(gè)程度時(shí)會(huì)顯著的降低性能。
CDN Hit 。鑒于查詢(xún)字符串基于文件內(nèi)容進(jìn)行哈希,只在有新建立時(shí)才會(huì)被再次取出。每天 3000 萬(wàn)到 5000 萬(wàn) Hit,帶寬大約為 300GB 到 600GB。
CDN 不是用來(lái)應(yīng)對(duì) CPU 或I/O負(fù)載,而是幫助用戶(hù)更快的獲得答案
部署
每天 5 次部署,不去建立過(guò)大的應(yīng)用。主要因?yàn)?br />可以直接的監(jiān)視性能
盡可能最小化建立,可以工作才是重點(diǎn)
產(chǎn)品建立后再通過(guò)強(qiáng)大的腳本拷貝到各個(gè)網(wǎng)頁(yè)層,每個(gè)服務(wù)器的步驟是:
通過(guò) POST 通知 HAProxy 下架某臺(tái)服務(wù)器
延遲 IIS 結(jié)束現(xiàn)有請(qǐng)求(大約 5 秒)
停止網(wǎng)站(通過(guò)同一個(gè) PSSession 結(jié)束所有下游)
Robocopy 文件
開(kāi)啟網(wǎng)站
通過(guò)另一個(gè) POST 做 HAProxy Re-enable
幾乎所有部署都是通過(guò) puppet 或 DSC,升級(jí)通常只是大幅度調(diào)整 RAID 陣列并通過(guò) PXE boot 安裝,這樣做非??焖?。
協(xié)作
團(tuán)隊(duì)
SRE (System Reliability Engineering):5 人
Core Dev(QA site)6-7 人
Core Dev Mobile:6 人
Careers 團(tuán)隊(duì)專(zhuān)門(mén)負(fù)責(zé) SO Careers 產(chǎn)品開(kāi)發(fā):7 人
Devops 和開(kāi)發(fā)者結(jié)合的非常緊密
團(tuán)隊(duì)間變化很大
大部分員工遠(yuǎn)程工作
辦公室主要用于銷(xiāo)售,Denver 和 London 除外
一切平等,些許偏向紐約工作者,因?yàn)槊鎸?duì)面有助于工作交流,但是在線工作影響也并不大
對(duì)比可以在同一個(gè)辦公室辦公,他們更偏向熱愛(ài)產(chǎn)品及有才華的工程師,他們可以很好的衡量利弊
許多人因?yàn)榧彝ザx擇遠(yuǎn)程工作,紐約是不錯(cuò),但是生活并不寬松
辦公室設(shè)立在曼哈頓,那是個(gè)人才的誕生地。數(shù)據(jù)中心不能太偏,因?yàn)榻?jīng)常會(huì)涉及升級(jí)
打造一個(gè)強(qiáng)大團(tuán)隊(duì),偏愛(ài)極客。早期的微軟就聚集了大量極客,因此他們征服了整個(gè)世界
Stack Overflow 社區(qū)也是個(gè)招聘的地點(diǎn),他們?cè)谀菍ふ覠釔?ài)編碼、樂(lè)于助人及熱愛(ài)交流的人才。
編制預(yù)算
預(yù)算是項(xiàng)目的基礎(chǔ)。錢(qián)只花在為新項(xiàng)目建立基礎(chǔ)設(shè)施上,如此低利用率的 web server 還是 3 年前數(shù)據(jù)中心建立時(shí)購(gòu)入。
測(cè)試
快速迭代和遺棄
許多測(cè)試都是發(fā)布隊(duì)伍完成的。開(kāi)發(fā)擁有一個(gè)同樣的 SQL 服務(wù)器,并且運(yùn)行在相同的 Web 層,因此性能測(cè)試并不會(huì)糟糕。
非常少的測(cè)試。Stack Overflow 并沒(méi)有進(jìn)行太多的單元測(cè)試,因?yàn)樗麄兪褂昧舜罅康撵o態(tài)代碼,還有一個(gè)非?;钴S的社區(qū)。
基礎(chǔ)設(shè)施改變。鑒于所有東西都有雙份,所以每個(gè)舊配置都有備份,并使用了一個(gè)快速故障恢復(fù)機(jī)制。比如,keepalived 可以在負(fù)載均衡器中快速回退。
對(duì)比定期維護(hù),他們更愿意依賴(lài)冗余系統(tǒng)。SQL 備份用一個(gè)專(zhuān)門(mén)的服務(wù)器進(jìn)行測(cè)試,只為了可以重存儲(chǔ)。計(jì)劃做每?jī)蓚€(gè)月一次的全數(shù)據(jù)中心故障恢復(fù),或者使用完全只讀的第二數(shù)據(jù)中心。
每次新功能發(fā)布都做單元測(cè)試、集成測(cè)試盒 UI 測(cè)試,這就意味著可以預(yù)知輸入的產(chǎn)品功能測(cè)試后就會(huì)推送到孵化網(wǎng)站,即 meta.stackexchange(原 meta.stackoverflow)。
監(jiān)視/日志
當(dāng)下正在考慮使用 http://logstash.net/做日志管理,目前使用了一個(gè)專(zhuān)門(mén)的服務(wù)將 syslog UDP 傳輸?shù)?SQL 數(shù)據(jù)庫(kù)中。網(wǎng)頁(yè)中為計(jì)時(shí)添加 header,這樣就可以通過(guò) HAProxy 來(lái)捕獲并且融合到 syslog 傳輸中。
Opserver 和 Realog 用于顯示測(cè)量結(jié)果。Realog 是一個(gè)日志展示系統(tǒng),由 Kyle Brandt 和 Matt Jibson 使用 Go 建立。
日志通過(guò) HAProxy 負(fù)載均衡器借助 syslog 完成,而不是 IIS,因?yàn)槠涔δ鼙?IIS 更豐富。
關(guān)于云
還是老生常談,硬件永遠(yuǎn)比開(kāi)發(fā)者和有效率的代碼便宜。基于木桶效應(yīng),速度肯定受限于某個(gè)短板,現(xiàn)有的云服務(wù)基本上都存在容量和性能限制。
如果從開(kāi)始就使用云來(lái)建設(shè) SO 說(shuō)不定也會(huì)達(dá)到現(xiàn)在的水準(zhǔn)。但毫無(wú)疑問(wèn)的是,如果達(dá)到同樣的性能,使用云的成本將遠(yuǎn)遠(yuǎn)高于自建數(shù)據(jù)中心。
性能至上
StackOverflow 是個(gè)重度的性能控,主頁(yè)加載的時(shí)間永遠(yuǎn)控制在 50 毫秒內(nèi),當(dāng)下的響應(yīng)時(shí)間是 28 毫秒。
程序員熱衷于降低頁(yè)面加載時(shí)間以及提高用戶(hù)體驗(yàn)。
每個(gè)獨(dú)立的網(wǎng)絡(luò)提交都予以計(jì)時(shí)和記錄,這種計(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。這可以帶來(lái) 3 個(gè)好處:給升級(jí)留下很大的空間;在嚴(yán)重錯(cuò)誤發(fā)生時(shí)可以保持服務(wù)可用;在需要時(shí)可以快速回檔。我更愿意把Stack Overflow看作是能夠運(yùn)行于大規(guī)模數(shù)據(jù)下,但本身并不算大規(guī)模的(running with scale but not at scale)。意思是我們的網(wǎng)站非常有效率,但至少目前為止,我們的規(guī)模還不夠“大”。讓我們通過(guò)一些數(shù)字來(lái)介紹Stack Overflow當(dāng)前是一個(gè)怎樣的規(guī)模吧。以下是一些核心的數(shù)字,來(lái)自于不久前在一整天(24小時(shí))內(nèi)的統(tǒng)計(jì),準(zhǔn)確說(shuō)是2013年11月12日。這是一個(gè)典型的工作日,并且只統(tǒng)計(jì)了我們活動(dòng)的數(shù)據(jù)中心,也就是我們自己的服務(wù)器。那些對(duì)CDN節(jié)點(diǎn)的請(qǐng)求和流量被排除在外,因?yàn)樗鼈儾⒉恢苯釉L問(wèn)我們的網(wǎng)絡(luò)。
負(fù)載均衡器接受了148,084,833次HTTP請(qǐng)求
其中36,095,312次是加載頁(yè)面
833,992,982,627 bytes (776 GB) 的HTTP流量用于發(fā)送
總共接收了286,574,644,032 bytes (267 GB) 數(shù)據(jù)
總共發(fā)送了1,125,992,557,312 bytes (1,048 GB) 數(shù)據(jù)
334,572,103次SQL查詢(xún)(僅包含來(lái)自于HTTP請(qǐng)求的)
412,865,051次Redis請(qǐng)求
3,603,418次標(biāo)簽引擎請(qǐng)求
耗時(shí)558,224,585 ms (155 hours) 在SQL查詢(xún)上
耗時(shí)99,346,916 ms (27 hours) 在Redis請(qǐng)求上
耗時(shí)132,384,059 ms (36 hours) 在標(biāo)簽引擎請(qǐng)求上
耗時(shí)2,728,177,045 ms (757 hours) 在ASP.Net程序處理上
(我覺(jué)得應(yīng)該發(fā)表一篇文章介紹我們?nèi)绾慰焖俨杉@些數(shù)據(jù),以及為什么值得耗費(fèi)精力去獲取它們)
注意以上數(shù)字包括了整個(gè)Stack Exchange網(wǎng)絡(luò),但那并不是我們?nèi)康摹3酥?,這些數(shù)字也僅僅來(lái)自于我們?yōu)榱藱z測(cè)性能而記錄的HTTP請(qǐng)求。“哇,一天有這么多個(gè)小時(shí),你們?cè)趺醋龅降模?rdquo;我們把這叫做魔法,當(dāng)然有些人喜歡說(shuō)成“許多個(gè)有多核處理器的服務(wù)器”,但我們依然堅(jiān)持這是魔法。以下是那個(gè)數(shù)據(jù)中心里運(yùn)行Stack Exchange網(wǎng)絡(luò)的設(shè)備:
4個(gè)MS SQL 服務(wù)器
11個(gè)IIS服務(wù)器
2個(gè)Redis服務(wù)器
3個(gè)標(biāo)簽引擎服務(wù)(任何針對(duì)標(biāo)簽的請(qǐng)求都會(huì)訪問(wèn)它們,比如/questions/tagged/c++)
3個(gè)ElasticSearch服務(wù)器
2個(gè)負(fù)載均衡器(HAProxy)
2個(gè)交換機(jī)(Nexus 5596和Fabric Extenders)
2個(gè)Cisco 5525-X ASA (可看作是防火墻)
2個(gè)Cisco 3945 Router
有圖有真相:

我們不僅僅運(yùn)行網(wǎng)站,旁邊架子上還有一些運(yùn)行著虛擬機(jī)的服務(wù)器和其他設(shè)備,它們并不直接服務(wù)于網(wǎng)站,而是進(jìn)行部署、域名控制、監(jiān)控、操作數(shù)據(jù)庫(kù)等其他工作。上面列表中的兩個(gè)數(shù)據(jù)庫(kù)服務(wù)器之前一直都是用作備份,直到最近才作為只讀的負(fù)載(主要用于Stack Exchange API),于是我們可以不需要太多考慮便繼續(xù)擴(kuò)大規(guī)模了。Web服務(wù)器有兩個(gè)分別用于開(kāi)發(fā)和存儲(chǔ)元數(shù)據(jù),運(yùn)行負(fù)載非常低。
讓我們?cè)賮?lái)總結(jié)一下:
核心設(shè)備
如果除去那些多余的設(shè)備,以下是Stack Exchange運(yùn)行需要的(保持目前的性能水平):
2個(gè)MS SQL服務(wù)器(Stack Overflow在一臺(tái),其他的在另一臺(tái),實(shí)際上只需一臺(tái)機(jī)器運(yùn)行還能有富余)
2個(gè)Web服務(wù)器(或許3個(gè)吧,不過(guò)我有信心2個(gè)足矣)
1個(gè)Redis服務(wù)器
1個(gè)標(biāo)簽引擎服務(wù)器
1個(gè)ElasticSearch服務(wù)器
1個(gè)負(fù)載均衡器
1個(gè)交換機(jī)
1個(gè)ASA
1個(gè)路由器
(我們真該找個(gè)機(jī)會(huì)嘗試這個(gè)配置,關(guān)閉部分設(shè)備,看看極限在哪)
現(xiàn)在還有一些虛擬機(jī)運(yùn)行在后臺(tái),執(zhí)行一些輔助功能,比如域名控制等等。但那都是些相當(dāng)?shù)拓?fù)載的任務(wù),我們就不做討論了,這里把重心放在Stack Overflow本身,看看它是怎樣全速加載出頁(yè)面的。如果你希望更精確全面,可以增加一個(gè)VMware虛擬機(jī)進(jìn)來(lái),用于執(zhí)行所有的輔助工作。這樣看來(lái)并不需要很多機(jī)器,但是這些機(jī)器的規(guī)格通常在云上難以實(shí)現(xiàn),除非你有足夠多的錢(qián)。以下是這些“增強(qiáng)型”服務(wù)器簡(jiǎn)要的配置介紹:
數(shù)據(jù)庫(kù)服務(wù)器有384GB內(nèi)存和1.8TB的SSD硬盤(pán)
Redis服務(wù)器有96GB內(nèi)存
ElasticSearch服務(wù)器有196GB內(nèi)存
標(biāo)簽引擎服務(wù)器有著我們能買(mǎi)得起的最快的處理器
交換機(jī)每個(gè)端口有10Gb的帶寬
Web服務(wù)器不是很特別,有32GB內(nèi)存、2個(gè)4核處理器和300GB的SSD硬盤(pán)
有些服務(wù)器有2個(gè)10Gb帶寬的接口(比如數(shù)據(jù)庫(kù)),其他有4個(gè)1Gb帶寬的
20Gb的帶寬太多余了?你還真特么說(shuō)對(duì)了,活動(dòng)的數(shù)據(jù)庫(kù)服務(wù)器平均只利用了20Gb通道中的100-200Mb。然而,像備份、重建等等操作,根據(jù)當(dāng)前內(nèi)存和SSD硬盤(pán)的情況,可以使帶寬完全飽和,所以說(shuō)這樣設(shè)計(jì)還是有意義的。
存儲(chǔ)設(shè)備
我們目前有大約2TB的數(shù)據(jù)庫(kù)存儲(chǔ)(第一個(gè)集群有18塊SSD硬盤(pán)—— 總共1.63TB,使用1.06TB;第二個(gè)集群由4塊SSD硬盤(pán)組成—— 總共1.45TB,使用889GB),這是我們?cè)谠品?wù)器上需要的(嗯哼,又要吐槽價(jià)格了吧),請(qǐng)記住這全部都是SSD硬盤(pán)。歸功于存儲(chǔ)器良好的表現(xiàn),我們數(shù)據(jù)庫(kù)的平均寫(xiě)入時(shí)間是0毫秒,甚至超出我們能度量的精度了。算上內(nèi)存中的數(shù)據(jù)以及兩級(jí)緩存,Stack Overflow中實(shí)際的數(shù)據(jù)庫(kù)讀寫(xiě)比例是40:60。你沒(méi)看錯(cuò),60%是寫(xiě)操作(點(diǎn)此了解讀寫(xiě)比)。此外,每個(gè)Web服務(wù)器都有兩塊320GB SSD硬盤(pán)組成的RAID1。ElasticSearch在每個(gè)區(qū)塊大約需要300GB的容量,由于我們會(huì)非常頻繁的寫(xiě)入或重建索引,SSD硬盤(pán)在這里是更好的選擇。
值得注意的是我們擁有一個(gè)SAN(存儲(chǔ)區(qū)域網(wǎng)絡(luò))連接到核心網(wǎng)絡(luò),那就是 Equal Logic PS6110X,它有24個(gè)可熱交換的10K SAS磁盤(pán)和2個(gè)10Gb的控制器。這個(gè)設(shè)備僅僅被VM服務(wù)器用作共享儲(chǔ)存空間以保證虛擬機(jī)高度的可用性,但并不實(shí)際支撐網(wǎng)站的運(yùn)行。換句話說(shuō),如果SAN掛掉了,在一段時(shí)間內(nèi)網(wǎng)站甚至無(wú)法察覺(jué)(只有虛擬機(jī)中的域名控制器能感知到)。
整合到一起
這所有的設(shè)備在一起是為了什么?性能。我們需要很高的性能,這是一個(gè)對(duì)我們來(lái)說(shuō)很重要的特性。所有站點(diǎn)的首頁(yè)都是問(wèn)題頁(yè)面,我們內(nèi)部把它親切地稱(chēng)作Question/Show(路由的名字)。在11月12日,這個(gè)頁(yè)面平均渲染時(shí)間是28毫秒,而我們的要求是至多50ms。為了使用戶(hù)獲得更好的體驗(yàn),我們盡一切可能縮短頁(yè)面加載的時(shí)間,哪怕只有一毫秒。在和性能有關(guān)的問(wèn)題上,我們所有的開(kāi)發(fā)人員都是“錙銖必較”的,這也有助于我們的網(wǎng)站保持快速響應(yīng)。以下是一些Stack Overflow上熱門(mén)頁(yè)面的平均渲染時(shí)間,數(shù)據(jù)還是來(lái)自于前面統(tǒng)計(jì)的那24小時(shí):
Question/Show: 28 ms (2970萬(wàn)次點(diǎn)擊)
User Profiles: 39 ms (170萬(wàn)次點(diǎn)擊)
Question List: 78 ms (110萬(wàn)次點(diǎn)擊)
Home page: 65 ms (100萬(wàn)次點(diǎn)擊) (這對(duì)我們來(lái)說(shuō)已經(jīng)很慢了,Kevin Montrose正在著手修復(fù)這個(gè)問(wèn)題)
憑借對(duì)每一次請(qǐng)求的時(shí)間線的記錄,我們能夠準(zhǔn)確觀察到頁(yè)面加載的過(guò)程。我們需要這樣的數(shù)據(jù),否則難道靠腦補(bǔ)來(lái)做決定嗎?有數(shù)據(jù)在手,我們就可以這樣監(jiān)控性能:

如果你對(duì)某個(gè)特定頁(yè)面的數(shù)據(jù)感興趣,我也很樂(lè)意發(fā)布出來(lái)。但這里我重點(diǎn)關(guān)注渲染時(shí)間,因?yàn)樗硎疚覀兊姆?wù)器需要多久來(lái)生成一個(gè)網(wǎng)頁(yè)。網(wǎng)絡(luò)傳輸速度是一個(gè)完全不同的話題了(盡管不得不承認(rèn)它也有很大的關(guān)系),不過(guò)將來(lái)我會(huì)講到的。
增長(zhǎng)空間
非常值得一提的是我們這些服務(wù)器運(yùn)行時(shí)的使用率都非常低。比如Web服務(wù)器的CPU平均使用率為5-15%,內(nèi)存只使用了15.5GB,網(wǎng)絡(luò)流量只有20-40Mb/s;而數(shù)據(jù)庫(kù)服務(wù)器CPU平均使用率為5-10%,使用了365GB內(nèi)存,以及100-200Mb/s的網(wǎng)絡(luò)。這使我們能做到幾件重要的事情:在網(wǎng)站規(guī)模增大時(shí)不至于需要馬上升級(jí)設(shè)備;當(dāng)出現(xiàn)問(wèn)題時(shí)(錯(cuò)誤的查詢(xún)、代碼以及攻擊等等,無(wú)論是什么樣的問(wèn)題),我們能保持網(wǎng)站始終不掛;在必要的時(shí)候降低功耗。這里有個(gè)我們Web層的監(jiān)控項(xiàng)目:

利用率如此之低的主要原因是高效的代碼。盡管本文的主題并不是這個(gè),但是高效的代碼對(duì)挖掘服務(wù)器的性能也有著決定性的作用。做一件非必要的事情所損失的,居然比無(wú)所作為還要多——把這引申到代碼中就是說(shuō),你需要把它們改進(jìn)得更高效了。這些損失或者消耗可以是能源、硬件(你需要更多更快的服務(wù)器)、開(kāi)發(fā)人員理解代碼更困難(平心而論,這個(gè)有兩面性,高效的代碼并不一定那么簡(jiǎn)單),以及緩慢的頁(yè)面渲染——可能導(dǎo)致用戶(hù)更少地瀏覽網(wǎng)站其他頁(yè)面甚至再也不訪問(wèn)你的網(wǎng)站了。低效率代碼帶來(lái)的損失可能比你想象的大很多。