算法:12306的餘票算法。

我看過網上稱某寶工程師寫的12306文章,覺得狗屁不通。他們將每個站點獨立成一件商品,每次購、退票都需要查詢刪改庫存,造成巨大的數據庫操作開銷。其實這是個簡單到不能再簡單的算法。我以8個站點的班次舉例,票面現值pm每位代表一個站點,客戶乘坐需求gp也一樣每位代表他需要乘坐的站點:

票面現值(初始值)pm=(00000000),客戶購票需求=gp(01111111),(01110000),(00001111),(01101100),(00110110)。。。等等等

(這裏需要注意的是gp賦值的原則,始發站(或任意上游站)該位都爲0,這樣就能銜接上該位爲1的下車乘客。例如,乘坐一站就是01,兩站011。。。前邊的0代表你在第幾個站始發,1代表你要乘坐經達的下游站,例如第二站上車001,第三站0001。。。乘類推)

算法原則:用二進制同位加法,原則爲同位加不能產生溢出(不能產生進位,否則爲錯誤)。

第一步:pm&gp=0 (and運算,根據位運算法則結果爲0則不溢出)。

第二步:餘票現值pm=pm|gp (由於有pm&gp=0屏蔽,這步直接or即可寫入pm值,這步即爲二進制同位加法)

第二步的結果:票面現值pm=01111111,01110000,00001111,01101100,00110110

pm值(後七位)帶有0的票恆爲餘票

再有新客戶購票時,pm(後七位)帶有0的繼續做算法。

例如有一個客戶他的購買需求gp=01111111,與餘票值pm運算,他將無法購買那些pm(後七位)值任意一位帶有1的餘票,因爲算法不準溢出(pm&gp必須=0才能購買)。

這樣一列火車2000個座位或者3000個座位,或者更多都沒有問題。所有票面值(後七位)或初始值帶有0的組成pm()數組,數組長度10萬以下都是簡單運算而已,一下就能匹配完成。

退票:退票時pm直接減去該客戶需求即可立即得出餘票現值。(即:pm=pm^gp,二進制xor運算)

查詢優化:所有餘票全部做同位and運算則立即得出查詢僞碼wm,查詢僞碼任意一位爲1則表示全部餘票在該站點餘票面值爲1。這樣一條僞碼可以完成先導查詢wm&gp=0,結果不爲0的查詢將無法進入pm()內進一步操作。大大降低了系統負擔(例如pm()長度爲2000,理想情況下查詢壓力下降2000倍)。

以8個站點的班列爲例。以票爲單位存儲,數據庫的記錄長度,將比以站點爲單位的數據庫,下降8倍,讀寫操作也將分別下降n倍。

某寶的工程師算法邏輯是直接操作庫的邏輯,那麼將數據庫映射成爲bmp處理,但這又帶來新的問題,比如鎖定機制,數據同步機制,寫入仲裁機制,這些在本算法中由天然的cpu硬件機制來實施。與硬件有一致性,機制成熟算法健壯性有保證。如果人爲的另立機制想拓展bmp算法的性能會導致很多問題,

比如你買一個站點,只想改寫bmp中的1bit,但是硬件機制是寫8bit一次。那麼後七位一樣要鎖定(不然會發生後七位有退票的線程改寫了那些商品位,而本線程又因硬件機制復原了內容的現象發生)。要使寫衝突保護與硬件高度一致,那就只能化爲變量來處理。那本算法已經是最優解。因爲本算法也將數據庫映射成爲bmp,但算法邏輯並不是直接操作庫的邏輯而是數組變量,確定性發生完後再更新入庫。

將每站點全部商品位化,是爲了營造高併發的假象而已,白白浪費了計算資源。以每趟車2000張票爲例子。由於每張票的獨立性,pm()在一開始能最大維持2000個線程寫入的鎖定,但是會迅速下降,因爲隨着購票確定性的發生,pm()是越來越短的。現代處理器16核能在本端維持32線程,看起來似乎併發少,但是本端處理能將各類延遲最小化,各種機制效率最大化,況且處理一個2000長度且越來越短的位運算數組,算力已經遠遠超過需求,來1000個數組都沒問題,按需分佈服務器即可。比起集羣帶來的2000線程假象更有效益。集羣應該用在那些“胸大無腦”的響應上,而不是來處理核心任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章