我曾在淘寶寫過一段時(shí)間代碼,2012年在一家百強(qiáng)民企做電商副總,當(dāng)時(shí)在極為艱苦的條件下帶隊(duì)開發(fā)了一個(gè)B2C網(wǎng)站,走支付寶和銀聯(lián)支付通道,年?duì)I業(yè)額千萬級(當(dāng)然實(shí)在太少了,我只是說這個(gè)網(wǎng)站投入了實(shí)際的運(yùn)營)。
也就在那個(gè)時(shí)候,我對12306嗤之以鼻,覺得他們做得太爛了,認(rèn)為自己能帶隊(duì)花幾百萬半年時(shí)間做個(gè)好的出來。于是我狂妄地想做一個(gè)開源的訂票系統(tǒng)給他們。我花了一個(gè)星期時(shí)間思考建立數(shù)據(jù)模型,思考到庫存這一步的時(shí)候,我才發(fā)現(xiàn),12306的庫存復(fù)雜性比淘寶、京東高很多倍,運(yùn)算量也大很多倍。傳統(tǒng)的分布式數(shù)據(jù)庫、緩存、負(fù)載均衡技術(shù)并不能恰好滿足12306的需求。
在平時(shí),12306也就是個(gè)正常的電商網(wǎng)站。但一到黃金周,12306就是一個(gè)全站所有商品都秒殺,所有SKU都是動態(tài)庫存的變態(tài)。
即使不考慮線下既有的電話、代售點(diǎn)等渠道,要實(shí)現(xiàn)一個(gè)12306,最少最少也是千萬級別的硬件投入(這是當(dāng)時(shí)的估算,沒有精算,可能與實(shí)際相差較大,總之,我說得不一定對,12306的業(yè)務(wù)也許沒我說的那么復(fù)雜,但也絕不是某些人噴的那么簡單),軟件和人力另算。那些叫囂只要40臺服務(wù)器、只要2個(gè)架構(gòu)師4個(gè)程序員、大談分庫分表和前端CDN的人們,只是紙上談兵罷了。所謂初生牛犢不怕虎,做了三年CMS和BBS,就以這個(gè)經(jīng)驗(yàn)來噴12306,未免太天真了。
媒體人噴12306,是他們不懂技術(shù),沒有能力和耐心來分析背后的難度。技術(shù)人員噴,則是因?yàn)榇蟛糠值募夹g(shù)人員在短時(shí)間思考時(shí),容易陷入過于樂觀的誤區(qū),經(jīng)典的例子就是估算工作量,程序員們往往容易估算出一個(gè)超短的工期,把寫程序的工作樂觀地想象成了打字員照稿敲鍵盤的工作。
知乎那篇文章,我覺得不是洗地。排名第一和第二的答案都說得很客觀。淘寶技術(shù)是比12306強(qiáng)大很多倍,淘寶現(xiàn)在的系統(tǒng)也是花了10倍于12306的錢、時(shí)間和人才做起來的。根本原因還是鐵路運(yùn)力不能滿足春運(yùn)需求,淘寶也解決不了這個(gè)問題。
12306這一年來進(jìn)步非常大。從前段動畫驗(yàn)證碼、分時(shí)段搶票,到后端去小型機(jī)、虛擬化、內(nèi)存數(shù)據(jù)庫的運(yùn)用??梢哉f,12306是中國政府機(jī)關(guān)做的最強(qiáng)大的網(wǎng)站(電商系統(tǒng)),能在短短一兩年內(nèi)做出這樣的改變,幾乎是個(gè)奇跡,就連一些市場化的民企都望塵莫及,甚至一些上市公司都比不上它?。ū热?1job和ctrip)。
事非經(jīng)過不知難,在網(wǎng)上批判12306的人,大部分還是形成了【國企 = 壟斷 + 腐敗 + 低效 】的思維定勢。小部分是真的輕視了它的難度。
至于12306一期工程3個(gè)億(含硬件)貴不貴我不評價(jià),我只提供一個(gè)數(shù)字供參考,百度一年的研發(fā)費(fèi)用(不含硬件)是10億,這個(gè)數(shù)字來自百度財(cái)報(bào)。網(wǎng)上能查到。3億看起來好大一個(gè)數(shù)字,真用到超大型的電商系統(tǒng)、搜索引擎系統(tǒng)里面,其實(shí)也不算什么天文數(shù)字了。
再解釋一下,為什么秒殺壓力大,以及為什么12306的動態(tài)庫存很復(fù)雜。
先說秒殺
2013年12月25日前后,天貓搞了一個(gè)圣誕季積分兌換活動,持續(xù)幾天。25號上午10點(diǎn)12分,放出了15000個(gè)天貓魔盒(淘寶集市有人賣,大概190-230塊),從成交記錄上看,是19秒內(nèi)全部搶完。
實(shí)際上,我也參加秒殺了,那天的題目特別簡單(請輸入xxx漢字的拼音首字母),我應(yīng)該是5秒內(nèi)答題完成并提交訂單,結(jié)果告訴我排隊(duì)的人太多,擠不進(jìn)去,并提示14秒以后重試。人太多就是因?yàn)轭}目太簡單了,門檻越低,5秒內(nèi)擠進(jìn)去的人也越多嘛,如果題目換成【2克濃度為3%的U235在大亞灣核電站能發(fā)多少KW的電】,5分鐘之內(nèi)也不會有1萬5千人跟我競爭。
我想,14秒以后哪還有我的事情呀,于是重新答題秒殺,結(jié)果出現(xiàn)了服務(wù)器錯(cuò)誤的頁面。反復(fù)刷新幾次,就告訴秒殺結(jié)束了。
在群里問了一下同事,有不到10個(gè)人回答我,都說沒秒到(也可能秒到的人悶聲發(fā)大財(cái),不回復(fù)我)。
淘寶是什么技術(shù)水平呢,淘寶有至少4000技術(shù)人員,至少4萬臺服務(wù)器(這都是兩年前的公開數(shù)據(jù)了,按規(guī)定可以談?wù)摚?013年11月11日成交額351億,2012年全年成交額超過1萬億。
淘寶擁有各種自主研發(fā)團(tuán)隊(duì):服務(wù)器、交換機(jī)(網(wǎng)上可以搜索到淘寶公開的綠色服務(wù)器開放標(biāo)準(zhǔn));操作系統(tǒng)(Linux Kernel taobao版,yunos手機(jī)操作系統(tǒng)是阿里云的,暫時(shí)不計(jì)入)、Web服務(wù)器(Tengine)、Java語言虛擬機(jī)(JVM taobao版)、數(shù)據(jù)庫(MySQL內(nèi)核 taobao版,google和facebook也有自己的版本,HBase淘寶版、還有自己全部從頭開發(fā)的OceanBase)、負(fù)載均衡器(LVS,LVS始創(chuàng)人就在淘寶,擔(dān)任研究員)、Java運(yùn)行容器(Jboss,其創(chuàng)始人之一,王文彬,也在淘寶,擔(dān)任副總裁)。
淘寶還有數(shù)不清的開源項(xiàng)目和中間件,如高性能Java通信中間件HSF、分布式數(shù)據(jù)庫中間件TDDL、異步消息系統(tǒng)notify等等等等。
以淘寶這樣的技術(shù)水平,也不能做到秒殺時(shí)讓每個(gè)用戶都沒有擁擠感,為什么呢?
一是要尊重物理原理,一臺服務(wù)器一秒鐘能承受的計(jì)算量是有極限的,任你怎么優(yōu)化,采用多高效的算法和編程語言,都突破不了某個(gè)極限,比方說汽車發(fā)動機(jī)驅(qū)動的F1賽車至今也不能突破400公里的時(shí)速(超音速推進(jìn)號那個(gè)1千多公里的時(shí)速不能算,那是飛機(jī)引擎驅(qū)動的)。再往深了說,就不容易懂了。感興趣的可以從著名的C10K問題開始看起。
二是要考慮經(jīng)濟(jì)效益,十一黃金周的時(shí)候,北京主城區(qū)到八達(dá)嶺長城的路堵得嚴(yán)嚴(yán)實(shí)實(shí),但不能因?yàn)辄S金周的高峰,就把這段路修成長安街那樣10車道的高速公路。否則的話,花費(fèi)天文數(shù)字(真的是天文數(shù)字,12306那3個(gè)億大概只夠修1-3公里)。修了一段路,黃金周是可以飆到80公里/小時(shí)了,可平時(shí)呢,拿來給兩邊的居民曬谷子?
淘寶目前的硬件和帶寬數(shù)量,已經(jīng)超出日常運(yùn)營的需求了,就是留了相當(dāng)大的余量給大促銷(眾所周知的是雙十一,雙十二,其實(shí)基本每個(gè)季度都有大促銷,每個(gè)月都有促銷,甚至天天都在促銷——聚劃算)。amazon當(dāng)年就是為了應(yīng)對黑色星期五的大促銷購置了大量的服務(wù)器,平時(shí)訂單量沒那么大了,amazon就把富余的服務(wù)器拿來搞云計(jì)算了。順便說一下,阿里云是當(dāng)今中國第一世界數(shù)一數(shù)二的云計(jì)算服務(wù)商,和amazon走的路也有點(diǎn)像。
再說動態(tài)庫存
淘寶秒殺天貓魔盒的時(shí)候,只有一個(gè)商品(行話叫做SKU),它的庫存是15000個(gè)。有一個(gè)人秒殺到了,庫存就減1,19秒賣完的,一秒要成功產(chǎn)生789個(gè)訂單(下訂單的請求可能是8萬個(gè),只是可能啊,非實(shí)際數(shù)字,也可能是1萬個(gè),用于說明一下壯觀程度)。想象一下,你在廣場上賣火車票,一秒鐘有8萬人舉著錢對你喊:賣給我!
上過大學(xué)的人都知道,比秒小的時(shí)間單位還有毫秒、皮秒、飛秒。但交易系統(tǒng)登記一個(gè)交易可不像電子繞著原子核跑一圈那么簡單,它要做這些事:檢查是否惡意訪問、取到系統(tǒng)時(shí)間、取到顧客默認(rèn)收貨地址、核對顧客秒殺資格(當(dāng)時(shí)的規(guī)定是天貓T2.T3達(dá)人)、生成訂單號、把顧客ID系統(tǒng)時(shí)間訂單號收貨地址寫入訂單系統(tǒng)、扣除顧客天貓積分、商品庫存減一、給顧客打標(biāo)記(每人只能秒一個(gè),下次不能秒了)等等,這每一件事都要花費(fèi)毫秒級別的時(shí)間,這些操作加起來的時(shí)間可能是接近1秒級別的,但由于淘寶的服務(wù)器比較強(qiáng)悍,而且采用了分布式和集群技術(shù),結(jié)果比1秒理想一點(diǎn)。但即使有1萬臺服務(wù)器,也不能把這個(gè)時(shí)間稀釋成萬分之一秒,因?yàn)椋唐分挥幸环N,它有15000個(gè)庫存,對應(yīng)的數(shù)據(jù)庫記錄只有一行,所有的交易請求都要到這里來處理。
能不能把這15000個(gè)拆分成5000個(gè)商品并分配到5000臺服務(wù)器上呢?那樣不就可以5000臺服務(wù)器同時(shí)處理了嗎?答案是不能,首先,5000個(gè)商品,意味著有5000個(gè)商品詳情頁,5000個(gè)購買按鈕,這對前期的營銷、引流是個(gè)災(zāi)難?;旧暇蜎]法做引流入口了,顯然這違背了商業(yè)管理原則,人為增加了信息混亂程度。其次,天貓魔盒秒殺也不是啥大事,即使按官方標(biāo)價(jià)399元來計(jì)算,也就6百萬的交易。如果6百萬的交易要花費(fèi)那么大的配套成本,那就太不劃算了。再次,淘寶有十幾億商品,這十幾億商品的展示交易和管理,本來就是分布到上萬臺服務(wù)器上去了。沒有必要再把每個(gè)商品按庫存拆成多個(gè)商品了。
這789人搶到了,還不一定會付款(99積分換天貓魔盒還好一點(diǎn),不需要去網(wǎng)銀,成本也極低,大部分是會付款的,3999秒殺iPhone 5S就不一定,有人可能網(wǎng)銀有問題,有人可能改變主意不想要了),所以就又帶來訂單取消重新恢復(fù)庫存的問題。還有想要的消費(fèi)者們,會認(rèn)為還有機(jī)會,繼續(xù)在前臺刷一會兒,最終這個(gè)秒殺會被熱情的消費(fèi)者們猛刷30秒到1分鐘。
(超賣這一部分科普筆法寫得有錯(cuò)誤,鑒于12306目前全在內(nèi)存數(shù)據(jù)庫中讀寫,沒有產(chǎn)生超賣問題,先把這個(gè)段落刪去。感謝@吹西門的雪 指正)
好了,講了這半天淘寶,可以說12306了吧?
我以北京西到深圳北的G71次高鐵為例(這里只考慮南下的方向,不考慮深圳北到北京西的,那是另外一個(gè)車次,叫G72),它有17個(gè)站(北京西是01號站,深圳北是17號站),3種座位(商務(wù)、一等、二等)。表面看起來,這不就是3個(gè)商品嗎?G71商務(wù)座、G71一等座、G71二等座。大部分輕易噴12306的技術(shù)人員(包括某些中等規(guī)模公司的專家、CTO)就是在這里栽第一個(gè)跟頭的。
實(shí)際上,G71有136 * 3 = 408種商品(408個(gè)SKU),怎么算來的?請看:
如果賣北京西始發(fā)的,有16種賣法(因?yàn)楹竺嬗?6個(gè)站),北京西到:保定、石家莊、鄭州、武漢、長沙、廣州、虎門、深圳……都是一個(gè)獨(dú)立的商品,
同理,石家莊上車的,有15種下車的可能,以此類推,單以上下車的站來計(jì)算,有136種票:16+15+14….+2+1=136。每種票都有3種座位,一共是408個(gè)商品。
好了,再看出票時(shí)怎么減庫存,由于商務(wù)、一等、二等三種座位數(shù)是獨(dú)立的,庫存操作也是一樣的,下文我就不再提座位的差別的,只討論出發(fā)與到達(dá)站。另外,下文說的是理論世界的模型,不是說12306的數(shù)據(jù)庫就是這么設(shè)計(jì)的。
旅客A買了一張北京西(01號站)到保定東(02號站)的,那【北京西到保定東】這個(gè)商品的庫存就要減一,同時(shí),北京西到石家莊、鄭州、武漢、長沙、廣州、虎門、深圳等15個(gè)站臺的商品庫存也要減一,也就是說,出一張北京到保定東的票,實(shí)際上要減16個(gè)商品的庫存!
這還不是最復(fù)雜的,如果旅客B買了一張北京西(01號站)到深圳北(17號站)的票,除了【北京西到深圳北】這個(gè)商品的庫存要減一,北京西到保定東、石家莊、鄭州、武漢、長沙、廣州、虎門等15個(gè)站臺的商品庫存也要減1,保定東到石家莊、鄭州、武漢、長沙、廣州、虎門、深圳北等15個(gè)站臺的商品庫存要減1。。。總計(jì)要減庫存的商品數(shù)是16+15+14+。。。。+1=120個(gè)。
當(dāng)然,也不是每一張票都的庫存都完全這樣實(shí)時(shí)計(jì)算,可以根據(jù)往年的運(yùn)營情況,在黃金周這樣的高峰時(shí)段,預(yù)先對票做一些分配,比如北京到武漢的長途多一點(diǎn),保定到石家莊的短途少一點(diǎn)。我沒有證據(jù)證實(shí)鐵道部這樣做了,但我相信,在還沒有12306網(wǎng)站的時(shí)候,鐵道部就有這種人工預(yù)分配的策略了。
想象一下,8萬人舉著錢對你高喊:賣給我。你好不容易在錢堆里找到一只手,拿了他的錢,轉(zhuǎn)身找120個(gè)同事,告訴他們減庫存,而這120個(gè)同事也和你一樣被8萬人圍著;也和你一樣,每賣出一個(gè)商品要找?guī)资畟€(gè)人減庫存。。。這就是12306動態(tài)庫存的變態(tài)之處。比你平時(shí)買東西的任何網(wǎng)站的庫存機(jī)制都復(fù)雜幾十上百倍。
再說一下?lián)屍辈寮?/strong>
機(jī)器永遠(yuǎn)比人快,當(dāng)你好不容易從8萬人里突出重圍,來到了柜臺前,你發(fā)現(xiàn),我操,來了10萬根綁著錢的竹竿,而且當(dāng)有退票出來的時(shí)候,你要闖過3層人肉才能接近柜臺,竹竿在8個(gè)人身后一伸,錢就到了柜臺前。你低頭看了一眼手機(jī),票就沒了,竹竿卻永遠(yuǎn)在那里伸著,永不低頭,永不眨眼。如果沒有這10萬根竹竿,雖然你很可能還是搶不到票,但不至于沮喪成這樣:我TM為什么總是手最慢的一個(gè)???!
防機(jī)器人搶票,也不是加個(gè)圖片驗(yàn)證碼那么簡單。我寫過文章系統(tǒng)性分析過,圖片驗(yàn)證碼有6種機(jī)器暴力破解的辦法,搶票插件用的是我說的第三種,OCR識別。Google采用的Wave波形字母已經(jīng)能比較好地防住機(jī)器OCR了,ems.com.cn上的驗(yàn)證碼就是反面教材,機(jī)器OCR成功率接近100%,12306的比ems的圖片驗(yàn)證碼強(qiáng)一點(diǎn)。不過,驗(yàn)證碼設(shè)置得復(fù)雜一點(diǎn)吧,人們要噴:這只是便宜大學(xué)生和辦公室白領(lǐng),農(nóng)民工連26個(gè)字母都認(rèn)不齊,怎么搞?搞動畫驗(yàn)證碼吧,也有人噴,視力不好的人怎么辦?最后驗(yàn)證碼搞得太簡單了,皆大歡喜了,其實(shí)最高興的是開發(fā)搶票插件的公司。
就算采用了機(jī)器完全不可能識別的驗(yàn)證碼,也防不住社會工程學(xué)的破解辦法。招募一堆網(wǎng)吧打游戲的青少年朋友,每成功輸入50個(gè)驗(yàn)證碼給1塊錢,或者等值的虛擬貨幣、游戲裝備,我保證想賺這個(gè)錢的人數(shù)不勝數(shù)。這點(diǎn)錢對轉(zhuǎn)賣車票的利潤而言,是可以接受的成本。有沒有什么技術(shù)可以防住社會工程學(xué)的破解辦法呢?能防住網(wǎng)吧青少年的驗(yàn)證碼只有【2克濃度為3%的U235在大亞灣核電站能發(fā)多少KW的電】。
沒有歷史包袱從零起步的交易系統(tǒng)?
以上討論只是把12306當(dāng)成和淘寶一樣沒有歷史包袱從零起步的交易系統(tǒng),實(shí)際上,它不是,它后面的票池,還有電話售票、火車站售票、代售點(diǎn)售票等多個(gè)傳統(tǒng)渠道要服務(wù)。除了客運(yùn)服務(wù),12306還有全國最大(很可能也是全球最大)的大宗物資貨運(yùn)系統(tǒng)。
架空政策(包括定價(jià)政策、警方打擊黃牛政策、身份驗(yàn)證政策)談技術(shù),是不可能解決春運(yùn)搶票困局的,要想讓春運(yùn)的時(shí)候每個(gè)人在12306搶票都毫無擁擠感(但不一定能搶到票,鐵路運(yùn)力擺在那),那就是逼著12306買一大堆服務(wù)器對付春運(yùn),春運(yùn)過去后,成為跟amazon一樣牛逼的云計(jì)算服務(wù)商。和逼北京修一條10車道的高速公路去八達(dá)嶺長城一個(gè)道理。
目前的12306技術(shù)上是還有問題,比如,搶票高峰,輸入個(gè)身份證號和圖片驗(yàn)證碼都卡得要死(本人親測),服務(wù)器端繁忙,你瀏覽器端卡什么呀。
但人家在進(jìn)步。相信2014年春運(yùn)的時(shí)候,技術(shù)已經(jīng)不再是一票難求的主要問題。在鐵路運(yùn)力不可能神速增加(孫中山先生計(jì)劃的20萬公里鐵路,土共修了快70年,才修到10萬公里)的情況下,要做到春運(yùn)更公平地買票,需要停靠政策調(diào)整。
非常時(shí)期有什么解決方案?
下文針對的是春節(jié)國慶這種非常暑期。其它時(shí)期,大部分線路保持現(xiàn)狀就行了,問題不大,極少部分票源緊張的線路可以按春運(yùn)處理:
1.拍賣法,價(jià)高者得之
當(dāng)硬座票拍出飛機(jī)票價(jià)格的時(shí)候,相信票就不難買了(可惜就是貴了),也沒有那么多黃牛了。要說淘寶有什么能幫12306一下子搞定技術(shù)問題的,淘寶的拍賣系統(tǒng)可以幫忙,浙江省高院在淘寶拍賣一年多,成交26億。
可惜這個(gè)方法不可能實(shí)行。現(xiàn)在的高鐵票價(jià)都被媒體和意見領(lǐng)袖噴成啥樣了,何況是拍賣。再說,火車票畢竟是生存之剛需,票價(jià)20年來不漲本來就有照顧補(bǔ)貼的成分在里面,全拍賣可能也是不妥當(dāng)。
2.抽簽法,運(yùn)氣好者得之
開車前2個(gè)月開放報(bào)名,開車前7天抽簽,中途可取消。預(yù)存票款,抽不中退款。上傳身份證和正臉自拍照,機(jī)器核對。
這樣的話,攔截黃牛的成功率就高很多了,黃牛可以預(yù)存票款,可以找到大量真實(shí)身份證號,你黃牛再讓每個(gè)給你身份證號的人把身份證照片和臉部自拍也給你試試?即使有人真想找黃牛,給身份證照片還是會猶豫一下吧。而且中間手工操作多了很多,黃牛成本提高,還不一定搞得到票。反正都是碰運(yùn)氣,我想真正的消費(fèi)者還是會選擇自己先去碰運(yùn)氣吧。
這個(gè)方法實(shí)施難度也大,無論怎么設(shè)計(jì)抽簽規(guī)則,必然有人大叫“有黑幕,不要相信政府”。
開車前7天出抽簽結(jié)果,改變行程的人應(yīng)該在7天前就能決定改還是不改了。沒抽到的也還有時(shí)間想別的辦法。當(dāng)然不一定是7天,15天,10天也可以,具體幾天要有數(shù)據(jù)模型來算。
3.拍賣 + 抽簽
軟臥、高鐵商務(wù)座等高價(jià)位的,拍賣,反正買這個(gè)的是經(jīng)濟(jì)能力相對較強(qiáng)的。那就拼誰經(jīng)濟(jì)能力更強(qiáng)吧。
硬座、站票抽簽。
4.憑身份證進(jìn)站,車票跟發(fā)票一樣,是報(bào)銷憑證,不是進(jìn)站憑證;退票后錢進(jìn)入12306賬戶,不可提現(xiàn),只可該乘客下次乘車用;黃金周期間,個(gè)人賬號最多訂購10張票,這個(gè)辦法可以打擊黃牛囤票再轉(zhuǎn)賣。運(yùn)行一段時(shí)間后,按賬戶余額弄個(gè)排行榜就知道誰是黃牛了,可惜這個(gè)需要車站設(shè)備改造配合。
作者:作者:西西河 - 代碼狗(寫于 2014年1月)
鏈接:http://www.cchere.com/topic/3965719