大麥網技術二三事

李陳忠發表於2017-10-23

杭州周杰倫2017年專案,大麥網搶票系統搶癱了,據傳阿里內部炸了鍋,大麥在阿里序列裡直接進入了被鄙視鏈的第一名,江湖上也是聲名狼藉。作為大麥故人(大麥網前技術總監及初創團隊核心),我已經是不止一次的在各種場合聽到諸如“大麥網技術水平太爛了”、“大麥基本沒什麼技術含量”、“大麥的技術還處於舊石器時代”等評論。對於差評,系統當機擺在眼前,也確實是無需辯駁;但作為大麥網初創團隊成員與最有資格談論大麥技術的成員之一,猶豫再三,還是決定聊一聊大麥網的一些技術往事,只為緬懷。

我是2008年4月份加入的大麥,這是我第一份工作(如果不算大學時兼職的話,初中開玩Q-BASIC,上四年大學幹了兩年兼職,寫了三四十萬行程式碼,挑戰杯大賽獲獎者)。

那時候還沒有“大麥網”,只有“中國票務線上”。在某個陽光明媚的週末上午,我走進了東中街32號樓旁邊的元嘉國際公寓821面試,這是一套loft的公寓。進門前我曾一度懷疑自己是不是進了傳銷窩點,怎麼看也不像一家正規公司。我和我的面試官、日後的好哥們-王威聊得很投機,我們都喜歡技術,屬於把技術當生活的那類人,並不覺得這是一份工作,而是既讓自己玩、竟然還給錢的美事(玩中文分詞、搜尋引擎、元搜尋…),出門時我已經決定要來這家公司了。

2009年初我們在快速做死了旅遊、機票、SNS、酒店、客棧、電影……等一系列產品後,最終決定把演出購票做深,打造一個電商模式的購票網站。對於決定去做這件事情也有點逗,我、王威做死了上述一系列產品之後,心情很沮喪,滿滿的挫敗感,我們一起在東方銀座停車場的花壇邊上坐了一晚上,不說話、抽著煙、看著來來往往的人,摳著腳(不是我)。記不清後來怎麼起的話題,總之最後把煙一丟,決定做個演出購票電商網站,然後第二天上班就開始開發,接下來就是滿滿的五年加班歲月,平均每天工作超過12小時,幾乎沒有週末和節假日,天天見識凌晨四點鐘的北京街頭。神馬睡袋帳篷的Low爆了,我們是椅子上、會議桌上、檯球桌下、辦公司的角落地上……都睡遍了。

大麥網站部分的整個技術體系我一手搭建(截止至2015年大麥重構前),同時我也是大麥使用者庫裡的第2號使用者(哈哈哈),2010年定崗技術總監,至2013年12月我離開前,大麥網歷年歷屆的搶票準備及協同工作都是由我主持。2013之所以離開,是因為大資料剛剛興起,我希望能夠在大麥內啟動大資料相關的事情,但是那會大家都看不懂,所以一直沒得到支援,於是便放棄了大麥的期權離開,成為一家公司的技術合夥人,做了一些資料相關的業務(所以也淪為了大麥初創團隊裡唯一一個沒有期權收益的成員)。

元嘉國際公寓821,大麥網的起點
元嘉國際公寓821,大麥網的起點

談起大麥,大麥是一家悶頭做事、行事低調的公司,大麥的技術團隊也非常低調。我在大麥的6年時光裡,我們從來沒有在任何公眾場合發過言說過事。實際上,在大麥網前期,由於硬體的投入很少,所以對技術的要求到了近乎苛刻的地步。2013年我離開前,對團隊硬性要求是頁面服務端執行耗時100毫秒算及格線(含選座、下單),每個頁面上線前先壓力測試看達標不達標,不達標全部打回繼續優化。我們對程式碼的優化是先把程式碼註釋成空頁面,然後一行一行加程式碼,看執行耗時增加了多少去優化的。我曾逼著某同學連續加班了三個通宵,把業務頁面從1秒多,優化到了300毫秒、繼續優化到了100毫秒、60毫秒……(幸好不恨我~)。我們用4臺伺服器扛過幾萬人的瘋狂搶票沒有掛(2009李宇春演唱會),雖然系統反應慢了點,說起來都是淚……慢的原因之一是因為買的水晶頭質量太差,其中一臺伺服器內網線的水晶頭鬆了,流量全部走了外網……(雖然我們窮~但那會我們人窮志不短,妄圖拿三五臺伺服器開始做一個改變幾千萬人購票體驗的事情)

大麥技術團隊前期奉行的是工匠精神,有許多令我們驕傲的東西。或許是後續的傳承出現了偏差。言歸正傳正傳聊技術,本文不聊負載均衡、分表分庫、SOA、快取、CDN、雲……等一系列滿街跑的程式設計師不管實操沒實操過,一張口都能侃幾句的“大併發高負載解決方案”。時間、篇幅、精力都有限,也無法長篇大論,因此我會從幾處細節切面入手,簡單聊一聊。

01 線上選座核心技術

