1. 程式人生 > >大型網站技術

大型網站技術

得出 ats 規模 apr 一份 nginx mysql zeromq 特點

大型網站開發

網站架構
緩存和數據一致性
分布式事務
負載均衡和高可用
微服務
消息隊列
秒殺系統

大型網站特點

海量數據 高並用 高可用 需求多

容量的估算

常見容量估算:數據量 並發量 帶寬 CPU|MEM|DISK

容量評估步驟
1.評估總訪問量
2.評估平均訪問量 QPS
3.評估高峰 QPS
4.評估系統 單機極限
5.計算容量

常見性能測試方案

ab
JMeter
LoadRunner

系統負載

System Load :系統CPU繁忙程度的度量 有多少進程在等待被CPU調用 (進程等待隊列的長度)
Load Average : 一段時間內系統的平均負載 1,5,15分鐘
top uptime w

負載的定義

Load < 1單核
Load = 1單核
Load > 1單核
Load = 雙核

應用服務器和數據庫分離

如何選擇(webapp和數據庫對機器的要求)配置
應用服務器需要處理大量的邏輯 需要強大的CPU
文件服務器需要存儲大量用戶上傳的文件 需要更大的硬盤
數據庫服務器需要更快磁盤檢索和數據緩存 需要更大的內存和硬盤

應用服務器集群

假設數據庫服務器無壓力情況下 把應用服務器從一臺變為兩臺甚至多臺
把用戶請求分散到不同服務器 提高負載能力

用戶的請求由誰來轉發到到具體的應用服務器
負載均衡的轉發的算法有哪些?
用戶如果每次訪問到的服務器不一樣,那麽如何維護session的一致性

負載均衡

Load Balance 是分布式架構考慮因素
將請求/數據均勻分攤到多個操作單元上執行 關鍵在於均勻

負載均衡架構

客戶端層 反向代理層nginx 站點層 服務層 數據層
每一個下遊有多個上遊調用 每一個上遊均勻訪問下遊 就是負載均衡

負載均衡工作方式

http重定向
當http代理(比如瀏覽器)向web服務器請求某個URL後,web服務器可以通過http響應頭信息中的Location標記來返回一個新的URL。這意味著HTTP代理需要繼續請求這個新的URL,完成自動跳轉。
缺點:吞吐率限制
優點:不需要任何額外支持
適用場景:我們需要權衡轉移請求的開銷和處理實際請求的開銷,前者相對於後者越小,那麽重定向的意義就越大,例如下載。你可以去很多鏡像下載網站試下,會發現基本下載都使用了Location做了重定向。

DNS負載均衡
DNS 負責提供域名解析服務,當訪問某個站點時,實際上首先需要通過該站點域名的DNS服務器來獲取域名指向的IP地址,在這一過程中,DNS服務器完成了域名到IP地址的映射,同樣,這樣映射也可以是一對多的,這時候,DNS服務器便充當了負載均衡調度器。
使用命令:dig google.cn 查看DNS的配置
DNS服務器可以在所有可用的A記錄中尋找離用記最近的一臺服務器。
缺點: DNS記錄緩存更新不及時、策略的局限性、不能做健康檢查
優點:可以尋找最近的服務器,加快請求速度。
適用場景:一般我們在多機房部署的時候,可以使用。

反向代理負載均衡
在用戶的請求到達反向代理服務器時(已經到達網站機房),由反向代理服務器根據算法轉發到具體的服務器。常用的apache,nginx都可以充當反向代理服務器。反向代理的調度器扮演的是用戶和實際服務器中間人的角色。
工作在HTTP層(七層)
缺點:代理服務器成為性能的瓶頸,特別是一次上傳大文件。
優點:配置簡單、策略豐富、維持用戶會話、可根據訪問路徑做轉發。
適用場景:請求量不高的,簡單負載均衡。後端開銷較大的應用。

