高性能Web應用打造攻略:擴展過程中20個最大的絆腳石
Sean Hull是Heavyweight Internet Group的創始人兼高級顧問,擁有20年以上技術顧問相關經驗,曾為多家知名機構提供咨詢,其中包括The Hollywood Reporter、Billboard、NBC Universal、Zagats、Rent the Runway及ideeli,這些高速增長的公司每個月處理接近1億的獨立訪客。Hull在Amazon EC2部署和Linux及MySQL性能上有著豐富的經驗。巨大流量的處理需涉及多個方面,其中包括網站擴展性、業務連續性、安全及架構挑戰等。近日,Hull分享了高性能Web應用打造的經驗,剖析了擴展之路上20個最大的絆腳石。以下為譯文:
1. 二階段提交
通常情況下,當數據庫中的數據發生改變時,它需要同時被寫入內存和硬盤。當一個提交發生時,傳統數據庫需要負責數據在真實存儲媒質上的持久化。牢記,內存的數據在崩潰或者重啟后都會消失。即使數據已經在數據庫中緩存,數據庫仍然需要將其持久化到磁盤上,MySQL的二進制日志及Oracle的redo日志都符合這個要求。
通過MySQL集群或者類似DRBD(Distributed Replicated Block Device)或者Amazon Multi-AZ(Multi-Availability Zone)的分布式文件系統,提交并不僅僅是在本地發生,同時也在遠端。二階段提交意味著需要從遠端得到反饋,鑒于網絡及一些其它的延時,提交速度可能會被毫秒級的降低,仿佛高速公路上的所有汽車都因重載變慢。如有考慮使用Multi-AZ或者讀備份,不妨查看Amazon RDS(Relational Database Service)與MySQL的比較。
同步復制同樣會出現這些問題,因此MySQL解決方案是半同步的,這也可以看做是在二階段提交上的一些讓步。
2. 緩存不足
在所有層中緩存的作用都至關重要,那么什么地方最需要使用緩存:瀏覽器、頁面、對象還是數據庫存?下面不妨統統試一下。
瀏覽器緩存似乎遙不可及,除非你清楚瀏覽器是從Web Server中取出指令及其渲染的頁面。因此,如果包含的對象有一個比較長的生命周期,瀏覽器將會緩存他們,不需要再次進行讀取。這不僅會加快用戶的瀏覽速度,同樣會有益于Server對網站的托管,因為會切實的減少用戶的二次讀取。
猛戳這里查看更多瀏覽器緩存相關,確保設置了期滿頭文件及緩存控制。
頁面緩存需要使用類似Varnish的技術,可以將它看成一個迷你的高速、低開銷Web Server。它不可以支撐類似Apache可以的復雜頁面,但是它可以讓非常簡單的頁面更快。因此位于Apache之前,用于減少負載,讓Apache可以處理更加復雜的頁面。就像交通警察,在專注更復雜的機動車前,先給自行車放行。
對象緩存由類似memcache的組件完成,可以把它當做是應用程序的便利貼。做數據庫查詢時會先訪問緩存尋求數據,如果發現了所需的數據,那么結果返回的時間將比訪問數據庫快10-100倍,這樣就可以快速的構建頁面,從而在眨眼間為用戶呈現。如果它沒有發現所需的數據,或者只發現了一部分,那么將會建立數據庫請求并將返回的數據放于緩存中,讓更多的后來者受益。
3. 緩慢的磁盤I/O、RAID 5、多租戶存儲
數據庫中的一切都受到存儲的限制,這里既包含了空間問題,也受設備讀寫的速度掣肘。
如果你使用實體服務器,那么一定要當心RAID 5,獨立磁盤冗余陣列的一種,數據保護和奇偶性將嚴重的影響寫操作。同時,如果其中一個磁盤損壞,那么這個陣列在磁盤重建時將變得非常緩慢。
這個方案一般使用RAID 10,它將為你提供獨立的鏡像集。這導致沒有奇偶校驗計算,從而不會影響重建時的速度。
云環境可能會涉及到類似于Amazon EBS(Elastic Block Store,一個類似于SAN的虛擬化磁盤)的技術。鑒于其基于網絡,你必須與其它租戶競爭存儲的讀寫。因為每個存儲能支撐的讀寫速度是固定的,所以你的鄰居可能會影響到你網站及應用程序的讀寫速度。
最近,Amazon又公布了一個重量級產品Provisioned IOPS(每秒I/O操作)。對于技術專家來說看起來非常不錯,但是對于其它人來說毫無價值。盡管如此,其依然重要,這意味著你可以鎖定你數據庫所需的磁盤性能。如果你想在Amazon上托管數據庫,那么不妨多關注一下這個。
4. 串行處理
在超市結賬時,如果有10個收銀臺開放,那么從事的是并行處理。如果每個收銀臺都在休息,只有一個登記處開放,那么從事的是串行操作。那么結賬的隊伍將變得非常長,不僅是結賬人的心情受到影響,購物者也同樣如此。這點經常發生在線路不夠的大橋收費站以及許多人一起離場的體育場所。
網絡應用需要嚴格的避免串行,因等待API調用而產生的阻塞,所有的后端節點都在等待一個搜索服務器,只要你應用程序的某處形成一個線就代表了串行化的發生,那么必須不惜一切代價去清除它。
5. 缺少Feature Flag
在給業務部門建立應用時,開發者一般從特性和功能入手。Feature Flag將至關重要,它讓人們可以通過后端配置文件或管理員UI頁面關閉或者打開特性。
為什么他們如此重要?如果你有早上4點的救火經驗,那么你將明白啟動應急計劃的必要性。你需要可以關閉評級、評論以及應用程序其它的一些特性,這將不會導致整個系統崩潰。更重要的是,在新特性發布時,有些時候問題并不明顯,直到一群互聯網用戶同時涌入。Feature Flag讓你可以選擇性的關閉一些功能,而不是關掉整個網站。
6. 單一的數據庫副本
你必須使用一個以上的讀副本或者MySQL從節點,這允許在主節點出問題時的快速故障轉移。
擁有數據庫的多個副本意味著橫向擴展。如果你有兩個,你就會看到3-4個會對你基礎設施的提升。
7. 使用數據庫進行排隊
MySQL數據庫非常適合儲存表格、數據以及他們之間的關系,不幸的是,它并不適合處理應用程序的隊列。盡管如此,許多開發者都習慣使用一個表格來達到這個目的。比如,讓你的應用程序包含一些作業表格,或者一個狀態列,擁有一些類似“in-process”、“in-queue” 及“finished”的值。
因為鎖競爭、scan及 poll進程將造成更多的工作,這些解決方案會帶來一些擴展性問題,它們將會顯著的降低數據庫速度。幸運的是這里存在很多有效的開源解決方案,比如RabbitMQ及Amazon的SQS。
8. 使用一個做全文查詢的數據庫
頁面搜索是掣肘應用程序的另一個方面,盡管MySQL一段時間也支撐了full-text索引。但是它們只對MyISAM表格有效,其它類型表格都不買賬,一直困擾著開發者。
一個解決方案是使用類似Solr的專業搜索引擎,這些技術擁有許多很好的庫去支撐你的編程語言,同時會提供一個更快的搜索速度。這些方案同樣易于擴展,并且不會讓你的數據庫成為累贅。
替代方案是Sphinx SE,一個MySQL存儲引擎,將Sphinx服務器整合進數據庫。此外,MySQL 5.6版本中還使用了InnoDB做全文搜索的默認搜索引擎。
9. 對象關系模型
ORM,就像毒藥一樣,一旦進行使用,就很難擺脫。有利的一面是ORM有益于快速原型,并且允許非專家級MySQL開發者使用對象或者內存模式進行讀寫訪問。如果你不進行擴展,那么他們的速度將非常快,并讓功能的快速交付得到保障。
然后DBA(數據庫管理員)會因為緩慢且丑陋的查詢來到開發團隊并且說:“這個查詢在應用程序什么地方,我們需要進行修復,它需要被重寫。”開發團隊則會說:“我們也不知道!”從而會收到來自運營組一些質疑的眼神。
有能力對bad sql進行跟蹤并將其指出是非常重要的。DBA團隊需要一定的指引找到bad sql的源頭。如果查詢是來自ORM,他們并不具備這些指引。這樣你的團隊將面對一個巨大的技術負債,同樣也是個非常難以解決的挑戰。
10. 缺少Instrumentation
Instrumentation(儀表盤)為應用程序提供了碼表和油表,這是汽車不可缺少的兩個部分。他們提供了應用程序的內部工作信息:他們記錄了時間信息,并且提供了應用程序耗時最多的環節。
一個非常人氣的網絡解決方案就是New Relic,它提供了一個非常全面的可視化界面,針對各個領域工作者——項目經理、開發者、運營團隊,甚至是業務部門都可以從圖中發現問題所在。當然,現在已經有很多的開源Instrumentation。
11. 缺少代碼倉庫及版本管理
盡管現在這個問題已非常少見,但是還有有些互聯網公司在沒有版本控制下去建立應用程序。當然那些使用了的人,很清楚它將給團隊帶來的日常優勢和組織控制。
如果你沒有使用,隨著應用程序變得更加復雜,你將被卷入技術負債的漩渦,為應用不同部分添加員工將變得異常困難。
一旦你使用版本控制,確保囊括了所有的組件,包含配置文件以及其它要素。而丟失部署時需要用到的部分,將帶來額外的風險。
12. 單點故障
如果你的數據只存在一個主數據庫上,這就是一個單點故障。如果你的服務器位于一個單獨的磁盤上,這也是個單點故障。單點故障可以比作“阿基里斯之踵”技術用語。
不惜任何代價,單點故障都要在應用程序中移除。麻煩的是如何去認識單點故障,即使是選擇單一的云供應商都可以稱為單點故障,如果擁有多個供應商,或者使用Amazon不同的部分,那么AirBNB的服務將幸免Amazon 2012年10月的部分宕機。
13. 缺乏只瀏覽模式
如果你深夜在Yelp、Facebook或者是Tumblr上發布評論,你可能就會收到這樣的信息:“該特性不可用,請稍后再試”。稍后可能是50或者60分鐘,也許可能還需多試幾次。對于非技術用戶,他們仍然感覺網站在正常運行,只是奇怪了一點而已。
實際情況是,應用程序只允許你去瀏覽網站,但是不可以做任何改變。可能是主數據庫或者是一些存儲組件不可用了。
只瀏覽模式可以通過保留主數據庫的多個讀備份來實現,使用MySQL副本或者是Amazon讀副本類似途徑。這樣就可以在沒有主數據庫情況下,保留網站全部瀏覽功能正常,這一點非常重要。
14. 脆弱的溝通
在擴展上談溝通可能非常奇怪,但是應用程序技術層不可能因為團隊間社交和文化上的差異分離。
強大的通信線路是必要的,隊員必須要知道在錯誤發生時將找誰。好的交流需要自信及知識淵博的領導,廣泛的聽取意見并加以改善。
15. 缺少文檔
當Web應用程序包含許多層時,文檔至關重要。開發者需要給程序、功能、頁面做文檔,讓后來者看代碼時可以清晰的發現所需的提示及見解。當有程序發生故障時,運營程序需要在配置文件上添加評論去表明更改歷史。業務流程及關系同樣應該出現在公司的wiki里,讓人們發現自己問題的解決方案。
文檔可以在各個層次起到幫助作用,每個人都應該擁抱這個愛好。
16 .缺少應急演習
應急通常情況下總會被忽視,團隊可能會有“我們覆蓋的地方都有備份”這類的說辭。不錯備份、恢復的途徑很通吃,關鍵是確保備份的文件萬無一失。一旦在故障轉移時發現備份有缺失,那么你不愿意發生的事情必然發生。
應急演習讓事情發生之前有了相應的經驗,公司應該將此作為運營團隊工作的一部分,每年都需要進行幾次。在云時代,應急演習已經變得比以前輕松許多。為保證所有組件都被備份而啟動服務器是非常值得的,在這個過程中你將學到這項操作該持續多長時間,以及困難所在的地方,更知道需要小心些什么。
17. 缺少監視和指標
監視有著與版本控制同樣的重要性:非常基本,缺少它工作將無法進行;然而確實有一些網站沒有監視,或者是監視力度不夠——有些服務器或者是核心組件并未被監視。
不間斷的收集系統及服務器數據,同時,應用程序和業務級別的可用性也同等重要。如果你不想親自動手,不妨考慮一個Web服務方案。
18. 莽撞的運營
你騎著馬在街道上飛奔,明著帶槍進入酒吧,你認為這是在交朋友?肯定不是,你在暴力的脅迫別人遵循,所有人都不會有歸屬感。強大的信心是好的,但是團隊的合作是不可缺少的,團隊的智慧高于任何個人。
團隊需要不斷的對改變做交流,用管理的角度進行,準備好應對失敗等等。采取小心及規避風險的態度,永遠都有備用計劃,你應該有能力撤銷所有改變,并且認真對待破壞性的改變,以及那些不可恢復的地方。
19. 日益增長的技術負債
隨著應用不斷的增長,隊伍需要花越來越多的時間在老代碼的維護和支持上,消除漏洞及缺陷。因此,花在新特性上的時間將越來越少。必須謹慎的維護好新特性與技術負債上投入時間的平衡。如果你發現你的技術負債增加,可能就是橫下決心重寫的時候了。重寫可能會影響到短期新特性及客戶特性的開發,但是從長遠的角度上看是非常好的。
技術負債并不總是能被輕易的認知,在你建立特性或者是修復bug時,你更習慣于聚焦局部的細節,非常容易失去全局觀,這也是為什么全面人才更益于Web擴展的原因。
20. 不足的日志
日志直接影響到度量及監視。你在尋找故障和排錯時肯定希望得到更多的日志,但是在一個不間斷運行的情況下,你同樣會需要一些關鍵服務的日志。服務器系統日志、Apache及MySQL日志、緩存日志等,這些都需要被保存。而在日志太多時,你總是會減少一些地方的日志,或者裁剪及循環日志文件,從而丟棄舊的。