12306票池架構探討(一)

來源:互聯網
上載者:User

 最近論壇裡已經慢慢有人在考慮票池的設計了,這是我關於票池架構的一些想法。具體的討論請去論壇上討論:http://12306ng.org/thread-1572-1-1.html


需求討論
到目前為止,我瞭解到票池需求有: 
1、車票的預售期不定,有30天的,也有10天的,但應該是10天的居多。
2、票在事先有計劃售票管理,制定票額計劃和編製臨時票額計劃。這樣一來票是動態分配的,在預售期的前幾天,會根據熱門乘車區間預先分配一些票,在預售期後面幾天,會將沒賣完的回收到票池裡。
3、需要考慮退票和改簽的問題。
4、需要考慮有一部分票可能是預留給一些特別的單位,這些預留票可能最後沒有賣出去,回收到票池中。
5、需要考慮中途上下車的情形,為了保證客運的產出,最好是同一個座位,儘可能地多賣票。比如說,上海到北京的火車,如果有乘客甲買了上海到南京,乙買了南京到北京的車票,從座位的使用效率來講,當然是甲和乙都坐一個位子就好了。

應該還有其他的需求,我建議一開始可以縮小需求範圍,避免需求膨脹,我覺得開始可以只考慮下面的需求:
1、只支援10天預售。
2、不支援計劃,也就是我們從上遊計劃系統擷取已經計劃好的票額,放進票池中。
3、支援退票和改簽。
4、支援預留票。
5、支援中途上下車的情形,一個座位重複賣票的問題。

架構設計思路
票池的架構應該考慮下面幾個問題:
1、票池應該可以方便分布式,從上面的需求來看,票池至少可以從兩個維度考慮分布,首先根據售票的地點,即車票的始發站分布。這樣一來,相應的票池伺服器離乘客是最近的,對於異地購票的乘客,可以直接重新導向到異地伺服器,或者在本機快取一些票都是可以的;再就是可以根據時間分布,即1天后開車的車票和9天后開車的車票是完全可以放在不同的伺服器上的。
2、為了保證售票的速度,應該盡量將整個票池放在記憶體中,一些關鍵的資料盡量放在CPU緩衝裡。這是因為,硬碟的隨機訪問速度是記憶體訪問速度的10000倍,硬碟的順序訪問速度要比隨機訪問速度快很多;而CPU二級緩衝的訪問速度又比記憶體快2到3倍,一級緩衝要比記憶體快10倍左右。
3、CPU將資料讀取到緩衝的過程一般是批量讀取的,而不是一個位元組一個位元組讀取;為了能夠儘可能地利用上CPU緩衝,因此要盡量將相關資料放在連續的記憶體裡。這樣一來,最好是盡量使用數組結構,而不是鏈表
4、使用鏈表等非連續結構還有幾個問題,第一在分配記憶體時,對於C/C++這樣的程式,在分配記憶體時尋找空閑記憶體比較耗時間,對於Java等基於記憶體回收語言,GC後更新鏈表的引用也是一個問題。第二就是記憶體片段問題,對於長期線上的伺服器,我覺得應該盡量避免使用鏈表結構。
5、儘可能的無鎖操作,即使整個票池都在記憶體裡,如果是需要鎖來同步多線程的話,會有幾個問題,第一是需要從使用者態切換到核心態,這個過程可能需要執行幾千個甚至更多的指令;第二是因為線程來回切換,原先CPU緩衝的代碼和資料都將無效,需要來回在緩衝和記憶體倒騰資料。
6、在對票池並發處理時,我覺得應該只有一個線程負責寫入資訊,其他的線程都只負責讀取。不使用多線程寫入的好處是,第一可以實現無鎖;第二可以避免偽共用問題。