IP負載均衡
工作在傳輸層(四層)
通過操作系統內黑修改發送來的IP數據包,將數據包的目標地址修改為內部實際服務器地址,從而實現請求的轉發,做到負載均衡。lvs的nat模式。
缺點:所有數據進出還是要過負載機器,網絡帶寬成為瓶頸。
優點:內核完成轉發,性能高。
適用場景:對性能要求高,但對帶寬要求不高的的應用。視頻和下載等大帶寬的應用,並不適合使用。

數據鏈路層的負載均衡
工作在數據鏈路層(二層)
在請求到達負載均衡器後,通過配置所有集群機器的虛擬ip和負載均衡器相同,再通過修改請求的mac地址,從而做到請求的轉發。與IP負載均衡不一樣的是,當請求訪問完服務器之後,直接返回客戶。而無需再經過負載均衡器。 LVS DR(Direct Routing)模式。
缺點:配置復雜
優點:由集群機器直接返回,提高了出口帶寬。
適用場景:大型網站使用最廣的一種負載均衡方法。

負載均衡常見策略

輪循(Round Robin)
能力比較弱的服務器導致能力較弱的服務器最先超載。

加權輪循(Weighted Round Robin)
這種算法解決了簡單輪循調度算法的缺點:傳入的請求按順序被分配到集群中服務器,但是會考慮提前為每臺服務器分配的權重。

最少連接數(Least Connection)
最小連接數算法比較靈活和智能,由於後端服務器的配置不盡相同,對於請求的處理有快有慢,它是根據後端服務器當前的連接情況,動態地選取其中當前積壓連接數最少的一臺服務器來處理當前的請求,盡可能地提高後端服務的利用效率,將負責合理地分流到每一臺服務器。

加權最少連接(Weighted Least Connection)
如果服務器的資源容量各不相同,那麽“加權最少連接”方法更合適:在考慮連接數的同時也權衡管理員根據服務器情況定制的權重。

源IP哈希(Source IP Hash)
這種方式通過生成請求源IP的哈希值,並通過這個哈希值來找到正確的真實服務器。這意味著對於同一主機來說他對應的服務器總是相同。

隨機
通過系統的隨機算法,根據後端服務器的列表大小值來隨機選取其中的一臺服務器進行訪問。實際效果接近輪詢的結果。

常見負載均衡方案

硬件
常見的硬件有比較昂貴的NetScaler、F5、Radware和Array等商用的負載均衡器,優點就是有專業的維護團隊維護、性能高。缺點就是昂貴,所以對於規模較小的網絡服務來說暫時還沒有需要使用。

軟件
Nginx(1w)、HAProxy(5-10w)、LVS(十多萬) F5 BIG-IP(百萬)

負載均衡如何維持session 會話

1.把同一個用戶在某一個會話中的請求 都分配到固定的某一臺服務器中 常見的負載均衡算法ip_hash
2.session數據集中存儲 session數據集中存儲就是利用數據庫或者緩存來存儲session數據,實現了session和應用服務器的解耦
3.使用cookie來代替session的使用。

數據庫讀寫分離

一主多從,讀寫分離,冗余多個讀庫

但隨著訪問量的的提高,數據庫的負載也在慢慢增大。大部分互聯網的業務都是“讀多寫少”的場景,數據庫層面,讀性能往往成為瓶頸。如下圖:業界通常采用“一主多從,讀寫分離,冗余多個讀庫”的數據庫架構來提升數據庫的讀性能。

mysql復制原理

1.master將改變記錄到二進制日誌(binary log)中(這些記錄叫做二進制日誌事件,binary log events)
2.slave將master的binary log events拷貝到它的中繼日誌(relay log)
3.slave重做中繼日誌中的事件,將改變反映它自己的數據。

復制無法擴展寫操作

上一個例子中,原因是因為無法像擴展讀操作一樣,方便的擴展寫操作。所以MySQL的復制功能只是用來擴展讀操作的,無法擴展寫操作。

增加寫負載的唯一方法就是分庫分表

DB主從架構一致性優化方法

