對於一個快遞公司,在全國範圍內有着大量的快遞點、快遞員、運輸車輛以及倉儲中心。而快遞自產生後,就會在這些地點、人物之間流轉。於是,一套完善的快遞管理追蹤系統是快遞公司的重要管理工具;git
用戶經過平臺客戶端下單後,產生惟一的快遞單號做爲惟一身份標識。快遞除了訂單號,還會有不少屬性信息,如:郵寄人、郵寄人手機、郵寄人地址、收件人、快遞類型等信息。生成快遞訂單後,用戶的郵寄物品纔會成爲「快遞」。快遞公司配合掃碼機器,將快遞的流轉事件、地點、時間等信息不按期推送至系統。快遞流轉信息不只能夠是簡單的量化數據,也能夠是描述性文字、地理位置等特殊信息。系統將流轉信息記錄成快遞的監控數據,同時修改快遞狀態、實時位置等。直至快遞送達收件人手中,結束快遞生命週期。github
經過系統,用戶能夠管理本身的歷史郵寄單列表、收件列表,掌握本身郵寄中的快遞軌跡。快遞公司也能夠查詢、修改快遞信息、追蹤快遞時效,並藉助海量軌跡監控數據,掌握快遞產生、收件的高頻路線,在高頻位置鋪設更多的基礎設施、轉移調度快遞員;數據庫
一、用戶在線下單生成快遞單,等候快遞員上門取件;
二、管理歷史訂單列表,瞭解快遞明細;
三、追蹤特定快遞週轉狀態、運送軌跡;數據結構
一、藉助掃碼器,實現快遞週轉事件採集、存儲;
二、統計、查詢全部快遞訂單,實現全訂單的管理:CRUD;
三、掌握全部郵寄中快遞的實時位置;
四、掌握任意一個訂單的週轉狀態、運送軌跡;
五、基於歷史快遞數據,分析快遞時效;
六、方便掌握高頻地域、路線,爲增設基礎設施、快遞員提供依據;
等等...併發
系統樣例,以下所示:官網控制檯地址:項目樣例運維
實現方案async
一般,用戶會選用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讀取(查詢快遞軌跡)
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表採集快遞週轉信息
//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() );
數據讀取
數據讀取分爲兩類:
//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類型的位置信息 }
原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。