線上選座,現在已經應用得非常普遍了,從演出到電影、到機票,被應用得爐火純青。如果我沒有記錯的話,大麥應該是國內最早線上上對C使用者提供線上選座的公司,第一個版本我們參考了TicketMaster ,09年李宇春第一次正式使用 。 當時是遙遙領先於國內任何一家公司的同類產品,後續競友們也出了一系列同類產品,且投入的服務裝置甚至比大麥還多 ,卻無一例外沒扛過搶票高併發。原因在於大麥的線上選座在一些技術細節上處理得非常好,甚至現有的大麥技術團隊都並不清楚自己的系統是怎麼做的,實際上大麥的技術方案還沒有實施到極致,還有提升的空間。離開後我進一步思考了下可以採用以下方案:

1、基於bit設計的座位資料傳輸協議

注意,是Bit,不是Byte、不是JSON、不是XML。截至今日,我看了許多線上選座的產品,包括行業內所謂神級公司的,無一例外都在用JSON,最多做了個GZIP壓縮。當然了,財大氣粗可以拿伺服器和網路頻寬去扛,也是無可厚非的。壓縮演算法是通用演算法,耗費伺服器計算資源,在資料協議本身沒有優化到極致情況下我是禁止使用的。大麥線上選座的第一個版本用的是JSON資料做座點陣圖資料傳輸,一個場次座位資料量將近1MB,在開啟選座的時候能看到進度條載入的明顯痕跡,而後來新資料協議實施完成後根本看不到進度條載入,因為同等資訊資料傳輸量已縮小到了1KB左右。不僅僅是資料量縮小了1000倍的事情,基礎好的同學應該能看明白這意味著什麼,1KB的報文比1MB的報文在IP資料分片傳輸上的效能和可靠性要高出多少。

這裡會牽涉到大量的位運算和資料型別基礎知識,所以搞不懂long型資料64bit相比int型資料32bit意味著什麼的小夥伴請繞行。

1)文字協議換成非文字協議

