讀書筆記--大規模web服務開發技術


總評     
     這本書是日本一個叫hatena的大型網站的CTO寫的,通過hatena網站從小到大的演進來反應一個web系統從小到大過程中的各種系統和技術架構變遷,比較接地氣。
     書的內容不是很難,所以總的來說比較容易閱讀,不需要特別累的啃,可想而知,不是非常深入的,更多的還是把作者的一些經驗寫出來,hatena這種量級的在國內應該是一個中型網站的水平,作者基本把這個量級web服務的運維的方方面面都講了一遍,看完可以對這個這種量級網站有一個總體的了解,個人認為還是值得一讀的。
 
逐章讀書筆記:  
第一章 大規模web服務的開發定位
     先給出這個網站的一些具體數據,好有個接地氣的感覺,作者寫作這本書的時候(2009年),hatena網站的大略規模數據是:
  •      注冊用戶100w以上
  •      1500w uv每月
  •      幾十億pv每月
  •      繁忙時流量430mbps
  •      服務器500台以上
  •      員工數量50人左右
     作者的定義是,幾百一千台左右服務器的web系統是一個大型web系統,上萬台的服務系統是超大型web系統。
第二章 大規模數據處理入門
     介紹了hatena的數據規模,最大的表數據是三億五千萬條記錄,其他表記錄是千萬級別。
     大規模數據處理的難點是:數據無法在內存中計算,但是相對內存來說,磁盤又是很慢的,速度差異是10^5到10^6倍。另外內存、磁盤和CPU連接的總線速度也有差異,書里給的一個數據是內存的總線速度能達到7.5G/秒,但是磁盤的總線速度只有58MB/秒,相差兩個數量級。
     web服務的擴展性:web服務的兩種擴展方式:縱向擴展(scale up)--通過購買昂貴的高速硬件提升單機性能;橫向擴展(scale out)--通過組合大量廉價低性能的硬件來提升性能。對於web服務而言,明顯是后者更合適。
     web服務擴展的兩個要點--cpu負載擴展和IO負載擴展,cpu只對請求做計算,所有容易擴展,只需要橫向加機器然后前端負載均衡分攤到各個機器即可,所以cpu負載容易擴展;而IO負載典型的是數據庫,由於擴展數據庫會存在數據分散、復制以及一致性等問題 ,所以IO負載很難擴展,不能通過簡單的加機器來解決。
     介紹了linux的sar命令,可以方便查看服務器的CPU和IO的負載情況。
第三章 操作系統的緩存和分布式
     由於磁盤和內存速度差距非常大,所以操作系統本身提供一些緩存機制來盡量減少磁盤讀取,提升性能。
     操作系統的頁面緩存:這塊講的不是很清楚,需要找操作系統相關資料詳細了解。
     降低IO負載的策略:
     1. 緩存:緩存對於降低IO負載的效果無疑是很明顯的。作者提到一個有趣的細節:是增加內存實惠還是投入人員開發一個更高壓縮率的壓縮算法實惠?作者給的說法是目前(2010年)內存硬件16G一下都是     很便宜的,超過16G甚至32G之后成本會陡增,所以如果你的數據明顯16G內能夠吃的住,就沒必要投入大量人力研發算法,反之就需要人才投入來研發算法。
     2.單台緩存不夠了,擴展多台:不過這里的擴展多台和應用服務器的簡單復制式的擴展又不一樣,給IO用的換成的擴展不是那種“簡單增加機器就行”,需要考慮合理的分布式策略。作者說了如下幾種策略:
     a. 根據數據表分區:將數據庫以表為單位分割到多台服務器上。比如hatena bookmark有1 2 3 4四張表,把1 2放在一個服務器上,3 4放在一個服務器上,一個千萬記錄級別的表一般10個G以內,如果服務器是8G或者16G內存,基本能完全讀入頁面緩存中,不需要走磁盤IO。
     b.對大表做分表:將一個大表分割成幾個小表,比如把ID首字母a-c的放在服務器1上,d-f的放在服務器2上,這樣實現表的分割。這種分表方式也是目前主流的分表方式,不過會有缺陷,就是如果需要改變分割粒度時,比如分的更細,就需要對數據作一些整理。一般情況,都要伴隨短暫的停機才能實現平滑過渡。
     c.“島”模式的切分:島比較抽象,其實就是做一些業務划分,把不同業務類型的數據歸到不同數據服務器上,比如人的訪問和爬蟲的訪問(通過user agent來識別),以hatena的bookmark為例,人的訪問集中在少數的熱門書簽上,這樣緩存命中率很高,而爬蟲是一直爬,所以會爬很冷門的書簽,這樣就會降低緩存命中率,如果兩個混在一塊,緩存命中率就下降了,所以要分開。而且,作者說,bookmark的爬蟲訪問比人訪問還多。
     作者還講到一個細節,就是服務器剛啟動不要投入生產環境,因為操作系統還沒換成,需要把數據庫文件cat一遍,做預熱。而且在做性能測試的時候,第一次的測試結果也要廢棄,因為沒有緩存,是不准的。
     本章的最后,作者指出,在負載均衡方面,操作系統是基礎知識,需要深入了解操作系統的運行原理,比如操作系統的緩存、進程、縣城、虛擬內存、文件系統等知識。