現有方案對比
我在之前的文章裡提到了使用有向圖的設計方案,我現在依然堅持這個方案 - 不過改成用有向圖做索引,我先對比一下論壇上其他幾個方案(詳細情況參看:http://12306ng.org/forum.php?mod ... 01&fromuid=5805):
1、二進位的方案,雖然在我的設計裡會有類似二進位的方案,但是原始二進位方案的一個很大的問題是,好像沒有考慮資料庫自身的實現,例如在文章裡說是編寫類似的查詢:
where (station>0011111100) and (not (station&0011111100)^0011111100) limit 10

上面的條件子句,從資料庫實現的角度來說,需要考慮怎麼建立索引,B樹應該是不能建立這樣支援按位操作的索引的(如果可以的話請糾正我),不過不知道位元影像索引是否可以支援 – 但mysql好像不支援位元影像索引。如果沒有一個很有效索引解決方案的話,在資料庫中使用二進位方案恐怕會變成漸進式掃描 - 也就是有大量的磁碟訪問,效率就很低了 。

2、兩個整數表示始發站和結束站,這個方案會經常維護二叉樹結構,而且樹的節點個數和高度都不是確定的 – 這是因為一個座位如果拆分成多個短途訂單,這個座位會在二叉樹裡有多個節點。 

 

 


在裡面,可以看到,每個網站(就是圖裡面的節點)用一個列表儲存了經過它的所有的車次(邊),通過有向邊的方式指明車次的方向,一個車次其實是由多條邊組成的。

可以把網站(例如北京)和車次(例如G017)本身看成擷取資料的索引,例如在server-core/cpp/sites.h裡,將所有的網站定義成一個枚舉型;server-core/cpp/trains.h裡,將所有的車次定義成一個枚舉型(以數字開頭的,在前面加上底線就可以了)。由於網站和車次不是經常更換,因此可以固定起來,以後有更新的話,只需要提供網站和車次的設定檔,直接產生上面兩個代碼就可以了,如果買票訂單儲存的是起始和終點站的索引的話,在重建的時候就需要考慮保證相同網站名的索引值不變,但如果訂單直接儲存網站名稱,就沒必要保證索引值不變了。

索引如的二維表所示,其中上面兩個數組分別是車次G108和G107的餘票資訊,“-”表示這個位置車次經過該網站,它的值實際是一個指標,指向對應車次的餘票數組:




又因為需要考慮中間上車的情況,二進位的方案如果是放在資料庫裡,會有很大的效能的問題,那麼我在考慮是否可以將二進位的方案整個放在記憶體呢?我覺得是可能的,主要是出於下面幾個發現:
1. 首先在裡,車次的餘票資訊的確是一個大數組,這個數組可以是一個位元組,每一位代表這個座位的售票情況,只要這個座位有過售票 - 不管是從始發站坐到終點站的,還是中間上車的,那麼就將這個位設成1。而一個車次的車廂配置、車廂的座位、鋪位配置在一個固定的時間段,至少是一天內是固定的,可以認為是不經常改變的。
2. 還沒有賣出去票,是不需要儲存在記憶體裡,只要在上面的數組裡將對應位設為0就好了。
3. 所有從始發站坐到終點站的車票也不需要保留在記憶體裡,只要在上面的數組裡將對應位設為1就好了.
4. 在記憶體裡我們只要找到一個資料結構,用來儲存中間會上下車的座位資訊就可以了,這個資訊就可以用二進位的方案來表述,第一是佔用的記憶體量小,第二是對比和修改都很快。
5. 至於退票,我還在考慮是放回票池,還是用一個單獨的鏈表結構來儲存,我現在傾向於放回票池。
6. 那儲存每個車次的中間上下車的餘票資訊,我們可以借鑒Windows系統管理記憶體配置的資料結構,這個結構可以做成一個包含數組的數組,數組的下標代表這個位置的元素的空閑位元,如所示:


 

每個車次都有類似的二維數組,在裡,數組的第一個元素裡,包含的是該車次所有最大連續空閑網站數為1的座位,也就是說只有1站沒有人坐的位置;第二個元素,是該車次有連續2站沒有人坐的位置,雖然第二個元素我們看到實際是有三站空餘,但我們仍然放在第二個元素裡。

這個時候,如果有人買票,例如是坐一站的,那我們就首先去第一個數組裡找,找到第一個匹配,將位補齊,這個時候發現位置已滿,因此將其從的數組中移除,移除它剩下的空就放在那裡,如所示:

 


如果有人買兩站的票,跟上面一樣,找到第二個數組的第一個座位匹配,買了票之後,它的值變成:“11111101”,因為它只有一站是閒置,因此我們將其放到第一個數組中去,如所示:

 


這個二維數組,每一個車次的列數是固定的 – 因為每個車次經過的網站數目是固定的,而每列對應的數組,如果空間不夠了,可以動態分配(這裡是一個風險,我還沒有仔細計算過極端情形)。

為了節省記憶體,每個座位的車次是一個長整形,即8個位元組組成,這8個位元組裡,前14位用來表示座位在車次的索引(14位裡,可以有12位表示索引,可以表示4096個座位,應該可以滿足一趟車上的坐票、臥鋪和站票資訊了,另外兩位可以用來做一些標誌位,具體幹什麼我還沒有想好),後50位就是座位的網站佔用資訊。如所示:


對於運行區間超過50個網站的車次,作為特殊車次特殊處理 - 這樣的車次應該不是很多,可以先枚舉下。

還有對分布式的支援、負載平衡等方面的想法,還沒有寫完,這兩周慢慢寫,先把現在想到的發出來,拋磚引玉。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.