對12306系統的服務以及售票系統有了進一步的瞭解:數據庫
其實,12306系統也至關因而電商系統,並且看起來商品就是票了。由於若是把一張票當作是一個商品,那購票就相似於購買商品,而後每張票都有庫存,商品也有庫存的概念。可是若是咱們仔細想一想,會發現12306要複雜不少,由於咱們沒法預先肯定好全部的票。緩存
對於12306系統來講,他的用戶無異因而是二者,其一就是咱們這些進行出行購票普通用戶,另外一方就是要售票的鐵道部。微信
對於傳統的電商系統來講,若是按照普通電商的思路,把票(站點區間)設計爲商品(聚合根),而後爲票設計庫存數量。我我的以爲是很糟糕的。由於一方面這種聚合根很是多,另外一方面,即使枚舉出來了,一次購票也必定會影響很是多其餘聚合根的庫存數量(只要被部分或所有重疊的區間都受影響)。這樣的一次訂單處理的複雜度是難以評估的。並且這麼多聚合根的更新要在一個事務裏,這不是爲難數據庫嗎?並且,這種設計必然帶來大量的事務的併發衝突,極可能致使數據庫死鎖。總之,我認爲這種是典型的因爲領域模型的設計錯誤,致使併發衝突高、數據持久化落地困難。架構
對12306系統來講,車次具備一次出票的全部信息,因此咱們應該把出票的職責交給車次。咱們應該知道,聚合設計有一個原則,就是:聚合內強一致性,聚合之間最終一致性。對於系統售票,要產生一張票,其實要影響不少和這個票對應的直線相交的其餘票的可用數量。由於全部的站點信息都在車次聚合內部,因此車次聚合內部天然能夠維護全部的原子區間,以及每一個原子區間的可用票數(至關因而庫存數)。當一個原子區間的可用票數爲0的時候,意味着火車針對這個區間的票已經賣完了。因此,咱們徹底可讓車次這個聚合根來保證出票時對全部原子區間的可用票數的更新的強一致性。對於車次聚合根來講,這很簡單,由於只是幾回簡單的內存操做而已,耗時能夠忽略。一列火車假若有ABCD四個站點,那原子區間就是3個。併發
根據訂單信息,拿到出發地和目的地,而後獲取這段區間裏的全部的原子區間。而後嘗試將每一個原子區間的可用票數減1,若是全部的原子區間都夠減,則購票成功;不然購票失敗,提示用戶該票已經賣完了。是否是很簡單呢?知道了出票的邏輯,那退票的邏輯也就很簡單了,就是把這個票的全部原子區間的可用票數加1就OK了。若是咱們從線段的厚度的角度去考慮,那出票時,每一個原子區間的厚度就是+1,退票時就是減一。就是相反的操做,但本質是同樣的。分佈式
在用戶進行購票付款的狀況下,用戶有可能不去付款或者沒有在規定的時間內完成付款。那這種狀況下,系統會自動釋放該用戶以前訂購的票。因此基於這樣的需求,咱們在業務上須要支持業務級別的2pc。即先預扣庫存,也就是先佔住這張票必定時間(好比15分鐘),而後付款成功後再真實給你這張票,系統作真正的庫存修改。性能
在系統進行購票查詢的時候,對於12306來講,查詢的請求佔了80%,提交訂單的請求只佔20%。但查詢因爲對數據沒有修改,因此咱們徹底可使用分佈式緩存來實現。咱們只須要精心設計好緩存的key便可;緩存key的多少要當作本,若是全部可能的查詢都設計對應的key,那時間複雜度爲1,查詢性能天然高;但代價也大,由於key多了。若是想key少一點,那查詢的複雜度天然要上去一點。因此緩存設計無非就是空間換時間的思路。而後,緩存的更新無非就是:自動失效、定時更新、主動通知3種。經過CQRS架構,因爲CQ兩端是事件驅動的,當C端有任何狀態變化,都會產生對應的事件去通知Q端,因此咱們幾乎能夠作到Q端的準實時更新。設計
文章部分總結摘自原文,向原做者致敬!若有侵權或不周之處,敬請勞煩聯繫(微信:15227013954)立刻刪除,謝謝!事件