基於Tablestore管理海量快遞軌跡數據架構實現

快遞軌跡管理

對於一個快遞公司,在全國範圍內有着大量的快遞點、快遞員、運輸車輛以及倉儲中心。而快遞自產生後,就會在這些地點、人物之間流轉。於是,一套完善的快遞管理追蹤系統是快遞公司的重要管理工具;git

用戶經過平臺客戶端下單後,產生惟一的快遞單號做爲惟一身份標識。快遞除了訂單號,還會有不少屬性信息,如:郵寄人、郵寄人手機、郵寄人地址、收件人、快遞類型等信息。生成快遞訂單後,用戶的郵寄物品纔會成爲「快遞」。快遞公司配合掃碼機器,將快遞的流轉事件、地點、時間等信息不按期推送至系統。快遞流轉信息不只能夠是簡單的量化數據,也能夠是描述性文字、地理位置等特殊信息。系統將流轉信息記錄成快遞的監控數據,同時修改快遞狀態、實時位置等。直至快遞送達收件人手中,結束快遞生命週期。github

經過系統,用戶能夠管理本身的歷史郵寄單列表、收件列表,掌握本身郵寄中的快遞軌跡。快遞公司也能夠查詢、修改快遞信息、追蹤快遞時效,並藉助海量軌跡監控數據,掌握快遞產生、收件的高頻路線,在高頻位置鋪設更多的基礎設施、轉移調度快遞員;數據庫

功能需求

面向用戶:

一、用戶在線下單生成快遞單,等候快遞員上門取件;
二、管理歷史訂單列表,瞭解快遞明細;
三、追蹤特定快遞週轉狀態、運送軌跡;數據結構

面向平臺:

一、藉助掃碼器,實現快遞週轉事件採集、存儲;
二、統計、查詢全部快遞訂單,實現全訂單的管理:CRUD;
三、掌握全部郵寄中快遞的實時位置;
四、掌握任意一個訂單的週轉狀態、運送軌跡;
五、基於歷史快遞數據,分析快遞時效;
六、方便掌握高頻地域、路線,爲增設基礎設施、快遞員提供依據;
等等...併發

系統樣例,以下所示:官網控制檯地址:項目樣例運維

實現方案async

MySQL方案與難點

一般,用戶會選用MySQL做爲方案數據庫,由於MySQL做爲數庫在查詢、分析等功能上有優點,用戶建立兩個表:訂單表、事件追蹤表實現對快遞數據的存儲。分佈式

可是快遞場景有幾個強需求:
第1、須要有強大的查詢、統計能力,實現快遞單的管理;
第2、對於海量快遞,有着高併發寫入需求,對寫入性能要求較高;
第3、數據持續膨脹,但歷史快遞訂單、事件數據多爲冷數據,存儲成本須要儘量低;
第4、數據將來挖掘潛在價值較高,須要有較好的計算生態;ide

而MySQL方案在面對第2、第三個強需求時,劣勢凸顯,海量併發、不斷的數據膨脹、存儲成本高一直以來都是關係型數據庫的痛點;高併發

表格存儲方案

選擇表格存儲有如下優點:

其1、表格存儲的多元索引(SearchIndex)功能輕鬆知足用戶的多維查詢、GEO檢索、統計等功能需求;
其2、基於LSM tree打造的分佈式NoSQL數據庫,輕鬆支持海量高併發讀、寫,零運維輕鬆應對數據量的不斷膨脹,理論上無上限。
其3、表格存儲按量計費,提供容量型、高性能型兩種實例,容量型對冷數據更適宜,提供了更低存儲成本。
其4、更重要的,表格存儲擁有較爲完善的計算生態,提供全、增量通道服務,提供流計算、批計算一體的計算體系,對將來監控數據價值挖掘提供渠道。

表格存儲在時序場景需求的技術點上擁有極高的匹配,而基於時序場景打造的Timestream模型更是將時序場景通用功能,封裝成易用的場景接口,使用戶更容易的基於表格存儲,根據自身需求設計、打造不一樣特色的軌跡追蹤系統;

數據結構設計

基於快遞的時序,將快遞的屬性信息做爲meta數據,而快遞的週轉路徑、狀態、位置等則爲data數據,下面對兩類數據作簡單介紹。

快遞元數據

meta數據管理着快遞的屬性信息,支持指標、標籤、屬性、地理位置、更新時間等參數,模型會爲全部屬性建立相應的索引,提供多維度條件組合查詢(包含GEO查詢)。其中Identifier是時間線的標識,包含兩部分:name部分(監控指標標識)、tags部分(固有不可變參數集合)。

在快遞場景中,用戶一般是基於快遞單號直接定位快遞,於是tags使用空的。而屬性信息則存儲快遞的郵寄人信息、收件人信息、郵寄起/止地址等,location字段,用於最新位置追蹤,可不按期根據產生新的狀態週轉數據時更新。

快遞軌跡數據

data數據記錄着快遞的狀態週轉信息,主要爲量化數據、地理位置、文字表述等任意類型。data數據按照+有序排列,於是同一快遞的全部數據物理上存在一塊兒,且基於時間有序。這種數據存儲方式,極大的提高了時間線的查詢效率。對應到快遞軌跡,監控數據主要記錄了:【who】do【something】@【where】with the location【geo】以及聯繫方式等。

讀寫接口使用介紹

寫數據