JSON等文字協議的優點在於簡單直觀,肉眼可見,好開發、好維護。但是有些關鍵場合追求極致還是非常有必要的。以一個數字“1234567890”為例,在JSON協議中它需要佔用的位元組數是:10byte=80bit,而用int型只有4byte=32bit,對於一個座位ID動不動10位數字以上的系統,光傳輸1000個座位ID有效資料量差別就是:(80-32)*1000=48000bit=6000byte 約等於6KB,再加上JSON格式裡的“{”、“}”、“=”、"""、動輒五六個英文字元的“seatid”屬性名……可想而知多了多少資料量。

2)絕對值用相對值代替

講這條之前,我們先看則科幻小故事,我便是受了這個故事的啟發:

“一個外星人偶然來到了地球,覺得地球很有意思,想帶資料回去。但是因為是偶然來的,自己的飛船不夠大,不可能放下很多樣本。於是外星人找到了一套大英百科全書,覺得這個很好,準備帶回去。但是發現那還不行,因為那一套太多了,還是太重了。外星人就把字母全部用數字代替,於是外星人得到了一串長長的數字,通過飛船的計算機全部按照百科全書順序排列好後準備帶走,但是外星人又發現飛船上的計算機還要儲存很多畫面和視訊,那串大英百科全書數字太長了,佔了很多硬碟空間——我們假設外星技術也需要硬碟。那怎麼辦呢?外星人就測量了自己飛船精確的長度後,把飛船假設為1。又把那串長長的‘大英百科數字’按照小數點後的模式,參照飛船長度,在飛船外殼上某處刻了很小的一個點。於是外星人回去了,他只刻了一個點,卻帶走了大英百科全書。回去只要測量出飛船的長度,再找到那個點在飛船上的位置……”

同一場演出的座位ID,一般是同一個數量級,比如第一個座位ID是“2010092012”,最後一個座位ID很可能是“2010093012”,在資料流裡一大串“2010092012、2010092013、2010092014…2010093012”,我都感覺自己傻。那麼,為何不記錄下起始座位ID,後續所有座位ID都只記錄與它的偏差值呢?於是就變成了“2010092012、1、2、3…1000”的形式,是不是連文字形式都看起來乾淨利落的減少了許多?不止於此,繼續閱讀下一條。

3)無視既定的數值型別,按需配位

用到位運算的時候到了。上面聊到long型和int型,long型數值64bit支援從-9223372036854775808~9223372036854775807範圍的數字,int型數值32bit支援-2147483648~2147483647範圍的數值,都別說上面第2)條提到的相對值數字了,就是用絕對值數字你家賣票賣到下輩子座位序號也超不出這個範圍啊,更何況還用不著負數 T_T。一個區域1000個座位載入下來,偏差值最大超不過1024,只需要10bit的空間就足以儲存單個座位ID了,既有資料型別ubyte佔8bit最大值255不夠用,ushort佔16bit最大值65535太浪費,我們需要一個只佔10bit的數值……OK,把int、long、byte、位元組統統從腦子裡抹掉吧,眼前是一串“0101010101……”到無限長的資料流,老老實實用">>"、"<<"左右移動著玩吧。

4)座位狀態2bit

座位有多種狀態,比如“可售”、“已售”、“鎖定”等,直接跟在座位ID後面拿2bit搞定吧。。。00、01、10、11,還能再支援一個狀態~

5)一個座位4個座標值減少到1個

這個估計是最傻逼的設計了,因為選座的每個座位是需要在場館背景裡畫出來的,因此需要有每個座位的座標。問題是選擇了四個點來確定一個座位……T_T,尼瑪的座位都一樣長寬,記錄最左上角的一個座標不就完事了麼……

2、說能賣的座位不一定能賣,說不能賣的座位一定不能賣

熱門演出搶票往往搶得血流成河,經常小一萬張票放出去30秒就搶沒了,沒搶到的粉絲們網上罵聲一片。不過話說系統都是我寫的我這麼多年竟然沒能成功搶到一次票[痛哭]。流量高到平時120倍,先別提分表分庫拿資料庫叢集頂的方案的,大麥那會還沒有阿里爸爸,沒有動不動撥幾個億先花著的待遇。有次我協調7臺伺服器協調得鼻青臉腫,有一臺還是調撥了部署著郵件服務端的破機器。怎麼辦啊,總不能兩手一攤說搞不定了吧。。。窮家窮當,繼續想轍啊。

座位資料在搶票那會是高實性資料,別指望在快取裡完成所有工作,出張重票你就哭去吧。琢磨來琢磨去想到一招可以解決:把所有座位狀態預先同步到redis裡(記住,一個座位一個坑),接下來對過來鎖座的請求先訪問快取,快取說能賣,不好意思不能相信你,穿透到後端資料庫詢問狀態加鎖座;快取說不能賣,那鐵定不能賣,不好意思您再搶其它座位去吧。。。

此舉可以讓資料庫壓力瞬間下降好幾個數量級。

選座技術先聊到此,其它的部分都沒什麼難點,自行腦補。
本文第四部分附有我做的併發資料分析,感興趣的同學可以算算數,看看是多少頻寬、多少伺服器、支撐了多少併發。

02 熱門專案搶票的技術準備工作

當初我們在每次大專案前都會做非常精細的資料分析,對購票過程中系統的流程進行詳細切分,評估業務過程中每個環節的併發壓力 ,進行系統調優。以資料來評估和驅動系統準備工作,絕對不是粗放式地抱著上百臺的伺服器,算算伺服器數差不多就洗洗睡去了。

1、搶票活動CheckList

從2009年起經歷了很多大型專案的搶票,什麼樣的情況都經歷過(譬如伺服器鬆了個水晶頭、機房出口頻寬被流量懟死了伺服器集體全部失聯、正搶著票呢辦公室突然斷網等等許多人這輩子都碰不上的情況),總結了很多的經驗,形成CheckList。每當有新熱門專案時就拿出CheckList逐項檢檢視各項工作是否做到位。每場搶票活動結束後仔細總結,再往CheckList上補上幾條。

CheckList裡的每一條背後,都有一場血的教訓。舉幾個栗子來說吧:

1)主要系統負責人配備3G上網路卡:某次搶票活動進行中,辦公室寬頻掛了,大麥網直接失控裸奔……從此以後,搶票開始前都給主要的系統負責人配3G上網路卡;
2)CRM客服系統大查詢操作進行限制:某次搶票活動進行中,某分公司一客服MM手一抖,點選了一下訂單彙總,資料庫直接宕了……從此以後,開搶前先把客服系統裡牽涉到大查詢的操作全關閉了;
3)簡訊通道餘額確認:某次搶票活動進行中,使用者都沒收到簡訊,因為簡訊通道錢用完了……
4)大麥網上第三方圖示及JS移除:某次搶票,一開票網頁載入不完,一查頁尾上掛著的某權威機構的JS直接被懟死了,資源等待中直至超時,導致大麥的頁面載入不完(赤裸裸的躺槍啊尼瑪痛哭流涕了)
5)搶票前機房裝置巡檢:某次搶票,某臺伺服器的水晶頭鬆了,資料流量全部走了外網系統超級慢……
6)搶票時機房派人值守:某次搶票,機房突然失聯,從哪都連線不進伺服器,大麥網直接失控裸奔……

滿滿的掛滿了淚……

2、系統流程及負載評估表

我們會對系統流程詳細分解後,預測各系統切面需要達到的負載量,反覆優化與壓力測試:

3、應對專案時職責明確、分工清晰

準備每一場專案,都如行軍打仗。戰場上最怕的是亂,在出現突發事宜時,排程有序、各司其職非常重要,是快速響應和故障處理的基礎。我們會明確好各部分的分工與責任人。

4、專案後總結分析

沒有總結就沒有收穫,失敗不可怕,怕的是盲目的失敗。因此每次專案結束後,需要對系統負載引數進行仔細總結與分析。舉個分析的栗子(水平好的同學可以算算數):

03 有的放矢,摸清硬傷

談系統優化,不能泛泛而談,一張嘴不是一系列高大上的方案、就是現有的不堪到需要全部推翻重新全來一個理想中的完美架構。通常只有演化成功的系統,沒有一開始就設計成功做得完美的系統。

鑑於篇幅有限,暫且就先分享到這裡。

相關文章