第四章 數據庫的橫向擴展策略
     這一章主要講數據庫的橫向擴展策略,hatena主要使用LAMP(linux apache mysql perl)架構,所以主要數據庫是mysql。
     作者提出分布式mysql的三大要點:
     1. 靈活使用操作系統緩存:
          a.考慮全部數據量,盡量讓數據量小於物理內存,這樣可以利用操作系統的頁面緩存,如果內存不足並且增加內存成本不高,就增加內存
          b.考慮表結構的設計對數據大小的影響,這里作者舉了個例子:hatena bookmark表有三億個記錄,如果增加一個大小為8字節的列,8*3億就是3GB,所以大數據量的表,緊湊的表結構能明顯降低數據量
     2. 正確設置索引
     mysql的索引數據結構主要是B+樹,是B樹的一種擴展,能將磁盤尋道次數最小化,具體數據結構資料另外找資料閱讀
     另外作者講了mysql索引相關的一些細節,比如mysql一次查詢只能使用一個索引,所以如果是多條件查詢就要設置復合索引,另外還講了下mysql的explain命令
     3.以橫向擴展為前提的設計
     講述了hatena的數據庫架構,分成master庫和slave庫,通過mysql的replication功能,master庫數據polling到slave庫,通過or框架將寫的都路由到master庫,而查詢的路由到slave庫,這樣實現了讀寫分離。這樣的結構好處是查詢擴展很方便,只要增加slave服務器就可以,因為所有slave都是全亮復制的,但是master比較難擴展,需要采用分表的策略來實現master擴展,不過作者也說,web服務查詢比寫入高的多。所以查詢更容易成為瓶頸。
     master的擴展需要用到partition、分表等策略。對於partition而言,就是把不同的表放在不同的服務器上,但是這樣的話,就無法做join了(貌似join沒法跨服務器?),這種情況就改成先查a表再用a的結果集查b表就可以了(利用where...in...的語句來實現)。
     partition雖然可以實現擴展,但是也是有代價的:1.運維變的復雜,不同數據庫分布在不同服務器上,提升了運維成本。2.機器多了,故障率就會上升。3.增加新服務器都需要同時增加slave服務器,機器數會增加好幾台。
第五章 大規模數據處理實踐入門
     這一章沒太多內容,主要提了一些大規模數據處理和文本分析入門知識,基本都是帶過,給后面章節埋個伏筆,具體內容在后面章節詳細展開。還講到一個細節,這個細節估計很多有過海量數據網站經驗同學也有感覺的,就是在海量數據的存儲和查詢的時候,很多是違反數據庫三大范式的,比如盡量盡量避免做join,笛卡爾運算形成根本吃不消,所以需要做適當冗余,來降低關聯查詢之類的。
第六章 壓縮編程
     作者拋出一個課題:對一個152M大小的保存整數的csv文件做壓縮,變成二進制,把數據大小縮減到一半一下。
     作者闡述了利用可變字節碼來壓縮的思路和偽代碼。具體的可以搜資料了解。