寫接口根據數據類型分爲兩類:meta寫入(新增快遞)、data寫入(快遞週轉數據)

  • 新增快遞:當用戶經過系統直接下快遞單後,產生惟一訂單號,加上用戶填寫的快遞單信息組成必要的凱迪數據。此時,就會生成一個時間線,產生一個meta數據;
  • 快遞週轉:當快遞發生取件、運輸週轉、派送、取件是,產生的狀態轉變數據時,就會產生一條追蹤數據,經過data數據的寫接口不按期的寫入;

讀數據

與寫數據同樣,針對兩類數據提供了兩類讀接口:meta讀取(快遞查詢)、data讀取(查詢快遞軌跡)

  • 查詢快遞:根據快遞號、寄件人手機、收件人手機等信息,獲取對應快遞的列表,掌握全部快遞的最新動態;
  • 查詢快遞軌跡:基於單個meta的Identifier,獲取該快遞從產生到結束整個生命週期內的軌跡週轉數據,能夠經過列表、地圖軌跡展現等方式,直觀的瞭解快遞的週轉過程;

方案核心代碼

SDK與樣例代碼

SDK使用:
時序模型Timestream模型集成於表格存儲的SDK中,目前已在4.11.0版本中支持;

<dependency>
    <groupId>com.aliyun.openservices</groupId>
    <artifactId>tablestore</artifactId>
    <version>4.11.0</version>
</dependency>

代碼開源:
https://github.com/aliyun/tablestore-examples/tree/master/demos/MailManagement

建表準備

在建立完成實例後,用戶須要經過時序模型的sdk建立相應的meta表(快遞元數據)、data表(快遞週轉數據):

private void init() {
    AsyncClient asyncClient = new AsyncClient(endpoint, accessKeyId, accessKeySecret, instance);
    //快遞抽象Timestream
    TimestreamDBConfiguration mailConf = new TimestreamDBConfiguration("metaTableName");
    mailDb = new TimestreamDBClient(asyncClient, mailConf);
}

public void createTable() {
    mailDb.createMetaTable(Arrays.asList(//自定義索引
        new AttributeIndexSchema("fromMobile", AttributeIndexSchema.Type.KEYWORD),
        new AttributeIndexSchema("fromName", AttributeIndexSchema.Type.KEYWORD),
        new AttributeIndexSchema("toMobile", AttributeIndexSchema.Type.KEYWORD),
        new AttributeIndexSchema("toName", AttributeIndexSchema.Type.KEYWORD),
        new AttributeIndexSchema("toLocation", AttributeIndexSchema.Type.GEO_POINT)
    ));
    mailDb.createDataTable("dataTableName");
}

寫數據

數據寫入主要分兩部分,meta表建立新快遞、data表採集快遞週轉信息

建立快遞單(meta表寫入)

//metaWriter對應meta表,提供讀、寫接口
TimestreamMetaTable mailMetaWriter = mailDb.metaTable();

//identifier做爲時間線的身份標識(unique),僅含:快遞單號ID,
TimestreamIdentifier identifier = new TimestreamIdentifier.Builder("mail-id-001")
    .build();

//基於identifier建立meta對象,併爲meta設置更多屬性,Attributes爲屬性參數
TimestreamMeta meta = new TimestreamMeta(identifier)
    .addAttribute("fromName", whos.get(Rand.nextInt(whos.size())))
    .addAttribute("fromMobile", "15812345678")
    .addAttribute("toName", whos.get(Rand.nextInt(whos.size())))
    .addAttribute("toMobile", "15812345678")
    .addAttribute("toLocation", "30,120");

//建立新的時間線,而後寫入監控數據
mailMetaWriter.put(meta);

採集快遞週轉事件(data表寫入)

//dataWriter分別對應data表,提供讀、寫接口
TimestreamDataTable mailDataWriter = mailDb.dataTable("mailDataTableName");
TimestreamMeta meta;//meta上一步已經構建

//建立新的時間線,而後寫入監控數據
mailDataWriter.write(
    meta.getIdentifier(),
    new Point.Builder(14500000000, TimeUnit.MILLISECONDS)
        .addField("who", "張三")
        .addField("do", "取件")
        .addField("where", "雲棲小鎮")
        .addField("location", "30,120")
        .build()
);

數據讀取

數據讀取分爲兩類:

快遞訂單多維查詢(meta表讀取)

//reader對應meta表,提供讀、寫接口,此處名字爲突出讀功能
TimestreamMetaTable metaReader = mailDb.metaTable();

//構建篩選條件
Filter filter = AndFilter(
    Name.equal("mail-id-001"),
    Attribute.equal("fromMobile", "15812345678")
);

Iterator<TimestreamMeta> metaIterator = mailDb.metaTable()
    .filter(filter)
    .fetchAll();

while (iterator.hasNext()) {
    TimestreamMeta meta = iterator.next();//deal with metas
}

快遞軌跡追蹤(data表讀取)

//dataWriter分別對應data表,提供讀、寫接口
TimestreamDataTable dataReader = db.dataTable("dataTableName");
TimestreamMeta meta;//基於已獲取的meta列表,分別獲取每一個快遞的軌跡追蹤

Iterator<Point> dataIterator = mailDb.dataTable(mailDataTableName)
    .get(meta.getIdentifier())
    .fetchAll();

while (iterator.hasNext()) {
    Point point = iterator.next();//deal with points
    long timestamp = point.getTimestamp(TimeUnit.MILLISECONDS);//毫秒單位時間戳
    String location = point.getField("location").asString();//獲取該點String類型的位置信息
}


原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索