12306.cn 網站掛了,被全國人民罵了。我這兩天也在思考這個事,我想以這個事來粗略地和你們討論一下網站性能的問題。由於倉促,並且徹底基於本人有限的經驗和了解, 因此,若是有什麼問題還請你們一塊兒討論和指正。(這又是一篇長文,只討論性能問題,不討論那些用戶界面、用戶體驗、或是是否把支付和購票下單環節分開的功 能性的東西)css
甲、認識業務的特殊性
任何技術都離不開業務需求,因此,要說明性能問題,首先仍是想先說說業務問題。html
- 其一,有人可能把這個東西和扣扣或是網遊相比。但我以爲這二者是不同的,網遊和扣扣在線或是登陸時訪問的更多的是用戶本身的數據,而訂票系統訪問的是中心的票量數據,這是不同的。不要以爲網遊或是扣扣能行你就覺得這是同樣的。網遊和扣扣的後臺負載相對於電子商務的系統仍是簡單。
- 其二,有人說春節期間訂火車的這個事好像網站的秒殺活動。的確很類似, 但 是若是你的思考不在表面的話,你會發現這也有些不同。火車票這個事,還有不少查詢操做,查時間,查座位,查鋪位,一個車次不 行,又查另外一個車次,其伴隨着大量的查詢操做,下單的時候須要對數據庫操做。而秒殺,直接殺就行了。另外,關於秒殺,徹底能夠作成只接受前N個用戶的請求 (徹底不操做後端的任何數據, 僅僅只是對用戶的下單操做log),這種業務,只要把各個服務器的時間精確同步了就能夠了,無需在當時操做任何數據庫。能夠訂單數夠後,中止秒殺,而後批 量寫數據庫。火車票這個豈止是秒殺那麼簡單。能不能買到票得當時告訴用戶啊。
- 其三,有人拿這個系統和奧運會的票務系統比較。我以爲仍是不同。雖然奧運會的票務系統當年也一上線就廢了。可是奧運會用的是抽獎的方式,也就是說不存在先來先得的搶的方式,並且,是過後抽獎,事前只須要收信息,事前不須要保證數據一致性,沒有鎖,很容易水平擴展。
- 其四,訂票系統應該和電子商務的訂單系統很類似,都是須要對庫存進 行:1)佔住庫存,2)支付(可選),3)扣除庫存的操做。這個是須要有一致性的檢查的,也就是在併發時須要對數據加鎖的。B2C的電商基本上都會把這個 事幹成異步的,也就是說,你下的訂單並非立刻處理的,而是延時處理的,只有成功處理了,系統纔會給你一封確認郵件說是訂單成功。我相信有不少朋友都收到 認單不成功的郵件。這就是說,數據一致性在併發下是一個瓶頸。
- 其五,鐵路的票務業務很變態,其採用的是忽然放票,而有的票又遠遠不夠 你們分,因此,你們纔會有搶票這種有中國特點的業務的作法。因而當票放出來的時候,就會有幾百萬人甚至上千萬人殺上去、查詢、下單。幾十分鐘內,一個網站 能接受幾千萬的訪問量,這個是很恐怖的事情。聽說12306的高峯訪問是10億頁面訪問量,集中在早8點到10點,每秒頁面訪問量在高峯時上千萬。
多說幾句:前端
- 庫存是B2C的惡夢,庫存管理至關的複雜。不信,你能夠問問全部傳統和電務零售業的企業,看看他們管理庫存是多麼難的一件事。否則,就不會有那麼多人在問凡客的庫存問題了。(你還能夠看看《喬布斯傳》,你就知道爲何Tim會接任Apple的CEO了,由於他搞定了蘋果的庫存問題)
- 對於一個網站來講,瀏覽網頁的高負載很容易搞定,查詢的負載有必定的難度去處理,不過仍是能夠經過緩存查詢結果來搞定,最難的就是下單的負載。由於要訪問庫存啊,對於下單,基本上是用異步來搞定的。去年雙11節,淘寶的每小時的訂單數大約在60萬左右,京東一天也才能支持40萬(竟然比12306還差),亞馬遜5年前一小時可支持70萬訂單量。可見,下訂單的操做並無咱們相像的那麼性能高。
- 淘寶要比B2C的網站要簡單得多,由於沒有倉庫,因此,不存在像B2C這樣有N個倉庫對同一商品庫存更新和 查 詢的操做。下單的時候,B2C的 網站要去找一個倉庫,又要離用戶近,又要有庫存,這須要不少計算。試想,你在北京買了一本書,北京的倉庫沒貨了,就要從周邊的倉庫調,那就要去看看瀋陽或 是西安的倉庫有沒有貨,若是沒有,又得看看江蘇的倉庫,等等。淘寶的就沒有那麼多事了,每一個商戶有本身的庫存,庫存分到商戶頭上了,反而有利於性能。
- 數據一致性纔是真正的性能瓶頸。有 人說nginx能夠搞定每秒10萬的靜態請求,我不懷疑。但這只是靜態請求,理論值,只要帶寬、讀寫性可以強、服務器計算能力夠,並支持的併發鏈接數頂得住 10萬TCP連接的創建的話,那沒有問題。但在加上數據一致性這一要求,這10萬就完徹底全成了一個可望不可及的理論值了。
我說那麼多,我只是想從業務上告訴你們,咱們須要從業務上真正瞭解春運鐵路訂票這樣業務的變態之處。nginx
乙、前端性能優化技術
要解決性能的問題,有不少種經常使用的方法,我在下面列舉一下,我相信12306這個網站使用下面的這些技術會讓其性能有質的飛躍。web
1、前端負載均衡
經過DNS的負載均衡器(通常在路由器上根據路由的負載重定向)能夠把用戶的訪問均勻地分散在多個Web服務器上。這樣能夠減小Web服務器的請求 負載。由於http的請求都是短做業,因此,能夠經過很簡單的負載均衡器來完成這一功能。最好是有CDN網絡讓用戶鏈接與其最近的服務器(CDN一般伴隨 着分佈式存儲)。(關於負載均衡更爲詳細的說明見「後端的負載均衡」)算法
2、減小前端連接數
我看了一下12306.cn,打開主頁須要建60多個HTTP鏈接,車票預訂頁面則有70多個HTTP請求,如今的瀏覽器都是併發請求的。因此,只 要有100萬個用戶,就會有6000萬個連接,太多了。一個登陸查詢頁面就行了。把js打成一個文件,把css也打成一個文件,把圖標也打成一個文件,用 css分塊展現。把連接數減到最低。shell
—— 注:之前撥號鏈接時代,由於帶寬小,大圖片沒下載完,用戶已經關閉了瀏覽器頁面,因此對鏈接數要求不高,但對一次的下載量要求很高。但如今帶寬已經足夠, 一次的下載量已經再也不是瓶頸,因此,不能再以過去那種方式處理這個問題,而應該合併文件,儘可能減小請求數,從而減小交互頻率,把節約出的交互資源用於網站 和用戶的其餘更有價值的互動上。數據庫
3、減小網頁大小增長帶寬
這個世界不是哪一個公司都敢作圖片服務的,由於圖片太耗帶寬了。如今寬帶時代很難有人能體會到當撥號時代作個圖頁都不敢用圖片的情形(如今在手機端瀏 覽也是這個情形)。我查看了一下12306首頁的須要下載的總文件大小大約在900KB左右,若是你訪問過了,瀏覽器會幫你緩存不少,只需下載10K左右 的文件。可是咱們能夠想像一個極端一點的案例,1百萬用戶同時訪問,且都是第一次訪問,每人下載量須要1M,若是須要在120秒內返回,那麼就須要,1M * 1M /120 * 8 = 66Gbps的帶寬。很驚人吧。因此,我估計在當天,12306的阻塞基本上應該是網絡帶寬,因此,你可能看到的是沒有響應。後面隨着瀏覽器的緩存幫助 12306減小不少帶寬佔用,因而負載一下就到了後臺,後臺的數據處理瓶頸一下就出來。因而你會看到不少http 500之類的錯誤。這說明服務器垮了。後端
4、前端頁面靜態化
靜態化一些不常變的頁面和數據,並壓縮一下(好比gzip,固然,也要考慮壓縮和讀取的資源消耗問題)。還有一個變態的方法是把這些靜態頁面放在內存,也就是(/dev/shm目錄下),直接從內存中把文件讀出來返回,這樣能夠減小對磁盤的讀寫數組
注:由於磁盤讀寫相對於內存讀寫來講太慢。
5、優化查詢
不少人查詢都是在查同樣的,徹底能夠用反向代理合並這些併發的相同的查詢。這樣的技術主要用查詢結果緩存來實現,第一次查詢走數據庫得到數據,並把 數據放到緩存,後面的查詢通通直接訪問高速緩存。爲每一個查詢作Hash,使用NoSQL的技術能夠完成這個優化。(這個技術也能夠用作靜態頁面)
對於火車票量的查詢,我的以爲不要顯示數字,就顯示一個「有」或「無」就行了,這樣能夠大大簡化系統複雜度,並提高性能。
6、緩存的問題
緩存能夠用來緩存動態頁面,也能夠用來緩存查詢的數據。緩存一般有那麼幾個問題:
1)緩存的更新方式:也叫緩存和數據庫的同步。有這麼幾種方法,一是緩存過期失效(也就是time out),在通過必定時間後,讓緩存失效,重查;二是,由後端通知更新,一旦後端發生變化,通知前端更新。前者實現起來比較簡單,但實時性不高,後者實現 起來比較複雜 ,但實時性高。
2)緩存的換頁。內存可能不夠,因此,須要把一些不活躍的數據換出內存,這個和操做系統的內存換頁和交換內存很類似。FIFO、LRU、LFU都是比較經典的換頁算法。相關內容參看Wikipeida的緩存算法。
3)緩存的重建和持久化。緩存在內存,系統總要維護,因此,緩存就會丟失,若是緩存沒了,就須要重建,若是數據量很大,緩存重建的過程會很慢,這會影響生產環境,因此,緩存的持久化也是須要考慮的。
諸多強大的NoSQL都很好支持了上述三大緩存的問題。
丙、後端性能優化技術
前面討論了前端性能的優化技術,因而前端可能就不是瓶頸問題了。那麼性能問題就會到後臺數據上來了。下面說幾個後臺常見的性能優化技術。
1、冗餘數據
容許數據庫出現冗餘數據,具體方法就是減小錶鏈接這樣的開銷比 較大的操做,但這樣會犧牲數據的一致性,風險比較大。不少人把NoSQL用作數據,快是快了,由於數據冗餘了,但這對數據一致性有大的風險。這須要根據不 同的業務進行分析和處理。(注意:用關係型數據庫很容易移植到NoSQL上,可是反過來從NoSQL到關係型就難了)
2、鏡像數據
幾乎全部主流的數據庫都支持鏡像(replication)。數據庫的鏡像帶來的好處就是能夠作負載均衡。把一臺數據庫的負載均分到多臺上,同時又保證了數據一致性(Oracle的SCN)。最重要的是,這樣還能夠有高可用性,一臺廢了,還有另外一臺在服務。
數據鏡像的數據一致性多是個問題,因此咱們要在單條數據上進行數據分區,也就是說,把一個暢銷商品的庫存均分到不一樣的服務器上,如,一個暢銷商品有1萬的庫存,咱們能夠設置10臺服務器,每臺服務器上有1000個庫存,這就好像B2C的倉庫同樣。
注:數據鏡像一致性問題其實沒那麼好解決,平常現實中常常遇到某一後操做失誤,須要恢復到前操做,但由於系統自己是自動化處理,每每只注意到數據完整性這種問題,而無法注意到數據對人的意義問題,從而可能這樣的一致性機制反而會形成現實的災難性,因此,還必須設置一條人工的確認操做(能夠有多種方式,好比用戶的訂單提交再次確認),不然數據一致性會使人抓狂
3、數據拆分(數據分區)
數據鏡像不能解決的一個問題就是數據表裏的記錄太多,致使數據庫操做太慢。因此,把數據拆分開來。數據拆分有不少種作法,通常來講有下面這幾種:
1)把數據把某種邏輯來分類。好比火車票的訂票系統能夠按各鐵路局來分,可按各類車型分,能夠按始發站分,能夠按目的地分……,反正就是把一張表拆成多張有同樣的字段可是不一樣種類的表,這樣,這些表就能夠存在不一樣的機器上以達到分擔負載的目的。
2)把數據按字段分,也就是堅着分表。好比把一些不常常改的數據放在一個表裏,常常改的數據放在另外一個表裏。把一張表變成1對1的關係,這樣,你可 以減小表的字段個數,一樣能夠提高必定的性能。另外,字段多會形成一條記錄的存儲會被放到不一樣的頁表裏,這對於讀寫性能都有問題。
3)平均分表。由於前兩種種方法是並不必定平均分均,可能某個種類的數據仍是不少。因此,也有采用平均分配的方式,經過主鍵ID的範圍來分表。
4)同一數據拆開存放。這個在上面數據鏡像提過。也就是把同一商品的庫存值分到不一樣的服務器上,好比有10000個庫存,能夠分到10臺服務器上,一臺上有1000個庫存。而後負載均衡。
這四種分區都有好有壞。最經常使用的仍是第一種。
注:數據一旦拆分存放,就須要有一個或是多個調度來讓你的前端程序知道去哪裏找數據,這種調度會消耗反應時間,所以,不可分得太細,調度表和拆開來的表大小比例如何?
把火車票的數據分區,並放在各個省市,會對12306這個系統有很是有意義的質的性能的提升。
4、後臺系統負載均衡
前面說了數據拆分存放,數據拆分存放能夠在必定程度上減輕系統負載,可是沒法減輕熱銷商品的負載,對於火車票來講,能夠認爲是大城市的某些主幹線上 的車票。這就須要鏡像數據來減輕負載。鏡像了數據,必然要用到負載均衡,在後臺,咱們可能很難使用像路由器上的負載均衡器,由於那是均衡流量的,由於流量 並不表明服務器的繁忙程序。所以,咱們須要一個任務分配系統,其還能監控各個服務器的負載狀況。
任務分配服務器有一些難點:
- 負載狀況比較複雜。什麼叫忙:是核芯處理量高?仍是磁盤讀寫頻率高?仍是內存使用量高?仍是併發數高?你可能須要所有都要考慮。這些信息要發送給那個任務分配器上,由任務分配器挑選一臺負載最輕的服務器來處理。
- 任務分配服務器上須要對任務隊列,不能丟任務啊,因此還須要持久化。而且能夠以批量的方式把任務分配給服務器。
- 任務分配服務器死了怎麼辦?這裏須要一些如即時替用(Live-Standby)或是失效備援(failover)等高可用性的技術。咱們還須要注意那些持久化了的任務的隊列若是轉移到別的服務器上的問題。
中央分配式:看到有不少系統都用靜態的方式來分配,有的用hash,有的就簡單地輪流分析。這些都不夠好,一個是不能完美地負載均衡,另外一個靜態的方法的致命缺陷是,若是有一臺計算服務器死機了,或是咱們須要加入新的服務器,對於咱們的分配器來講,都須要從新刷新。
下游請纓式:由下游的計算服務器去任務服務器上拿任務。讓這些計算服務器本身決定本身是否要任務。這樣的好處是能夠簡化系統的複雜度,並且還能夠任意實時地減小或增長計算服務器。可是惟一很差的就是,若是有一些任務只能在某種服務器上處理,這可能會引入一些複雜度。 不過整體來講,這種方法多是比較好的負載均衡。
5、異步處理、 限流閥 和 批量處理
異步、限流閥(throttle) 和批量處理都須要對併發請求數作隊列處理的。
- 異步處理:在業務上通常來講就是收集請求,而後延時處理。在技術上就是能夠把各個處理程序作成並行的,也就能夠水平擴展了。可是異步的技術問題大概有這 些,1)被調用方的結果返回,會涉及進程線程間通訊的問題。2)若是程序須要回滾,回滾會有點複雜。3)異步一般都會伴隨多線程多進程,併發的控制也相對麻煩一些。4)不少異步系統都用消息機制,消息的丟失和亂序也會是比較複雜的問題。
- 限流閥: 這實際上是個保護機制,是在不提高性能和資源消耗的狀況下,防止系統被超過本身不能處理量給搞垮了,這樣系統看起來會顯得穩健!使用限流閥技術通常來講是對於一些本身沒法有效控制或者是須要穩定不中斷運行的系統,好比,和你網站對接的銀行系統,或者是長城防火牆。
- 批量處理的技術,是把一堆基本相同的請求批量處理。 好比,你們同時購買同一個商品,沒有必要你買一個我就寫一次數據庫,徹底能夠收集到必定數量的請求,一次操做。這個技術能夠用做不少方面。好比節省網絡帶 寬,咱們都知道網絡上的最大單位傳輸量(MTU,或者翻譯爲最大傳輸單元,就是一次傳輸的量),以太網是1500字節,光纖能夠達到4000 多個字節,若是你的一個網絡包沒有放滿這個最大傳輸單元,那就是在浪費網絡帶寬,由於網卡的驅動程序只有一塊一塊地讀效率纔會高。所以,網絡發包時,咱們須要收集到足夠多的信息後再作網絡吞吐,這也是一種批量處理的方式。批量處理的敵人是流量低,因此,批量處理的系統通常都會設置上兩個閥值,一個是做業量達標,另外一個是自動過期失效,只要有一個條件知足,就會開始提交處理。
因此,只要是異步,通常都會有限流閥機制,通常都會有隊列來排隊,有隊列,就會有持久化,而系統通常都會使用批量的方式來處理。
雲風同窗設計的「排隊系統」 就是這個技術。這和電子商務的訂單系統很類似,就是說,個人系統收到了你的購票下單請求,可是我尚未真正處理,個人系統會跟據我本身的處理能力來限制住這些大量的請求,並一點一點地處理。一旦處理完成,我就能夠發郵件或短信告訴用戶你來能夠真正購票了。
在這裏,我想經過業務和用戶需求方面討論一下雲風同窗的這個排隊系統,由於其從技術上看似解決了這個問題,可是從業務和用戶需求上來講可能仍是有一些值得咱們去深刻思考的地方:
1)隊列的DoS攻擊。首先,咱們思考一下,這個隊是個單純地排隊的嗎?這樣作還不夠好,由於這樣咱們不能杜絕黃牛,並且單純的ticket_id很容易發生DoS攻擊,好比,我發起無數個 ticket_id,進入購票流程後,我不買,我就耗你半個小時,很容易我就可讓想買票的人幾天都買不到票。有人說,用戶應該要用身份證來排隊, 這樣在購買裏就必需要用這個身份證來買,但這也還不能杜絕黃牛排隊或是號販子。由於他們能夠註冊無數個賬號來排隊,但就是不買。黃牛這些人這個時候只須要幹一個事,把網站搞得正常不能訪問,讓用戶只能經過他們來買。
注:可見,除了限流閥機制,還要一個過時機制,外加一些輔助檢測手段(好比須要認證的登錄)——其實,此處比拼的就是網站和黃牛的硬件實力了,只要可以把門檻提升到黃牛的硬件投入超越邊際效益臨界點,便可。
2)對列的一致性?對這個隊列的操做是否是須要鎖?只要有鎖,性能必定上不去。試想,100萬我的同時要求你來分配位置號,這個隊列將會成爲性能瓶頸。你必定沒有數據庫實現得性能好,因此,可能比如今還差
3)隊列的等待時間。購票時間半小時夠不夠?多很少?要是那時用戶正好不能上網呢?若是時間短了,用戶也會抱 怨,若是時間長了,後面在排隊的那些人也會抱怨。這個方法可能在實際操做上會有不少問題。另外,半個小時太長了,這徹底不現實,咱們用15分鐘來舉例:有 1千萬用戶,每個時刻只能放進去1萬個,這1萬個用戶須要15分鐘完成全部操做,那麼,這 1千萬用戶所有處理完,須要1000*15m = 250小時,10天半,火車早開了。(我並亂說,根據鐵道部專家的說明:這幾天,平均一天下單100萬,因此,處理1000萬的用戶須要十天。這個計算可能有點簡單了,我只是想說,在這樣低負載的系統下用排隊可能都不能解決問題)
注: 這說法有點問題:首先,一趟車的載人數是有限的,因此並無必要處理1千萬用戶的需求,能夠經過設定提早購票天數,來分流這些人員。但按照春運總十億人次 來算的話,若是一次能放進10萬個,10萬×15分鐘=25000小時,則須要1042天處理完,若是一次能放進100萬,則須要104天處理完,仍是不 夠,春運沒有那麼多天,一次須要放進1000萬,纔夠!
4)隊列的分佈式。這個排隊系統只有一個隊列好嗎? 還不足夠好。由於,若是你放進去的能夠購票的人若是在買同一個車次的一樣的類型的票(好比某動車臥鋪),仍是等於在搶票,也就是說系統的負載仍是會有可能 集中到其中某臺服務器上。所以,最好的方法是根據用戶的需求——提供出發地和目的地,來對用戶進行排隊。而這樣一來,隊列也就能夠是多個,只要是多個隊 列,就能夠水平擴展了。
我以爲徹底能夠向網上購物學習。在排隊(下單)的時候,收集好用戶的信息和想要買的票,並容許用戶設置購票的優先級,好比,A車次臥鋪買 不到就買 B車次的臥鋪,若是還買不到就買硬座等等,而後用戶把所需的錢先充值好,接下來就是系統徹底自動地異步處理訂單。成功不成功都發短信或郵件通知用戶。這樣,系統不只能夠省去那半個小時的用戶交互時間,自動化加快處理,還能夠合併相同購票請求的人,進行批處理(減小數據庫的操做次數)。這種方法最妙的事是能夠知道這些排隊用戶的需求,不但能夠優化用戶的隊列,把用戶分佈到不一樣的隊列,還能夠像亞馬遜的心願單同樣,讓鐵道部作車次統籌安排和調整(最後,排隊 系統(下單系統)仍是要保存在數據庫裏的或作持久化,不能只放在內存中,否則機器一掛,就等着被罵吧)。
丁、小結
寫了那麼多,我小結一下:
0)不管你怎麼設計,你的系統必定要能容易地水平擴展。也就是說,你的整個數據流中,全部的環節都要可以水平擴展。這樣,當你的系統有性能問題時,「加3倍的服務器」纔不會被人譏笑。
1)上述的技術不是一朝一夕能搞定的,沒有長期的積累,基本無望。
2)集中式的賣票很難搞定,使用上述的技術可讓訂票系統能有幾佰倍的性能提高。而在各個省市建分站,分開賣票,是能讓現有系統性能有質的提高的最好方法。
3)春運前夕搶票且票量供遠小於求這種業務模式是至關變態的,讓幾千萬甚至上億的人在某個早晨的8點鐘同時登陸同時搶票的這種業務模式是變態中的變態。業務形態的變態決定了不管他們怎麼辦幹必定會被罵。
4)爲了那麼一兩個星期而搞那麼大的系統,而其它時間都在閒着,也就是鐵路才幹得出來這樣的事了。
戊、附錄:
1、雲風方案:
其實鐵路訂票系統面臨的技術難點無非就是春運期間可能發生的海量併發業務請求。這個加上一個排隊系統就能夠輕易解決的。
原本我在 weibo 上閒扯兩句,這麼簡單的方案,本覺得你們一看就明白的。沒想到仍是許多人有疑問。好吧,寫篇 blog 來解釋一下。
簡單說,咱們設置幾個網關服務器,用動態 DNS 的方式,把併發的訂票請求分攤開。類比現實的話,就是把人分流到不一樣的購票大廳去。每一個購票大廳均可以買到全部車次的票。OK ,這一步的負載均衡怎麼作我就不詳細說了。
每一個網關其實最重要的做用就是讓訂票的用戶排隊。其實整個系統也只用作排隊,關於實際訂票怎麼操做,就算每一個網關後坐一排售票員,在屏幕上看到有人來買票,輸入到內部訂票系統中出票,而後再把票號敲回去,這個系統都能無壓力的正常工做。不然,之前春運是怎麼把票賣出去的?
咱們來講說排隊系統是怎麼作的:
其實就相似咱們去熱門館子吃飯拿號。只不過要防止別人僞造號插隊而已。
若是你來一我的(一次 HTTP 請求),我就隨機產生一個我作過一些簽名處理的號碼返回給你。暫時稱爲 ticket id 。這個 ticked id 是很難僞造的。
系統在內存裏開一個大數組(32G 內存夠排上億人了吧),就是一循環隊列。把這個 ticket id 放在隊列尾。
用戶如今拿着 ticket id 向網關發起請求。網關利用一次 hash 查詢,在內存中的數組隊列裏查到它的位置,馬上返回給用戶。用戶的前端就能夠看到,他在這個網關(售票大廳)前面還有多少人等着。
這裏的關鍵是,整個隊列都在本機的內存中,查詢返回隊列中的位置,能夠實現的比一個處理靜態文件的 web server 還要高效。靜態文件至少還要去調用文件 IO 呢。靜態文件 web server 能夠處理多少併發量,不用我介紹了。
同時,前端會控制用戶拿着 ticket id 查詢隊列位置的頻率。高負載時能夠 1s 一次,甚至更長時間。爲了防止用戶本身寫腳本刷這個請求(雖然沒有太大意義,由於刷的多也不會排到前面去),若是見到同一個 ticket id 過於頻繁的查詢。好比 10s 內查詢了 20 次以上。就直接把這個 ticket id 做廢。持有這個 ticket 的人就須要從新排隊了。
對於最後排到的人,系統會生成一個惟一的不可僞造的 session id ,用戶下面就能夠經過這個 session id 去作實際的購票流程了。能夠連去真正的購票服務器,也能夠經過網關中轉。非法的 session id 會馬上斷掉,用戶一旦知道僞造 session id 幾乎不可能,只有經過 ticket id 排隊拿到,除非是惡意攻擊系統,否則不會有人亂拿 session id 去試。
咱們再給每一個 session id 設置一個最長有效時間,好比半小時。若是超過半小時尚未完整購票流程,那麼就從新去排隊。
至於同時開放多少個 session id ,也就是至關於開放多少個購票窗口,就取決於購票系統能承受的負載了。不過簡單計算一下,就知道有排隊系統保證了良好的次序,再以計算機的吞吐能力,解決 不過幾億人的購票請求,即便這些人都同來排隊,也就是一組機器幾小時的處理量而已。
這票 blog 也就是隨便寫寫,可能不太嚴謹,但意思達到了。中間有不少數據須要估算,也不是太難的事情。
爲何如今的購票系統這麼濫?關鍵在於大量的網絡帶寬,計算力浪費在了「維持次序」上。系統不穩定時,大量的只作了一半的無效的購票流程浪費掉了這 些。要響應高併發的 HTTP 請求,關鍵就在於迅速反應,不要什麼都想着從數據庫繞一圈。排隊的隊伍維持就徹底不須要使用數據庫。若是全部 HTTP 請求都馬上返回,在短期內能夠處理的 HTTP 請求量也會很是大。而若是你一下處理不了這個請求,又把 TCP 鏈接保持在那裏,就莫怪系統支持不住了。
另外,用戶看到了不斷在減小的隊列前面的人數,他們也會安心等待。只要網站頁面刷新流暢(只處理隊列信息很容易保證),用戶體驗會很好。
最後補充幾句廢話:由於鐵路購票系統不少年前就實現了內部網絡化,有成熟系統支撐,運做多年。此次作互聯網版本,必定不能放棄原有系統新來一套。否則實體購票點也在網頁上刷不出票就崩潰了。
因此要作的僅僅是怎麼作一個系統和原有系統對接。這樣風險最小。兩套系統能夠分別優化處理能力。基於這個設計起點,因此我纔不看好全部企圖取代原有系統的方案。
再補充:
一、、排隊前把身份證填好,系統編碼到 ticket 裏去。
排到了這個 ticket 只能夠用這個身份證買一張或幾張同車次的票。
註冊環節徹底能夠免了。若是須要註冊,填個密碼就 OK。用戶名能夠暫時用身份證,買完票能夠換,或者去別的服務器上換,不干擾購票流程。
你 8 點出票能夠 7 點開始排。系統反正認定你了沒人插隊。現實就如此。不要去想比一樣跟你同樣須要票的人更有優先權。
======================================================================
2、其餘網友見解:
一、唉,一個網站在大數據量訪問是掛掉,是很正常的現象,有什麼好炒做的!並且數據量不是幾萬,而是幾億!!!掛掉不丟人!!!!!
二、訂票其實還有一個特色:各車次的分管鐵路局是固定的!若是按鐵路局將系統分割,負載將大大分散。就如10086.cn同樣,他會要求你先輸入手機號後跳轉到各個分公司的系統。——但這樣異地購票或者使用異地手機的就麻煩了!
三、「網遊和扣扣在線或是登陸時訪問的更多的是用戶本身的數據」,非遊戲從業者,不過感受遊戲裏某些公共事件都是在讀寫公共資源,有時候規模也不小呢;不過副本技術確實極大的縮小了這個規模。
四、文中提到能夠使用緩存,好比如今12306是10分鐘更新一次,緩存每10分鐘都會失效,刷新緩存帶來的峯值要怎麼處理?
五、用排隊+配額申請制,
一句話「更新」(update)轉換成「插入」(insert)操做, 非要說像的話, 這像個廣告系統
六、放票模式不作更改,運力不提高,單靠優化網站仍是被罵。
假設鐵道部有能力優化,那麼能夠預見,在很短的時間內,票就被賣出去了,沒有買到票的人就會罵鐵道部,票都被黃牛買了。
七、 我以爲須要注意一點火車票和電子商務的不一樣,車次是有限的,不像淘寶這樣商品無限多,並且業務邏輯簡單得多。這 樣能夠很容易的按照車次分區,每一個車次是單獨的隊列,甚至徹底能夠放在內存,不一樣車次間不存在鎖的問題,即便同車次也能夠優化鎖粒度。另外,查詢結果沒必 要可刻意要求和實際票務狀況同步一致,能夠偏差一秒鐘,查車次和查餘票分開,這樣90%的查詢時能夠緩存的。假設車票在開票10分鐘內就被搶完, 根據每列車的座位數和每一個座位被賣的次數,能夠估算每一個車次,每秒要處理幾個請求,實際也沒幾個。
另外不一樣於電子商務的庫存,每一個座位能夠被賣幾回,用位運算實現很簡單。
人們之因此用外掛刷票,由於網站爛,登錄難,下單難,付款難等等,若是訂票過程流暢,外掛天然就沒人用了,再加上簡單可靠的防刷新和限流措施,應付如今的量沒問題,鐵道部的數據不可信,看他們說他們處理的是世界頂尖的難題就知道。
八、 技術方面不懂,不過我認爲對於12306網站的庫存管理,和普通B2C的電商庫存管理,有不少不同的地方
(1). 春運期間,運力不足,訂單始終大於庫存,但庫存是固定的,即車票的最大限額。
(2).閒暇時間,訂單小於庫存,但車次固定,座位固定,庫存也是固定的,即使是一個乘客,也會開一班列車
鐵道部幾乎不用考慮訂單和庫存之間的供求關係,普通電商過於看重庫存管理,是由於懼怕庫存積壓過多,直接影響到企業資金鍊的運行,而這個問題對於鐵道部根本不存在,鐵道部只要考慮怎麼把固定的車票數量快速,流暢,均勻的分發到每位乘客手裏就能夠了。
並且我感受,這麼多年應付春運的經驗,鐵道部對於減輕售票壓力的方案也會很成熟的,不過確定都沒有用在這個網站上面上。
九、由於無票了,用戶不甘心,反覆刷——訪問壓力大10倍
由於網頁慢、死,因此用戶不停的刷——訪問壓力大100倍
這是惡性循環:越是慢,訪問量越大
若是,網頁流暢了,哪怕仍是無票,就不存在這99倍的刷新了,而只是10倍的刷新
這是良性循環:越是快,訪問量反而少了
十、鐵老大作了一件費力不討好的事情,這事情直接拿30%的票給淘寶,30%給騰訊,本身留剩下賣,這一天幾千萬的點擊量是個巨大的廣告收入啊,本身不花一個子,衝別人大口要錢,這樣本身花了幾千萬作了個被萬人馬的東西,不如本身不花錢,輕鬆拿錢,不行還罵別人。
直接找阿里巴巴或騰訊這樣的戰略伙伴,垂拱而治,何樂而不爲呢?
十一、帶寬、核芯、存儲都不是緊缺資源,一切的根源在於票太少人太多。一到春運運量能翻好幾倍,運力只能提高一倍還不到。更好的系統是必須的,可是買不到票仍是捱罵。
一個能夠考慮的辦法是:限制300千米之內的短程票,分流到公路客運去,這樣能夠緩解一點點壓力。
最好能依據統計分析來肯定:最小的瓶頸在哪。
十二、查詢和下單等操做用到的數據,應該是從鐵道部客票系統已有的數據接口來直接獲取數據。12306.cn和客票系統共享之前的老數據庫。老舊的系統,必然適應不了如今的春運的變態需求。
1三、票量=票量-1的時候——那麼是否是隱含這個意思:針對某個車次有一條數據庫記錄用於登記剩餘的車票,致使訂購某車次的全部訂單都要等待鎖定這條記錄而且更新?若是是這麼設計的, 確實有點那啥。通常來講一個車次的票量有限,徹底能夠用一個內存中的test-and-set(reduce)的操做來進行控制,熱門車次的票量很快就會下降到零,後面的訂單都test不過去,能夠直接拒絕了。
(1)數據庫鎖不可怕,大量訂單都要鎖定同一資源纔可怕
(2)用數據庫的鎖來作票量更新和統計,帶上事務就要慢好幾拍;若是這個票量更新要和每筆訂單的事務合併提交,那就更要慢上N倍。
————可是,車次那麼多,這樣的內存量也會很是驚人!!!!
1四、併發下的數據一致性。放在內存裏也同樣要上鎖作互斥。
1五、我以爲不少人沒用過這個網站就來發表意見,這個網站雖然反覆的提示用戶太多稍後再試,但是絕對沒有說掛掉或者慢的狀況。只要登錄進去,訂單成功提交,後續 過程基本沒有頁面慢死的狀況。估計他是直接從請求中抽取一部分人登錄來控制在線用戶數量,而後再提交的訂單中抽出一部分進入付款來控制售票速度。————也就是說,自己已經使用了限流閥的方法了。
1六、這樣一個系統如何實現相對公平性,特別是此次對於電腦操做不會或較弱的人就不公平。
好比若是系統採用了排隊系統後,不從技術角度考慮, 我這裏還有幾個問題想不通。
(1). 何時開始排隊,由於你們知道票是上午十點或下午三點出票。
這個隊伍是一直存在,從系統運行時起。仍是天天從新排。
若是天天從新排,在重排的那一刻,還在系統裏的人如何理?我想這裏大部分人是踢出來,否則重排就沒意義了。
若是隊伍一直存在,進入的人是能夠永遠在裏面,仍是有策略的踢出來。這裏 永遠在裏面應該不可能。
若是有策略的踢出來,好比過一小時踢出來,那若是我要買的票在三點出票,我會想我要在兩點多一點進入系統,
而買票的人應該都是這種想法,那我應該何時開始排隊,才能保證我能在兩點多一點進入系統,天哪,感受這個無解,絕望!
(2). 假如隊伍會在天天清空並在天天三點開始排隊,但在排隊的那一刻起,領到的號靠前的條件是什麼?
應該是手速和網速。若是這樣的話,咱們能夠看看排在隊伍前面的是些什麼人:
程序刷的, 遊戲高手, IT重業人員, 都市白領, 普通受過教育接觸計算機的人 文化低(對應操做計算機的能力)的外來務工人員。
程序刷能夠禁,但效果如何就不清楚了。
若是是這樣的話,與現有系統比,不一樣之處就在於
現有系統在人們試了無數次後,給人絕望
而排隊後,一開始就給人絕望,你的號碼是1000000,在你的前面還有999999人等待處理。
對於一個具體的會操做電腦的普通人而言:
現有系統:試了無數次,網頁崩潰無數次,拼盡了耐心,終於買到了一張票。
排隊系統: 放棄吧,你在這買不到票
若是開始排隊算做比賽的話,那就是職業運動員和業餘運動員的
固然,我絕對不是在說現有的系統好!我只是在說咱們全部的技術都做好了,仍是不能從業務角度實現它的合理公平性。
沒事,瞎想的!
1七、因此我以爲正確的方法實際上是抽獎,在全部請求中抽獎,中獎的登錄入系統,在全部訂單請求中抽獎,中獎的進入支付。這樣比排隊系統公平不少,每一個訂單都有中獎的機會。
1八、雲風的方案可靠就表如今把現實中的購火車票排隊系統經過網絡來實現, 效率確定要比現實排隊高多了.
先排到隊, 而後再查詢, 再買票…總體火車購票系統的需求不就是這樣嗎?
另外, 負載小, 壓力小, 實現簡單靠譜.
排到隊的, 每一個隊列的後臺用人工一對一服務都處理的過來…
1九、我認爲鐵道部的必定都知道上文的全部東西,由於我這樣通常的人就知道了,也都會放進去考慮,個人系統正好是跨舊系統查詢與追求效能,與鐵道部需求很像,固然流量差不少。
我想主要的問題應該是當初估計尖峯流量是一億,最後來了十億,第二是後端系統承受不住,或是交換資料的部份承受不住,由於後端系統是內網舊系統,新舊系統 溝通還要兼顧效能會有極大挑戰,就算通常採用異步MQ等方式,可是量那麼大可能也是吃不完,總不能下單後十小時纔給結果。
不過這篇文章很值得想開發大系統的人去思考一下,我想仍是要通過這些問題的洗禮後天然就會了,我就是這樣系統搞掛幾回, PM下臺幾個就會了,反正怎麼換也不會換掉技術人員。鐵道部的人下次出來應該也都很強了,沒有通過這些問題真的死過的,也沒人會要你去設計這類的系統。
那麼多人用hibernate的甚至EJB,這樣的系統這類O/RM的個人經驗是擋不住的,仍是直接弄memcache/Coherence, 資料庫用 partition table等比較穩
20、我看到的幾點以下:
第一,從訪問結果來看12306.cn已經使用了CDN網絡,提供商名字忘記了,能夠查出來
第二,從訪問動態頁面的結果看,12306.cn用的應該是ngnix+Jboss
第三,12306.cn和奧運訂票系統同樣由太極集團製做
第四,從訪問結果看CDN基本上沒出什麼問題,每次500都是由Jboss返回的。我的理解,是jboss掛了
2一、如今最大的瓶頸是 提交訂單很是慢,很是困難,其次是支付接口常常出問題;登陸和查詢不是那麼緊。
提交訂單瓶頸的緣由我估計是,鐵路內部採用了網絡支付限制,大約是1萬張(忘記從哪裏看到的),可是可能同時就是n多人同時搶票,這樣,客觀形成了一個排隊效應;排在後面的人還要等前面的人去支付,再去更新數據庫,把事務加到裏面了,那是至關慢。
解決問題的首要一步是肯定這個系統達到什麼效果,鐵道部定時發票的節奏是不可能改變的,那套數據庫也是不可能改變的,支付銀行你也是沒辦法去改變的。僧多 粥少,刷票也是不可避免的,你也不可能作到人人上去就能買到票。這個系統建設的目的就是,用戶能快速登陸,查詢,若是有票可以很快買到,或者很快被告知不 被買到,並且可以避免黃牛。那麼,這個系統就是成功的。
反過來看看,如今的網站呢?用戶沒法感知是否成功,明明看了有票就是提交不上,提交上了半天不響應,而後說不讓你買了,好不容易買了交了錢了,支付那邊又超時了。
因此說,我對12306的想法是:
1.找個好的產品經理,像用戶看到票有而後就是提交不上去,一點其餘可供感知的東西都沒有,這種設計水平我就不說了;
2.優化網絡流程,例如能夠把網絡支付作到網站裏,或者徹底是異步支付;網絡放票,能夠作成按片不定時放,上午放上海,下午放杭州,隨機放票,避免形成過多的併發;
3.再就是作好負載均衡,共享數據信息之類。其實我以爲查詢之類的能夠用阿里雲之類的來搞定啊,春運期間,搞定核心數據流程就OK了。
2二、這東西,你讓百度、騰訊、阿里巴巴的團隊去作都很差使。如今那個訂票的web站點當然有不少問題。可是他們的開發人員確定接觸不到鐵道部的票務核心網,鐵道部最多給他們幾個調用接口,至於接口的響應速度等web站點的架構根本沒法涉及。
這很是相似淘寶在雙11大促銷的時候,淘寶的技術很是好,瀏覽、查詢、交易、訂單,從購物到支付寶到阿里旺旺,沒有任何問題,可是網銀頁面掛了,由於各大銀行的接口跟不上。
如今就算作一個很是牛逼容許十三億人同時併發的web站點,鐵道部的票務核心系統跟不上,你這個web作再好,也只是個架子。最多就是報錯提示能更人性化一點。
想要讓訂票變的順暢,只有重構鐵道部的票務核心系統,那估計就是一個超大的大工程。
——————如今顯然是web沒作好,後一步卡不卡就不知道了,由於瓶頸在web了
2三、你們都開始把系統的優化改造方案向着真實世界的購票大廳排隊體系思考了,這很好,但真實世界裏必定就會帶來新的人爲因素,例如銀行的排隊,就區分 我的用戶、企業用戶、VIP用戶,高級會員啥的, 只要有人敢把12306改爲排隊的購票系統,就必定會弄出來不一樣等級的用戶權限,到時候排隊的優先級、插隊等真實世界的需求就都來了,那時候看你們還 說哪一個公平來着。
2四、排隊算法的原型是火車站售票廳的排隊買票。
而網上購票的排隊,有兩種方案選擇,一是用戶先選好從哪裏到哪裏的票,再排隊。二是先排隊,排到你了再選要從哪到哪裏。
第一種,你能夠想象下這種情景。售票廳爲了加快速度,要求全部人排隊前先領個小票,上面寫着從哪到哪,若是排到你的時候沒你寫的票了,就不能買 了,你要回去從新領個小票重選車次和首末站,而後必然得回到隊尾重排,不回到隊尾的話不然你選站會妨礙後面排隊的人,致使領小票再排隊失去意義。
那買票的人能不造反嗎?他選票時有票的,排到他時沒了,得一次次重選重排,還不如如今售票廳的賣法有效率呢!
第二種,先排,排到再選票,好了,售票廳有熟練的售票員引導你,你兩三分鐘能買好,而網站,你給多少時間選票呢?一個旅客要先搜出想要的票的即時信息,可能沒有,再搜下一趟,再搜轉車方案。注意提早搜好是沒多大意義的,由於票信息很快過期,只有排到你時搜出來的纔有現實意義,只能排到再搜。那一我的也得給他三分鐘以上來選好票下單,甚至由於不少人不熟悉操做,得給更長時間。
若是這隊伍前進得比火車售票廳還慢,而網上排隊的人又必然比售票廳還多,那這隊你排一天都未必輪獲得你,還不可能每一個人都坐在電腦前死等,網站排隊變成了笑柄。
2五、我雖然也噴一次12306, 其是由於填了幾個小時的驗證碼, 有點火,
可是內心仍是佩服的, 至少如此高的頁面訪問量 , 訪問人數量, 網站而後還流暢的運行, 已經很不錯了, (即便是說用戶過多, 那只是後面系統處理不過來的緣由)
由於人太多, 拼命加硬件也能夠解決,前面查詢與後面支付問題, 可是隻是每一年春節才用一次, 不值當的, 因此我有一個小小的想法來解決這個問題,
我說的這個問題特指: 目前網站前臺接待工做作的很好, 可是把你帶到後堂, 就沒有人招呼了, 就跟我大學的時候, 吃飯的時間你們都拿水瓶去打水, 結果茶房擠破頭, 可是呢最終都有水打, 只是要耗太多的時間在那等, 我就想你們都茶壺放在那, 而後有一我的專門來打水 , 你們都估計(根據送來水壺的時間)水打好了, 就直接到拿走就好了, 節省時間
因此我想到的方法是: 採用預訂機制, (歸根結底是由於沒有排除)
1. 先查詢出我要訂哪些票(票的價格都有), 此時直接能夠支付(註冊時已經認證過身份證信息), 支付好了之後, 你們就該幹嗎幹嗎,
2. 系統根據預訂隊列, 按照系統所能使出最大能力, 生成車票, 若是可能能夠短信提醒或郵件提醒訂票成功與否, (若是有客戶選擇的時間範圍,自動滾動到下一批的隊列)
3. 最終仍是沒有票, 執行退款動做
我想這樣作的好處, 你們不用無謂的去查詢多張, 再訂啊訂啊, 增長無謂的服務器和帶寬負擔, 這樣就能夠把前臺接客的資源節省(甚至支付資源)一大部分, 並用與支付環節
2六、不少事情並非技術能夠解決的。
今天是由於網上購票系統扛不住了致使買票困難,致使罵聲一片,而若是真的是系統很牛叉能夠抵擋的了這樣的訪問,那麼新的問題又來了:在放票的一瞬間票就被秒殺光了。
結果會怎樣?
全國人民再次抱怨買不到票,甚至懷疑購票系統。
而且這樣會致使專門有人開發刷票軟件。那樣,再牛X的系統、再好的排隊系統也無濟於事的。。。由於網上處處充斥着刷票程序。這樣,真正經過人工登陸去購票的人仍是買不了票。
我卻是想到了一個非技術的可行辦法
簡單的說就是把預售改爲預定,任何人均可以實名制預定某個班次火車,而後截止預定時間後系統對旅客進行虛擬購票,第一批爲分組1,虛擬售罄後再從1車箱1 號座位開始虛擬售票,爲第2組,再售罄後就是第三組,以此類推。假如總共虛擬售出了5組,那麼在經過一個公開的方式選出某一組爲得到該車次的購票權(好比當天滬深指數之和對總組數的餘數+1即爲該組得到購買權),而後在規定的天數內付款買票便可。
這樣,即不須要那麼牛叉的並不是系統,買票的人也不須要到3點去秒殺火車票了。
2七、關於老系統的問題:
2八、系統在內存裏開一個大數組(32G 內存夠排上億人了吧),就是一循環隊列。把這個 ticket id 放在隊列尾。
======================================================================
黑傳說見解:
原文已經被我改造過,爲方便理解,添加了註解(斜體字),用通俗的詞彙替換了一些名詞(通常會在括號里加注專用名詞),並對一些說法作了通俗化的更改。
說到底三個問題:
一、火車自己的運力問題:這是根本的問題,其餘都是輔助
二、網站硬件上可能須要繼續投入,畢竟這是一我的口大國的高交互量、須要高穩健度的網站
三、業務模式能夠稍做調整,這樣不只改善了用戶體驗,並且還節約了系統資源開銷。好比排隊,能夠排到若干人再完成總交易,這樣能夠控制在本身的處理範圍內。
轉載 http://coolshell.cn/articles/6470.html
最簡單的方式就是新系統分配到必定配額的車票,獨立運行。其實這應該是最佳的方案,不然在正常狀況下,網絡的出票效率確定是高於其餘方式,若是不作配額,99%的票都會被網絡拿走。
稍微複雜一點的就是加一個仲裁的服務器,每幾分鐘分別向老系統和新系統放票,新老系統也是獨立運行。
最後,比較笨的方法新系統經過外掛的形式和老系統交互,系統串行化請求,加個流量控制以後向老系統請票,這樣新系統出票速度不會很快,但至少不會讓用戶卡在用戶交互界面上。