第七章 算法實用化
     作者闡述了算法復雜度的一些知識,O(n)負責讀和O(logn)復雜度的差別,等等。不過作者也通過hatena bookmark的一個例子來說明,降低復雜度是一種理想的情況,但是實際中還是要結合實際,在滿足實用的情況下,簡單就是美,也就是我們平時常說的,不要過度設計。
     hatena diary的關鍵字加鏈接處理。作者舉了這個例子來說明算法演進提升系統服務能力。關鍵字加鏈接就是把博客中一些特定詞語加上一些類似wiki的解釋鏈接或者相關鏈接,這種大家在看各大新聞網站的時候都能看到。一開始關鍵字數量不多,他們就用正則匹配的方式來做,就是把關鍵字組合成or的正則表達式(foo|bar|...)類似這樣,然后去匹配文本,匹配到就加鏈接,由於正則表達式是基於狀態機實現的,所以匹配性能很差,會逐個單詞去匹配文本,是O(n^2)的復雜度,所以隨着詞庫增加性能就吃不消了,於是作者提出把正則表達式改成trie樹,改變匹配方式來提升性能,trie是一種單詞查找樹,通過把公共前綴集中到一起,來提升匹配性能,正則表達式中,搜索的時候是把每個單詞去文本里匹配一次,而在trie樹中,搜索是把文本放到trie樹中去匹配,只需要順序匹配一次即可。不過最后,作者采用了另一種更高效的算法,叫做aho-corasick算法,這個算法是trie樹匹配的一種改進版本,具體內容大家可以搜索下。通過這個實例作者提出了他的思路:一開始使用簡單快捷的方式先實現功能上線,等到達到相應復雜度之后再做優化,也就是我們說的不要過早優化過度優化,應該小步快跑。
     hatena bookmark文章分類處理。作者根據這個場景討論如何用算法解決新問題。hatenabookmark提供自動分類功能,就是系統獲取文本內容然后判斷是哪個分類。主要用到貝葉斯過濾器,使用朴素貝葉斯算法從概率上判斷文章屬於哪個類別,這個的前提是事先已經通過機器學習告訴貝葉斯過濾器什么樣的文章屬於哪個類別,這塊內容數據機器學習領域,跟現在流行的大數據比較沾邊。
     本章后面附了一個專欄,寫的是拼寫錯誤改正功能的算法實現,這個功能就是搜索引擎中的“你是不是要找”功能,你輸入了一個錯誤的詞,搜索引擎會幫你糾錯。hatenabookmark也有類似功能,主要思路是計算搜索查詢和字典中詞語的編輯距離(從錯的改成對的次數),比如博客元-博客園,糾錯的距離就是1,編輯距離可以用動態規划算法實現,如果字典中存在編輯距離短的就是認為可以推薦糾錯的。當然搜索引擎的糾錯比這個要復雜的多。
第八章 hatena關鍵字鏈接的實現
     這一章主要講了上一章提到的關鍵字鏈接aho-corasick算法的具體實現,細節就不細表了。
第九章 挑戰全文搜索技術
     這一章主要講解了搜索引擎的倒排索引技術,基本原理大家應該都懂的,不細表。
第十章 創建全文搜索引擎
     這一章以一個實際的例子講解了一個簡單搜索引擎的創建,代碼是用perl寫的。對perl不熟...掠過...
第十一章 支持大規模數據處理的服務器/基礎設施入門
     從十一章開始,作者主要講一些基礎設施相關的內容。
     作者把web服務和傳統企業軟件服務作了對比,企業軟件流量低但是嚴謹性要求很高,web服務容易產生流量爆發,嚴謹性相對而言沒那么高。
     作者還提到了雲服務和自行構建基礎設施的對比,這本書日本出版的時候是09年,從這里也可以看出日本在互聯網領域的領先,國內應該還是最近兩三年開始高呼雲的。作者對雲的看法是小公司很適合雲服務,比如亞馬遜的EC2等,因為雲的特點是靈活和可擴展,但是大規模的公司就需要自己搭建服務,因為雲服務在大數據量下怕會出現一些極限的不可控。
第十二章 保證可擴展性的必要思路
     本章作者列舉了一個服務器的支撐能力,一台4核8G的服務器,每分鍾處理幾千個請求,那么每個月能處理100w級別的PV,就是說百萬PV以內一台服務器可以撐得住。
     另外就是粗略的講解了web服務不同層的擴展思路,應用層由於無狀態,所以加機器擴展,另外就是根據不同用途擴展,面向爬蟲的和面向真實用戶的采用不同的策略等。觀察負載主要看load,根據經驗值,load不要超過CPU的核數都是ok的。比如四核CPU的,load不要超過4.
