12306餘票是怎麼計算的?
很多時候發現,明明APP上顯示還有餘票一張,但下單後卻告知已經沒有,返回到列表刷新依然顯示1張。不知道12306這個余票是怎麼計算的,為什麼會出現這樣的情況呢
閑扯12306餘票查詢設計,肯定不太對(尤其是演算法),但是扯下思路吧。
車票售賣特點,有一張車票賣出去就會影響到整個列車線的票數:北京開往廣州的一趟列車有人買了石家莊到鄭州的票,同時意味著出發點在北京-石家莊,目的地是鄭州-廣州的所有情況都會少一張票。
1.最low的方法。每次查詢車票時候都算一遍各個站點的票數,取最小值。假設一共3站,北京,石家莊,廣州,共10個座位。有人要查詢北京-廣州的票:已經賣出了北京-石家莊9張票,石家莊-廣州1張票,取中間車站的最小值10-9=1,剩一張。這樣如果是20站查看兩頭車站的余票就要計算19次再找最小值。
為什麼說它low呢?因為票數不多但是查看余票的人太多!而且人都不傻,在沒票時候還會刷新!所以來一次訪問計算20次,10萬人加上每個黃牛開10台電腦用來刷票,瞬間訪問數達到100W,想要1秒內都得到響應需要1秒計算2000W次。
2.稍微好一點的方法。票被買走時候把每個車站余票計算一下存儲起來(Nosql,redis,memcache),查看余票時候直接返回這個結果。同樣每秒100W訪問,但是只有1000人在這秒買到了票,計算余票時候只需要1000*20=20W次,省下了1800W次的運算。
3.更「實際」一點的方法。每隔1秒同步一次數據狀態,而不是每次操作。假設12306每次查余票需要更新1k數據,一般單個網卡1G帶寬,理論能同時支持12.8W的訪問量,100W人都要看就至少有8個伺服器在工作,方法2中,余票需要存放8份的話就需要1000*8=8000次更新。還要考慮各個地區訪問的不均等性,電信,網通,鐵通等線路,整體列車余票情況數據量也有幾十K,所以1秒8000次更新也幾百兆的流量了。但是如果每隔1秒同步當前的數據狀態就好很多,這樣變成了1*8=8次,更新資料庫傳輸總量1M/s左右,差不多吧?
即便如此,還是扛不住訪問量。因為我們上邊討論的是1輛列車的情況
因為電腦上12306是用的網頁模式,查詢車票那個頁面大小6K,即便是用了ajax,刷新一次北京-鄭州,這樣的大站需要傳輸7K的純json數據,個人感覺有很多欄位都浪費了,沒什麼用。
所以就我看來,12306可以做以下改進,不是一切問題都要用技術解決的。
1.已經做了,分段放票,減少扎堆
2.12306自製一款好的搶票軟體。既然搶票權在自己手裡了,刷票軟體就少了,大量減少流量。
3.別搞現在的圖片驗證碼了,這些驗證碼刷不出來,辨識度低根本是暴力防刷,不但防機器人,真人都不行。
4.更改搶票規則,反正都是拼手速,拼網速,拼人品,何必這麼費勁大家一頓刷刷刷呢?寫個演算法分配下吧。
上邊說的是題目中」怎麼計算「這一問題,因為刷出來票買不到不是計算問題,所以...好像有些跑題?
下方的原答案答案是給沒有接觸過計算機類的各位看官看的,就是大致說了下看到票買不到的原因。
想學習高性能,高並發,分散式網站架構設計的各位大牛,還是仁者見仁智者見智吧,我真心不敢大放厥詞......
--------------------------下方原答案,上方是閑扯--------------------
謝邀,
看了這麼長時間的知乎,今天終於輪到我獻醜了。由於12306這個開放性問題太大,我就只能說下為什麼看到票卻買不到的最基本情況,權當拋磚引玉吧。
12306網站是全世界最繁忙的網路之一,由於大量的人集中在某個時間點大量訪問該網站,導致網站的並發處理能力要求非常高。我是個it小菜鳥,就給沒有計算機基礎的各位看官講講皮毛知識,大牛莫笑。
網上有這麼一個統計圖:
1.網站是怎麼運作的。
一個用戶想知道現在有沒有票,於是打開網頁,網站的後台就去查看自己的資料庫,檢索下有沒有用戶需要的數據,然後將結果展示到頁面上,就形成了頁面上的余票信息。
然後用戶看到:嘿~有票!買買買!點擊購買後,網站後台將用戶訂票的信息在資料庫中更新一下:北京到巴黎的票100張變成了99張,然後購票成功。
我這裡說的很簡單,沒有提到支付環節與退票,咱們先看這個簡單的例子,畫個圖大概是這個樣子的:
2.當有101個人都在搶票時候呢?
悲劇了,現在搶票更接近真實了,在早上8:00:00,有101個人同時要搶這100張票,會發生什麼呢?對於一個人來說,網站實行「查看有多少票」(檢索資料庫),「買一張票」(修改資料庫)的時間非常快,大約在50ms以內,所以很順利就執行了。但是101人同時點擊購買呢?全按照上圖的簡單流程,就多賣了1張票!所以需要再加一層檢查:
由於不止一個人同時買票,所以看到票數一瞬間與點擊買票的一瞬間實際上剩餘的車票數量發生了變化!因此買票時候會再次檢查余票。如下圖,雖然紅框內同時發生,不過你網速手速不行......
所以,你看到有票但是買時候沒票
3.上百萬人同時訪問一個網站呢?
基本與上邊步驟都一樣,不過網站後台把用來給用戶看的資料庫與用來給系統改寫的資料庫拆開了。也就是說有一個(多個)資料庫專門是給用戶讀的,有一個資料庫專門是用來做加減操作的。它們之間每隔一定時間就把內容同步一次。(用來改寫的資料庫叫主庫,用來讀取的資料庫叫從庫,一般情況下主庫只有一個,從庫有很多個。主庫的數據每隔一段時間同步到從庫中)
查看票數時候只訪問從庫:還有票哇,好開心!
買票時候改成了訪問主庫:你妹!說好的有票呢!
最後:實際應用中12306流程比這個複雜的多,比如同時10人搶1張票給誰?這麼大訪問量我帶寬扛不住怎麼辦?由於列車太多,少一張票就會影響整個列車線,計算量太大怎麼辦?40分鐘等待時間車票怎麼辦?所以這裡只列舉了正常情況下比較簡單的邏輯,權當一看。
@Man Lee 講了很多技術上的問題,我補充一個非技術上的:
某張票處於在預定但未支付的狀態時,會顯示成有餘票但無法購票。
這張票不是在12306上,而是卡在售票窗口上,鐵路售票終端那是先選出若干張車票,再刷身份證,再支付,再出票這麼一個流程,但如果窗口那邊有張票卡在出票之前的步驟上,余票數不一定為0,但這張票事實上已經被佔了。
至少過去的出票邏輯有這種條件,現在是不是這樣不清楚了,但可以確定的是:12306本身和鐵路售票系統是兩套獨立的系統,12306向售票系統發送請求(過去這裡還有過瓶頸),所以12306上購票、退票的瞬間可能會有兩邊狀態不一致的情況。不知道是你們想複雜了還是我想簡單了,按空間換時間思路,一個座位是一個key=座號的資源,value是該趟列車的各站區間列表,1代表售出被佔用,0代表空閑可售:
每天的每一趟列車的每一個座位就是這麼一個數據結構,a-e是停靠站點名:
seat_id -&> {
a-&>b: 0,
b-&>c: 1,
c-&>d: 1,
d-&>e: 0
}
所以:
1、一次余票查詢就是按起止站點列表,查詢指定日期和車次所有座位區間值==0的key;
2、一次售票就是更新一個key的value中部分區間的值0-&>1;
3、退票、改簽同理。
一列火車的座位數量是有限的,每天全國開通的旅客列車數量也是有限的,我估算一趟列車座位/鋪位2000個(按20節車廂每節100個座號),全國每天開通客車10000趟(這個我沒概念純瞎猜,估計應該會更少一些),每趟列車平均停靠站點數20個的話,一個億級items存儲能力的kv服務就可以滿足。而且任意兩趟列車互相的資源都是互不干涉的,所以還可以隨意切分,極端情況下可以每一天的每一趟列車獨享一個存儲節點。
我能想到的問題就是各趟列車關注度不同,大部分列車座位供大於求沒有讀寫壓力,可以合併到一個kv節點中節約成本,個別列車是千軍萬馬搶票,所以作為性能瓶頸和頻繁讀寫鎖同步的瓶頸,需要好好設計一下,結果無非就是分布服務,讀寫分離和後台同步,熱點車次區間做短時間緩存,放棄強一致性,不承諾每次刷票結果的準確性,實際上我看現在似乎也是這麼做的。
如果有考慮缺失,歡迎批評。演算法極其智障,上面猜線段樹的你確定這是?
額……寫幾句吧,不保證準確。票好像是按比例劃分的。石家莊-鄭州賣出一張,就從石家莊-鄭州的總票數上減去,而北京-鄭州原來有幾張還是幾張。都是預留的。原則上,長途預留高於短途預留,全線預留高於某個區間預留。窗口和線上分別預留。否則讓不會用網路購票的老百姓情何以堪……票只要能支付,即進入鎖定狀態,別人買不去。顯示有,點擊沒票,是查詢周期問題,系統固定時間查詢,大家看到的是歷史信息,如果正好趕上系統刷新了,就比較准……最早有10分鐘延遲,現在不清楚,策略改沒改不知道。鐵路數據量之大,不是單靠拼理論能說清楚的。
剛知道……日報評論和這裡是兩碼事……其實除了 @Man Lee 說的,12306和一般電商的區別還在於。
你買手機,庫存100,賣出去一台,庫存就是99,
但車票不一樣,,他是可以買半個的,比如Z35,北京到廣州,一共停5個站:
北京西,鄭州,武昌,長沙,廣州。如果硬卧有200張,並且全部沒有被限制,隨便買。
有人買了北京西到鄭州的一張票,12306資料庫的記錄就要變成。
當天,Z35,北京到廣州剩餘199張,鄭州到廣州剩餘1張。
有人又買了鄭州到長沙的兩張票,12306資料庫的記錄就要變成。
當天,Z35,北京到廣州剩餘198張,長沙到廣州剩餘2張,北京西到鄭州剩餘1張。
如果這時有人查Z35,北京西到廣州,余票就是198張。如果他查北京西到鄭州,余票就是199張。如果他查長沙到廣州,余票就是200張。有點暈,如果當一堆人一塊兒買呢?
來個排列組合:一共是(4!)種組合。4+3+2+1=10種組合。
只停了5個站的車次是很少見的,除了G和Z和C字頭,大部分T字頭和K字頭都是10個站往上的。
我估計12306是把車票拆成【站-站】的形式,類似於天貓,JD的「優惠套餐」。你買一張票,扣除多個【站-站】的形式「商品」
而且鐵總並不會這麼買票,按停站可能上車人數大小,離目的地的遠近,乘車時間,給各個站提前預留數量。
。比如Z35可能是,北京110張卧鋪(北京嘛,必須人多),鄭州40張,武昌40張(這都是大站,而且上車時間很好),長沙10張(乘車時間到凌晨1點半了,買的人會少)。
高鐵上這個問題有時候比較明顯,始發站一堆票沒人買,過路站沒票了,直到發車前幾天,始發站的票才會開放給中間站售賣。
純按著演算法算的話,確實比較費勁,每售出一張票,整條線路的票都要減一,這是極限情況,也是設計演算法的時候要考慮的情況。
注:以下情況屬個人猜測。
但是實際中我不這麼認為,實際中為了讓票利益最大化,通常會把票分到後半程,同時加上經驗,會給每個到站定額,也就是從始發站到目的站之間票的數目是預先分配好的,比如說,北京到廣州的車,可能60%的票給了全程,20%的票給了北京到倒數第一個大站……於是減票數的時候就沒有那麼複雜了,就是在這個區間上減1即可。
其實從日常買票時也能看出來12306就是這麼做的,比如買北京到武漢的,無票,但同一時間同一車次,多買一站就有票了,說明票是預先規劃好的。
P.S. 感謝數比點贊數多是怎麼回事。
記得以前看過一篇文章說12306的余票數據在後台是10分鐘刷新一次的,雖然查詢的時候顯示還有,但是難免這十分鐘有其他人通過窗口,電話,網路訂票等途徑已經把這張票買走了。
根據我坐火車的經驗,一般同一節車廂大部分人都是從同一個地方上車到同一個地方下車。
然後我推理,鐵路應該是按照以往的數據統計好哪個區間需要多少車票,然後這個區間就放多少張,比如(1……2……3……4)這四個站,1~4給10張,2~3給20張,2~3賣出去一張,不影響1~4的票數,因為是早就留好的。然後每個地方計算自己的餘額就好了,賣出去一張就減一,不影響別的地方的票數,所以不用太複雜的計算。
不知道這樣說能不能說明白。
一天相當於一列車,選的時間段相當於乘車區間,然後能看見哪裡有空座(全綠是可以坐的),然後系統數出來有多少全綠,就知道有多少空座。
鐵道公司有豐富的經驗,那就是不要把票提前拆分。
首先放出起點站到後半程各個大站的坐票,卧票,此時中途站車票非常少,並且拆分車票是定死的數量,不會因為某人買了短途票,再放出短途票的。(這也是為什麼買車票要講技巧,很多人多買幾站提前下車)。
在開車前某個時間點,才會把這輛車的車票完全開放。空座位任意賣。
鐵路部門這樣,是最大程度利用運力,將座位提供給長途旅客,短途旅客就忍一忍,站一下就到了。
現在的機制是分段放票,非熱門車次臨近開車的時候會有很多票放出來的。
12306計算余票,按照時間段放,每天放一點,
12306的余票機制,光用人腦想就很繞了,,也不知道鐵總是怎麼個分發票和座位的政策啊…
我認為可能是機房緩存錯誤還沒有同步吧?我也根據我的經驗獻醜一把吧。站點和站點之間的票數應該是固定的,是鐵總預先計算好的。預定一張就會少一張。關於余票有剩餘但是無法購買這個問題,情況應該可以這麼理解,12306有多達2000台伺服器來應對網上購票峰值,並且會根據用戶的IP來匹配一台合適的伺服器,但是這台伺服器與此用戶IP的網路響應並不一定是最快的(我覺得一定不是),伺服器和伺服器之間也會有一定的網路延遲。當最後一張票從A伺服器賣出,而B伺服器會延時數秒或數十秒才會收到響應從而刷新出余票,所以與B伺服器連接的用戶IP會顯示有餘票,但是實際已經賣出所以買不了。還有一點是當用戶1在急速響應下刷新出最後一張余票時,刷新出驗證碼以及提交驗證碼都會有一定的耗時,這與網速,用戶操作以及電腦和伺服器配置有關。這種耗時與上述伺服器之間的延時應該是構成有餘票但無法購買的原因。
最近使用搶票軟體一直對12306的搶票排隊系統很感興趣。我的理解是這樣的,購票時首先根據伺服器和用戶IP之間的網路響應刷新出驗證碼,其次才是用戶識別提交驗證碼完成車票預訂。兩個步驟都會耗費一定的時間,但哪一步是最關鍵的步驟?我認為第一步是。比如某兩個站點只有一張票,而只有兩個人去搶,用戶A與伺服器之間的網路響應有50ms,用戶B與伺服器之間的網路響應達200ms;兩者相繼刷新出驗證碼之後,A反應比較慢,10秒時候才完成識別驗證碼;而B反應快,5s隨即完成驗證碼識別。最後A用了10s+完成了搶票,B則用了5s+。那麼誰會買到票呢?我想應該是A。即12306會在技術上對排隊對搶票進行影響(伺服器與用戶之間的網速),而非用戶操作上對搶票進行掣肘(肉眼識別以及提交驗證碼)。所以許多搶票軟體會過濾識別用戶主機IP 與12306兩千台伺服器之間的網路響應,並且在搶票時自動會在響應最好的伺服器讀取余票等相關信息方便用戶搶票,只要用戶在合理的時間識別驗證碼(&<10s???),搶到票幾率應會比普通搶票要大。
初始余票猜測是這麼分配的這裡只是舉個例子,數量是我隨手寫的。
之後每個區間段賣出一張票就把對應區間票數減一。資料庫中數據大概十幾分鐘更新一次。
離開車前三天,會根據剩餘票數情況重新調整區間余票。
開車前兩小時,會打破區間的限制,直接放票。此時,之前買了後退票的票也會大量集中放出。(為了防黃牛,12306不是一有人退票就釋放該票源,而是隨機時間放出)
至於,題主所說顯示有票買不到,貌似是因為有人選了座不付款。
這裡說個這麼多年買票的經驗,如果不是始發站,開車前三天之前沒有票,就多花點錢延長區間。如果快開車了還沒有票,就減小區間,先上車,再補站票。不可能純實時顯示正確數據,這麼做沒有任何價值,技術上代價極其大。
所以你看到的數字可能只是xx分鐘前的一個快照或者cdn上的一個緩存
其實票還有很多,只是12306的分票邏輯造成很多人買不到票
很正常啊,有可能隊列堆積。沒有實施處理隊列消息。到付款時刻,才發現沒票了。
假設,你查詢了一次數據,這次數據過期的時間是2分鐘。那麼在這兩分鐘的時間內,就會有下面的情況出現。
刷刷刷
-&>卧槽還有一張余票
-&>卧槽我運氣真好,開心五秒先
-&>艾瑪,票是00:30的,是明天凌晨還是後天凌晨?!
-&>等等我看看票價對不對
-&>票被別人買走(其他客戶或售票點)
-&>嗯嗯,信息都對著呢,好,我要下單了。(時間過了一分鐘)
-&>咦?訂單不成功?卧槽被人搶了?!
-&>我再刷一下
-&>刷刷刷
-&>這時,有人退票了
-&>數據刷新了,有一張余票(為毛還有一張?12306是sb么?余票到底怎麼算的?黑人問號臉???)
-&>請返回第一個箭頭,腦補while循環
是不是明明已經下單了,這張票已經讓寶貝買走了,在有一張顯示和寶貝沒有關係啊,回答是否正確,謝謝
推薦閱讀:
※為什麼很多產品經理都不願意做後台?
※各種問題都可以用/適合用「頭腦風暴」方法來解決嗎?
※有哪些腦殘產品設計?
※為什麼麥當勞的漢堡包有些是用紙包,有些是用盒子包,有的先用紙包再用盒子包?
※岡本003和岡本002的用戶體驗差異究竟有多大?
TAG:互聯網 | 產品設計 | 技術架構 | 火車票購買 | 12306中國鐵路客戶服務中心 |