半同步復制
系統先對DB-master進行了一個寫操作
寫主庫等主從同步完成,寫主庫的請求才返回
讀從庫,讀到最新的數據(如果讀請求先完成,寫請求後完成,讀取到的是“當時”最新的

寫操作的腳本強制讀主庫,可借助數據庫代理實現
將統一腳本中的後續讀操作都從主庫讀取。可以配置數據庫代理自動實現。比如db_proxy等

緩存記錄寫key法
將某個庫上的某個key要發生寫操作,記錄在cache裏,並設置“經驗主從同步時間”的cache超時時間,例如500ms
修改數據庫
讀庫時先到cache裏查看,對應庫的對應key有沒有相關數據如果cache hit,有相關數據,說明這個key上剛發生過寫操作,此時需要將請求路由到主庫讀最新的數據
如果cache miss,說明這個key上近期沒有發生過寫操作,此時將請求路由到從庫,繼續讀寫分離

用搜索引擎和緩存來緩解讀庫的壓力

數據庫做讀庫的話,常常對模糊查找力不從心,即使做了讀寫分離,這個問題還未能解決。
以我們所舉的交易網站為例,經常根據商品的標題來查找對應的商品。對於這種需求,一般我們都是通過like功能來實現的,但是這種方式的代價非常大。此時我們可以使用搜索引擎的倒排索引來完成。
緩存是一種提高系統讀性能的常見技術,對於讀多寫少的應用場景,我們經常使用緩存來進行優化。

常用搜索引擎

Lucene更像是一個SDK。有完整的API族以及對應的實現。你可以利用這些在自己的應用裏實現高級查詢(基於倒排索引技術的)。是一套信息檢索工具包,但並不包含搜索引擎系統,因此在使用Lucene時你仍需要關註如數據獲取、解析、分詞等方面的東西。
Solr是基於Lucene做的。它更接近於我們認識到的搜索引擎系統,它是一個搜索引擎服務,通過各種API可以讓你的應用使用搜索服務,而不需要將搜索邏輯耦合在應用中。
Elasticsearch也是基於Lucene做的。但它比Solr在實時搜索上性能更高,使用上也更傻瓜簡單一些。目前使用者也越來越多,推薦使用。
Sphinx是一個基於SQL的全文檢索引擎,可以結合MySQL,PostgreSQL做全文搜索,它可以提供比數據庫本身更專業的搜索功能,使得應用程序更容易實現專業化的全文檢索

緩存與數據庫一致性

更新緩存 VS 淘汰緩存

更新緩存的代價很小,此時我們應該更傾向於更新緩存,以保證更高的緩存命中率
如果余額是通過很復雜的數據計算得出來的,傾向於刪除緩存。
淘汰緩存操作簡單,並且帶來的副作用只是增加了一次cache miss,建議作為通用的處理方式。

先操作數據庫 vs 先操作緩存

當寫操作發生時,假設淘汰緩存作為對緩存通用的處理方式,又面臨兩種抉擇:
先寫數據庫,再刪除緩存。
先刪除緩存,再寫數據庫。
對於一個不能保證事務性的操作,一定涉及“哪個任務先做,哪個任務後做”的問題。
解決這個問題的方向是:如果出現不一致,誰先做對業務的影響較小,就誰先執行。

先寫數據庫,再淘汰緩存:
第一步寫數據庫操作成功,第二步淘汰緩存失敗,則會出現DB中是新數據,Cache中是舊數據,數據不一致。

先淘汰緩存,再寫數據庫
第一步淘汰緩存成功,第二步寫數據庫失敗,則只會引發一次Cache miss。

緩存的一致性哈希算法

拆分

垂直拆分:按功能或業務將原來一個表中的內容拆分成多個表,或者一個庫拆分成多個庫。

水平拆分:將同類型的數據分別存放與相同結構的多個表中。
一個好的分表鍵通常是數據庫中非常重要的核心實體的主鍵。比如用戶ID.
user_id % 5 = 0
user_id % 5 = 1

跨庫join的問題

在拆分之前,系統中很多列表和詳情頁所需的數據是可以通過sql join來完成的。而拆分後,數據庫可能是分布式在不同實例和不同的主機上,join將變得非常麻煩。

1.全局表
系統中所有模塊都可能會依賴到的一些表在各個庫中都保存。

2.字段冗余
“訂單表”中保存“賣家Id”的同時,將賣家的“Name”字段也冗余,這樣查詢訂單詳情的時候就不需要再去查詢“賣家用戶表”

3.數據同步
定時A庫中的tbl_a表和B庫中tbl_b關聯,可以定時將指定的表做主從同步。

如果join很復雜,首先需要考慮分庫的設計是否合理,其次可以考慮使用hadoop或者spark來解決。

垂直拆庫,跨庫事務的問題

CAP:一致性、可用性和分區容錯性
分布式事務的實現:二階段提交
兩階段提交協議(Two-phase Commit,2PC)經常被用來實現分布式事務。

一般分為事務協調器TC和資源管理SI兩種角色,這裏的資源管理就是具體的數據庫。

1.應用程序發起一個開始請求到TC;
2.向所有的Si發起

如何保障系統的高可用

1.單點往往是系統高可用最大的風險和敵人,應該盡量在系統設計的過程中避免單點。
2.高可用保證的原則是 “冗余”。只有一個單點,掛了服務會受影響;如果有冗余備份,掛了還有其他backup能夠頂上。
3.高可用架構設計的核心準則是:冗余。
4.有了冗余之後,還不夠,每次出現故障需要人工介入恢復勢必會增加系統的不可服務實踐。所以,又往往是通過“自動故障轉移”來實現系統的高可用。

高可用和負載均衡的軟件方案

LVS和haproxy都是實現的負載均衡的作用。
keepalived和heartbeat都是提高高可用性的,避免單點故障。
Keepalived和heartbeat簡單區別:
keepalived配置簡單, heartbeat功能強大配置豐富。
LVS和keepalived都是通過檢測vrrp數據包來工作,因此keepalived更適合與lvs搭配。
Heartbeat功能豐富更適合業務使用。
在keepalived實現不了的業務中選擇Heartbeat。
日常工作中常用的搭配方式是:
LVS+ keepalived和haproxy+heartbeat

消息隊列

消息隊列常用的應用場景有哪些?
主要就是:應用解耦合、異步操作、流量削鋒。

異步操作

將註冊信息寫入數據庫成功後,發送註冊郵件,再發送註冊短信。以上三個任務全部完成後,返回給客戶端。

用戶的響應時間相當於是註冊信息寫入數據庫的時間,也就是50毫秒。註冊郵件,發送短信寫入消息隊列後,直接返回,因此寫入消息隊列的速度很快,基本可以忽略,因此用戶的響應時間可能是50毫秒

應用解耦

用戶下單後,訂單系統需要通知庫存系統。傳統的做法是,訂單系統調用庫存系統的接口

解耦以後,假如,在下單時庫存系統不能正常使用。也不影響正常下單,因為下單後,訂單系統寫入消息隊列就不再關心其他的後續操作了

流量削減

流量削鋒也是消息隊列中的常用場景,一般在秒殺或團搶活動中使用廣泛。

秒殺活動,一般會因為流量過大,導致流量暴增,應用掛掉。為解決這個問題,一般需要在應用前端加入消息隊列。
可以控制活動的人數;
可以緩解短時間內高流量壓垮應用

常見消息隊列軟件

RabbitMQ
使用Erlang編寫的一個開源的消息隊列,本身支持很多的協議:AMQP,XMPP, SMTP, STOMP,也正因如此,它非常重量級,更適合於企業級的開發。

Redis
Redis是一個基於Key-Value對的NoSQL數據庫,開發維護很活躍。雖然它是一個Key-Value數據庫存儲系統,但它本身支持MQ功能,所以完全可以當做一個輕量級的隊列服務來使用。

ZeroMQ
號稱最快的消息隊列系統,尤其針對大吞吐量的需求場景。ZMQ能夠實現RabbitMQ不擅長的高級/復雜的隊列,但是開發人員需要自己組合多種技術框架,技術上的復雜度是對這MQ能夠應用成功的挑戰。ZeroMQ僅提供非持久性的隊列,也就是說如果down機,數據將會丟失。其中,Twitter的Storm中默認使用ZeroMQ作為數據流的傳輸。

ActiveMQ
類似於ZeroMQ,它能夠以代理人和點對點的技術實現隊列。同時類似於RabbitMQ,它少量代碼就可以高效地實現高級應用場景。

Kafka/Jafka
是一個高性能跨語言分布式Publish/Subscribe消息隊列系統。
快速持久化,可以在O(1)的系統開銷下進行消息持久化;
高吞吐,在一臺普通的服務器上既可以達到10W/s的吞吐速率;
完全的分布式系統,Broker、Producer、Consumer都原生自動支持分布式,
自動實現復雜均衡;支持Hadoop數據並行加載,對於像Hadoop的一樣的日誌數據和離線分析系統,但又要求實時處理的限制,這是一個可行的解決方案。

緩存

緩存是大型網站中的重要組件,主要解決高並發,大數據場景下,熱點數據訪問的性能問題。適用於讀多寫少的場景。
從我們的使用角度來分,主要有:
CDN緩存
反向代理緩存
分布式緩存

CDN緩存

CDN主要解決將數據緩存到離用戶最近的位置,一般緩存靜態資源文件(頁面,腳本,圖片,視頻,文件等)。
其基本思路是盡可能避開互聯網上有可能影響數據傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定。

反向代理緩存

反向代理位於應用服務器機房,處理所有對WEB服務器的請求。
反向代理一般緩存靜態資源,動態資源轉發到應用服務器處理。常用的緩存應用服務器有Varnish,Ngnix,Squid。

代理緩存比較
varnish和squid是專業的cache服務,
nginx需要第三方模塊支持;
Varnish采用內存型緩存,避免了頻繁在內存、磁盤中交換文件,性能比Squid高;
Varnish由於是內存cache,所以對小文件如css,js,小圖片啥的支持很棒,後端的持久化緩存可以采用的是Squid或ATS;
Squid功能全而大,適合於各種靜態的文件緩存,一般會在前端掛一個HAProxy或nginx做負載均衡跑多個實例;
Nginx采用第三方模塊ncache做的緩沖,性能基本達到varnish,一般作為反向代理使用,可以實現簡單的緩存。

分布式緩存

分布式緩存,主要指緩存用戶經常訪問數據的緩存,數據源為數據庫。一般起到熱點數據訪問和減輕數據庫壓力的作用

分布式緩存

Redis
Redis 是一個開源(BSD許可)的,基於內存的,多數據結構存儲系統。可以用作數據庫、緩存和消息中間件。 支持多種類型的數據結構,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 與範圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial) 索引半徑查詢。

集群方案:
Twemproxy
Codis
官方近期推出的Redis Cluster

Memcache與Redis的比較
1數據結構:Memcache只支持key value存儲方式,Redis支持更多的數據類型,比如Key value,hash,list,set,zset;
2多線程:Memcache支持多線程,redis支持單線程;CPU利用方面Memcache優於redis;
3持久化:Memcache不支持持久化,Redis支持持久化;
4內存利用率:memcache高,redis低(采用壓縮的情況下比memcache高);
5過期策略:memcache過期後,不刪除緩存,會導致下次取數據數據的問題,Redis有專門線程,清除緩存數據;

秒殺系統

特點:庫存只有一份,所有人會在集中的時間讀和寫這些數據。但是最終成功的人卻很少

思路:將請求盡量攔截在系統上遊。

1瀏覽器層請求攔截
JS層面,限制用戶在x秒之內只能提交一次請求。
2站點層(webServer)請求攔截與頁面緩存
同一個uid,限制訪問頻度,做頁面緩存,x秒內到達站點層的請求,均返回同一頁面。
3服務層(SOA)請求攔截與數據緩存
對於寫請求,做請求隊列,每次只透過有限的寫請求去數據層,如果均成功再放下一批,如果庫存不夠則隊列裏的寫請求全部返回“已售完”
對於讀請求,cache來抗,不管是memcached還是redis,單機抗個每秒10w應該都是沒什麽問題。
4數據層處理真正的下單請求

大型網站技術