第十三章 保證冗余性和系統的穩定性
     作者提出,系統穩定性方面,最重視的就是單點穩定,即避免系統出現單點故障(指沒有冗余,一旦故障就停止服務沒法切換)
     應用程序服務器的冗余:通過負載均衡來分配流量,並且負載均衡器對服務器進行心跳檢查,來判斷機器是否正常服務,並且實現故障服務器的自動下線(不放流量)和自動恢復(重新放流量),這樣來保證整個集群的正常服務。
     數據庫服務器的冗余:這塊的冗余大家熟知就是主備冗余,有master和slave,如果master掛了就立馬切到slave。
     存儲服務器的冗余:存儲服務器主要是來保存圖片等媒體文件,hatena的存儲服務器采用的是第三方的分布式存儲服務器叫做MogileFS,本身會有容災功能。
     對於維護系統穩定性,作者提出兩條大的對策:1.維持適當余量,不要榨干服務器,總是滿負荷,這樣容易出現雪崩,作者提出極限的70%,就是說正常服務的時候不要占用系統70%的資源,超過就要增加服務器。2.消滅不穩定性因素,比如消滅各種潛在的內存泄露,慢sql等因素。3.發生異常時的自動控制,比如有些程序會存在潛在的內存泄露,這種情況一般服務器剛啟動沒問題,但是慢慢內存耗盡,這種時候如果發現頻繁的swap,就對web服務器自動重啟(利用了monit這個工具來監視系統負載、內存等狀況並適時執行apache重啟甚至操作系統重啟),這樣雖然不能解決問題,但是能保證不宕機,還有就是定期(10s)檢查數據庫服務器上執行的sql查詢並且殺掉慢查詢,等等。
第十四章 提高效率
     這里特指提高硬件資源的利用率。
     作者在上一章提到不能讓服務器超過70%的正常負載極限以及需要冗余,這必然導致一些資源的浪費,這一章作者就提出在適度冗余的前提下,盡可能的提高資源利用率。
     作者提出提高硬件利用率的思路:虛擬化技術。說白就是虛擬機,在一台物理機上虛擬出多太虛擬機,這樣可以提高物理機的整體資源利用率,目前國內的大型網站也普遍采用這種方式,淘寶就是采用一虛三的方式來做虛擬機。hetena采用的虛擬技機技術是Xen。但是虛擬機會肯定會帶來額外的開銷,作者給出的數據是:cpu2%-3%,內存10%,IO降低5%,但總的來說虛擬機帶來的利用率提升和運維上的方便很值得這些額外的性能付出。
     這一章作者還講了hatena服務器硬件的一些東西,為了降低成本,很少采用價格昂貴的專門服務器,而是以自己組裝廉價PC機為主,另外就是嘗試采用SSD來提高服務器IO性能。
第十五章 web服務和網絡
     這章主要講了三個分界點:1.流量1Gbps,這個也是千兆網卡的極限,這個時候單個路由器支撐不住,要么買昂貴的專業路由器,要么搭建多個路由器。2.500台主機,當主機到500台后,子網的arp表會基本達到極限,再上去就容易丟包,所以一個子網的主機盡量控制在500台以內。3.全球化,如果有歐美用戶訪問hatena,那么請求講跨越太平洋,這種延時就能難接受了,所以hatena的一些下載服務采用CDN的方式分布到全球,這樣靜態資源就可以通過CDN來下載了,hatena用的CDN服務是amazon cloudfront.
第十六章 當前構建web服務需要的實踐技術
     這一章作者主要講了一些大型web服務都會用到的中間件系統,包括異步隊列系統、大數據存儲系統、緩存系統、大數據計算集群等。
     作業系統:大型web服務的一個請求后面會處理很多事情,會跟很多分布式系統打交道,如果整個流程都是同步處理的,那么鏈路就會很長,處理耗時也很長,qps就會很低,而且鏈路長會導致出問題風險也增加,所以就會把一些可以異步處理的流程剝離出來交給隊列系統異步處理,這樣同步請求就不需要等待處理完成,提升主流程穩定性以及響應時間。作者舉了個栗子,比如hatena的bookmark,用戶收藏了一個url后,他們會異步的去拿url的概要、提取關鍵詞、分類處理等。這些處理有的很耗時,卡在主流程會影響用戶體驗。最簡單的作業系統就是調用一個單獨的處理腳本,但是在大型系統里不合適,hatena用的隊列系統主要是TheSchwartz和Gearman。
     存儲系統:主要對比了RDBMS和NOSQL(key-value存儲),應該說,這兩個本身就決定了不同的用途,使用場景的差異顯而易見。hatena用的RDBMS主要是MySQL,沒說用哪個nosql,不過目前國內持久性nosql用的比較多的應該是Hbase,mongoDB也有些人再用。緩存系統一般用memcached.
     代理/緩存系統:主要講了http的反向代理服務器搭建,主要講了squidhe varnish這兩個反向代理服務器搭建和對比,作者指出,合理搭建反向代理服務器對提升整體性能很有幫助,反向代理的緩存系統能郵箱的減輕后端應用服務器的壓力,這兩個是目前最流行的反向代理系統,相關資料也很多。
     計算集群:大規模web服務都需要做很多離線計算,最典型的入日志計算,比如算uv、pv等,都要對及時上百GB的日志文件做處理,所以需要搭建專門的計算集群。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM