1. 程式人生 > >阿里面試題——天貓部

阿里面試題——天貓部

設計12306售票系統的資料庫

訂票系統分解,得到位池、餘票池、訂票池、取票池四個票務處理系統,以及獨立的前臺伺服系統、支付系統和通知系統,其中的精華就是位池、餘票池系統。售票窗和電話取票內部原理與網上訂票一樣,只是多了一個人工客服或電話客服。將訂票系統分解,目的是使訂票事件非同步處理,獨立系統處理專業事件,實現簡單,效率更高。子系統對應的資料結構也更符合實際需求,資料處理更快。每個子系統的處理任務不同,對伺服器的要求也不同,從而實現伺服器的靈活配置。分解系統的依據是使票務資訊資料庫結構更簡單、查詢鏈更短、“靜態化”。如此以來,我們查詢票務資訊時面對的彷彿就是一張簡單的表格,根據我們提供的列車車次或者出發站/目的站直接就可以定位到相應列車的餘票資訊。而且這個餘票資訊還是實時更新的——後臺驅動在默默地為您服務。

位池系統

 位池系統是餘票查詢與訂票的基礎。位池資料庫記錄所有列車每一個票位在沿途各站點的使用情況
{ /** 位池文件結構,訂票系統的根基,發生訂票時,向餘票池請求減去或增加相應票位,向待支付票池請求生成訂單。
一個文件對應列車中的一個位,如座位或者臥鋪位,甚至站位。
文件的集合形成一個特定車次的列車。
一般一趟車次有不到2000車位,即集合裡面文件樹不到2000個,如開放站票可能會多點。
一天內所有列車集合形成當日所有列車位池資料庫,當日列車位池形成當日位池資料庫。
全國有不到5000次列車,即表示每日的資料庫中約有不到5000個集合,每日一個數據庫*/
    "_id" : 列車位唯一ID,    //由‘日期+列車ID+列車位ID’編碼而成。
"seat_type" : 位型別, //如上鋪、中鋪、下鋪、座位、站票、一等座、二等座等,該欄位建索引。 "station" : { //途經站點,標記該位車票在哪一個站點被誰訂購,下面以G1011武漢-深圳北(高鐵)站點資料示例。 "武漢" : null, //null表示該位在該站點無人訂購。 "咸寧北" : null, "岳陽東" : 410xxxxx10, //表示身份證(或其它證件號)為410xxxxx10的顧客訂購了岳陽東到株洲西的票。 "長沙南" : 410xxxxx10, "株洲西" : 430
xxxxx30, //表示身份證(或其它證件號)為430xxxxx30的顧客訂購了郴州西到深圳北的票。 "郴州西" : 430xxxxx30, "廣州南" : 430xxxxx30, "虎門" : 430xxxxx30, "深圳北" : null } }

位池驅動程式專門伺服位池資料庫,功能如下:

根據票務字典,每日出票階段進行資料初始化;
接受來自訂票池的請求。對於下單事件所有列車票請求(一個訂單有一張或多張列車票),在位池資料庫中查詢特定列車車次的某個符合位型別的列車位文件,檢查相應站段是否為null。如果所有要求的列車票均存在,則在相應請求站段寫入顧客證件號。寫入成功後向餘票池發出請求,要求餘票池減去相應票位ID,同時向訂票池反饋下單成功。
接受來自訂票池的退訂請求和來自取票池的退票請求,根據請求,把位池中相應的車位重置為null,請求餘票池增加相應的票位ID,同時向訂票池或取票池反饋請求成功。

訂票池

訂票池系統以最簡約的方式受理訂票事件。支援同時訂多張票和備選訂票方案

{ /** 訂票池文件結構,處理過期未支付的無效訂單,為支付系統提供資料。
一個訂單文件對應一個下單人的下單事件。*/
    "_id" : 訂單唯一ID,    //由‘下單人編號+下單時間+訂單編號’編碼而成。
    "gen_time" : null,    //下單時為null,下單成功後填入訂單生成時間值,也標記了有效支付期的開始。
    "pay_time" : null,    //訂單支付時間,驅動程式掃描該欄位,null表示未支付,存在時間值表示已支付或者已被鎖定請求釋放中。
    "pay_info" : {訂單支付詳細資訊},    //支付資訊物件,初始值為null,支付成功後即寫入資料。
    "order" : [{    //主訂單列車票物件陣列,陣列中每個物件代表一張預定的列車票資訊
        "seat_type" : 位型別,    //如上鋪、中鋪、下鋪、座位、站票、一等座、二等座等,該欄位建索引。
        "seat_num" : null,    //即位池中的列車位唯一ID,下單時為null,下單成功後填入位ID。
        "price" : 票價,    //根據訂票站段資訊從字典表查詢而來。
        "customer" : {    //訂票人資訊
            "name" : 姓名,
            "cer" : 證件,
            "cer_num" : 證件號,
            "phone" : 手機號,
            "email" : 電子郵箱
        },
        "s_station" : 出發站名稱,    //出發站點,如上面430xxxxx30顧客,該值為“株洲西”。
        "d_station" : 到達站名稱,    //到達站點,如上面430xxxxx30顧客,該值為“深圳北”。
        },
        {第二張列車票},    //訂單中第二張列車票物件。
        {...}],     //更多。
    "re_order" : [{    //備用訂單列車票物件陣列,結構同上,主訂單失敗時啟用備用訂單。
        "seat_type" : 位型別,    //如上鋪、中鋪、下鋪、座位、站票、一等座、二等座等,該欄位建索引。
        "seat_num" : null,    //即位池中的列車位唯一ID,下單時為null,下單成功後填入位ID。
        "price" : 票價,    //根據訂票站段資訊從字典表查詢而來。
        "customer" : {    //訂票人資訊
            "name" : 姓名,
            "cer" : 證件,
            "cer_num" : 證件號,
            "phone" : 手機號,
            "email" : 電子郵箱
        },
        "s_station" : 出發站名稱,    //出發站點,如上面430xxxxx30顧客,該值為“株洲西”。
        "d_station" : 到達站名稱,    //到達站點,如上面430xxxxx30顧客,該值為“深圳北”。
        },
        {第二張列車票},    //訂單中第二張列車票物件。
        {...}]     //更多。
}

訂票池驅動程式專門伺服訂票池資料庫,功能如下:

接受顧客訂單請求,生成訂單文件。然後向位池系統發出訂單請求,位池系統反饋訂單成功後向訂單文件每個列車票物件寫入成功獲得的列車位ID和訂單正時生成時間。然後向顧客反饋下單成功,同時向支付系統發出支付請求。如果下單失敗,啟用備用訂單,再次向位池系統發出訂單請求。如果備用訂單仍失敗,則刪除該訂票文件,同時向顧客反饋下單失敗。
若支付系統反饋支付成功,則向取票池發出請求生成取票文件,同時在訂票池中刪除該已支付的訂票文件。
對於超過有效支付期的訂票文件,向位池系統發出退單請求,位池系統反饋退單成功後即刪除該訂票文件。

取票池

取票池系統生成取票文件,受理查詢、退票、出票、記錄存檔等事件。

{ /** 取票池文件結構,處理退票、出票事件。與訂票池文件結構不同,一個取票池文件對應一張列車票。
由於取票操作是以證件號為依據,需對[customer][cer_num]欄位建索引。
根據出發站點將取票文件分成不同集合不同資料庫,提高查詢效率。
按照前面假設的資料最大化估算,取票池總文件數量可能達到億級。*/
    "_id" : 列車票唯一ID,    //由‘列車位唯一ID+訂票人證件型別+訂票人證件號’編碼而成,記錄至訂票系統個人賬戶中。
    "type" : 位型別,    //如上鋪、中鋪、下鋪、座位、站票、一等座、二等座等。
    "pay_time" : 支付時間,    //訂單支付時間,也是車票確認時間。
    "pay_interface" : 訂單支付介面,    //訂單支付介面。
    "pay_info" : {訂單支付詳細資訊},    //支付資訊物件,為退票提供退款目標。
    "ticket_time" : 出票時間,    //null表示未取票。
    "ticket_place" : 出票地點,    //null表示未取票。
    "price" : 票價,    //根據訂票資訊從字典表查詢而來。
    "seat" : 位置編號,    //如“XX車廂XX號上鋪”,根據“_id”中列車位唯一ID,查詢票務字典得到。
    "customer" : {    //訂票人資訊
        "name" : 姓名,
        "cer" : 證件型別,
        "cer_num" : 證件號,    //該欄位建索引。
        "phone" : 手機號,
        "email" : 電子郵箱
    },
    "s_station" : 出發站名稱,
    "d_station" : 到達站名稱,
    "s_time" : 出發時間,    //根據訂票資訊查詢票務字典得到,人性化。
    "d_time" : 到達時間,    //根據訂票資訊查詢票務字典得到,人性化。
}

取票池驅動程式專門伺服取票池資料庫,功能如下:

根據訂票池的請求生成相應的取票文件,若一個訂票文件包含多張列車票,則會生成多個取票文件,同時將取票文件記錄到使用者賬號,方便使用者查詢或退票;
若使用者發出某張列車票退票請求,向位池發出退票請求;
獲得退票請求成功反饋後向支付系統請求退還相應款項;
獲得退款成功反饋後將該退票文件永久存檔,同時在取票池中刪除相應的取票文件。
響應現場取票機的查詢及取票請求,取票成功後向該文件寫入取票時間、地點,然後將該取票文件永久存檔,同時在取票池中刪除相應的取票文件。
對於超過取票期限的取票文件,執行退票請求

餘票池

餘票池系統是訂票系統的關鍵,對使用者而言,餘票池系統實現了偽靜態化特徵。它還能體現某一列車位在不同站點空置情況。

{ /** 餘票池文件結構,處理餘票查詢事件。
位池發生訂票行為時,向餘票池發出資訊,餘票池根據訂票日期、列車ID、列車位ID,出發站和到達站資訊,在餘票池中刪除相應的餘票資訊。
一個文件對應一趟列車的餘票資訊,所有列車當日餘票資訊組成一個集合,訂票期內(如12天)每天所有列車集合共形成一個餘票池資料庫。
針對春節的返程票訂購,可另外生成一個返程票餘票池資料庫。*/
    "_id" : 列車唯一ID,    //由‘日期+列車ID’編碼而成。以下欄位屬性名為該列車所經站點,對應值是可能目的站點的組合物件。以G1011為例。
    "武漢" : {    //出發車站
        "咸寧北" : {    //表示武漢到咸寧北有以下型別車票及相應列車位ID陣列。
            "位型別1" : [列車位唯一ID,,],    //表示位型別1,如一等座,剩餘列車位ID陣列。陣列長度即剩餘數量。
            "位型別2" : [列車位唯一ID,,],    //如,二等座。。。
            ...    //其它位型別,如中鋪、上鋪等,省略表示。
        },
        "岳陽東" : {,,},    //武漢到岳陽東段的餘票,省略表示,下同。
        "長沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "廣州南" : {,,},
        "虎門" : {,,},
        "深圳北" : {,,}
    },
    "咸寧北" : {    //出發車站
        "岳陽東" : {,,},
        "長沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "廣州南" : {,,},
        "虎門" : {,,},
        "深圳北" : {,,}
    },
    "岳陽東" : {
        "長沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "廣州南" : {,,},
        "虎門" : {,,},
        "深圳北" : {,,}
    },
    "長沙南" : ...
    "株洲西" : ...
    "郴州西" : ...
    "廣州南" : ...
    "虎門" : {
        "深圳北" : {
        "位型別1" : [列車位唯一ID,,],
        "位型別2" : [列車位唯一ID,,],
        ...
        }
    },
    "深圳北" : null
}

餘票池驅動程式專門伺服餘票池資料庫,功能如下:

根據票務字典對餘票池初始化,未出票時,餘票池包含所有的列車位ID。
響應位池的請求,增加或減去指定列車位ID。以G1011為例,假設430xxxxx30顧客訂購郴州西到深圳北的一等座。位池處理後得到了該車票的列車位唯一ID,假設是“20121001-G1011-0117”號(2012年10月1日G1011次1號車廂17座),向餘票池發出減票請求。餘票池收到請求,找到該趟G1011次列車的餘票文件。從始發站欄位“武漢”開始,在其中郴州西到深圳北的“一等座”屬性對應的陣列中,刪除“20121001-G1011-0117”,然後依次在“咸寧北”、“岳陽東”、“長沙南”…“虎門”做類似操作。其中,深圳北是到達站,訂票時位池系統並未佔位寫入顧客身份證,這裡也無需做相應刪除操作。諸位也許覺得,操作有點多~,但這是必須的,為了最短的查詢路徑。查詢事件總是遠大於訂票事件的。
處理查詢請求。例如,410xxxxx10顧客查詢10月1日從岳陽東到株洲西的票。餘票池驅動先根據票務字典查找出10月1日所有從岳陽東和株洲西的列車ID,再根據列車ID查詢對應的餘票文件,在餘票文件中找到主鍵“岳陽東”,再在其物件中找到目的地“株洲西”,最後返回[岳陽東][株洲西]對應的物件值:位型別和列車位ID陣列長度的鍵值對。今後若提供選座功能,則該查詢列車位ID陣列。這應該是最短查詢路徑。

票務字典(靜態資料庫)

列車趟次——途徑站名——到達/出發時間——票價字典

{    /*列車趟次——途徑站名——到達/出發時間——票價字典,靜態值。
    與下面的車站名——列車趟次字典等組成票務字典資料庫。為位池、票池、餘票池等提供基礎資訊。*/
    "_id" : 列車ID,    //由‘車次’編碼而成。以下欄位屬性名為該列車所經站點,對應值是可能目的站點的組合物件。以G1011為例。
    "武漢" : [
        到達時間,    //始發站為null
        發車時間,    //發車時間
        {
        "咸寧北" : {    //表示武漢到咸寧北有以下型別車票及票價。
            "位型別1" : 票價,    //表示位型別1,如一等座,及對應票價。
            "位型別2" : 票價,
            ...    //其它位型別,如中鋪、上鋪等,省略表示。
            },
        "岳陽東" : {,,},
        "長沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "廣州南" : {,,},
        "虎門" : {,,},
        "深圳北" : {,,}
        }
    ],
    "咸寧北" : [
        到達時間,    //到達咸寧北的時間
        發車時間,    //發車時間
        {
        "岳陽東" : {,,},
        "長沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "廣州南" : {,,},
        "虎門" : {,,},
        "深圳北" : {,,}
        }
    ],
    "岳陽東" : [
        到達時間,    //到達岳陽東的時間
        發車時間,    //發車時間
        {
        "長沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "廣州南" : {,,},
        "虎門" : {,,},
        "深圳北" : {,,}
    ],
    "長沙南" : [,,],
    "株洲西" : [,,],
    "郴州西" : [,,],
    "廣州南" : [,,],
    "虎門" : [,,],
    "深圳北" : null
}
列車趟次——位型別——列車
列車趟次——位型別——列車位ID字典

{    /*列車趟次——位型別——列車位ID字典,靜態值。*/
    "_id" : 列車ID,
    "位型別1" : [列車位ID陣列],
    "位型別2" : [列車位ID陣列],
    ...
}
###車站名——列車趟次字典

{    /*車站名——列車趟次字典,一般車票查詢基於車站到車站.
該字典迅速提供車站到車站的可用列車車次,然後根據列車車次查詢相關餘票。*/
"武漢" : [靠站列車1唯一ID, 靠站列車2唯一ID,,,,],   //表示在武漢站停車的所有列車車次。
"株洲" : [,,,],
...    //全國所有列車站的資料字典。
}