Timestream開發最佳實踐

背景

Timestream模型是針對時序場景設計的特有模型,可讓用戶快速完成業務代碼的開發,實現相關業務需求。可是,若是業務系統不只想實現基礎的相關業務功能,還要達到最佳的性能,而且兼顧到將來的擴展性的話,就不是一件特別容易的事情。html

本文會以共享汽車管理平臺爲例,介紹一系列的timestream最佳設計和使用,給業務設計和使用提供一些參考。數組

場景和模型簡介

在共享汽車管理平臺這個場景中,主要是對車輛的狀態軌跡監控、車倆元數據以及訂單元數據進行管理。另外,還會對相關的數據進行計算分析並存儲相關結果:性能優化

  • 車輛狀態軌跡:記錄了車輛的狀態監控,好比車速、位置、續航等數據,另外還須要記錄車輛行駛過程當中的違章記錄,好比:是否超速、是否闖紅燈等等;
  • 車輛元數據:記錄車輛的基本屬性信息,便於用戶進行車輛檢索,好比:車型、車牌、顏色等;
  • 訂單元數據:訂單相關信息記錄,包含行程的起止時間、車輛、用戶、費用等信息

業務主要是對上面三部分數據進行查詢和檢索,知足業務場景的需求。其中車輛元數據以及狀態軌跡數據是典型的時序序列,能夠很方便的映射到Timestream模型中。

下圖是數據模型的映射:

下面介紹一下模型設計的細節以及設計中須要注意的一些優化點,這些優化點對於業務功能以及性能上都有必定的提高。app

業務模型設計

在Timestream模型中,主要包含了元數據和數據點兩部分數據,分別使用一個元數據表以及若干個數據表進行存儲。下面介紹這兩類數據在存儲設計的關鍵點。異步

元數據表設計

在共享汽車這個場景下,元數據表主要存儲兩類數據:車輛的基本信息、車輛的最近狀態數據(位置、續航、狀態、違章統計等),業務會根據各種信息進行多條件的組合查詢符合條件的車輛。

如上圖所示,Timestream的元數據表會經過多元索引來提供豐富的數據檢索能力。在Timestream模型的元數據中,包含了name、tags、attributes三類數據,其中name、tags默認會提供數據檢索能力,attributes則須要在建立Meta表的時候指定須要索引的attributes字段以及相關信息,默認attributes並不支持檢索。
須要注意的是,目前並不支持動態修改Meta表的索引字段,因此最好能在設計之初可以考慮到當前以及將來的功能需求,下面介紹一下相關信息是如何映射到模型以及相關的設計。async

name設計

name字段的選取是很關鍵的,是數據檢索性能的一個重要影響因素,不一樣的name字段設計可能會致使查詢延時相差一個數量級。name字段的選取建議知足如下條件:ide

  • 絕大多數查詢場景都會對該字段進行精確查詢
  • 該字段單個取值下的最大記錄數不宜過多,好比說不超過一千萬條記錄

在共享汽車管理平臺這個場景下,管理的是各個平臺的車輛,而在車輛檢索的時候,必定會指定的條件是平臺的名字,而且某個平臺的車輛其實也不會太多,通常也就百萬量級,因此這裏能夠將平臺做爲name。性能

tags設計

在Timestream模型中,Name和Tags能夠惟一肯定某個元數據,在這個場景中,惟一肯定某輛車的信息是:平臺、車輛ID,其中平臺是name,因此,tags中只須要存儲ID便可。
tags設計須要注意:fetch

  • tags的總長度儘量的短,只把惟一肯定主體的信息放到tag中,其他信息均放到attributes中
  • tag只支持string類型的數據,若是業務字段是數值類型,須要將其轉成string進行存儲

attributes設計

attributes是主體的可變屬性,也能夠用來存儲主體的非惟一屬性。在這個場景中,車輛的基本信息以及當前狀態都是用attributes來進行存儲。attributes設計關鍵點:優化

  • 建立meta表的時候須要指定有檢索需求的attributes以及相關屬性,默認attributes是不支持索引的
  • 數值型數據儘量使用int來存儲,attribute支持多類型的數據,但在數據檢索過程當中,int類型的數據檢索效率比string類型高的多,建議使用int,索引類型爲LONG
  • 考慮將來系統的擴展性,能夠預留一列做爲擴展字段,索引類型爲KEYWORD,而且是Array

索引建立示例代碼:

public void createMetaTable() {
    db.createMetaTable(Arrays.asList(
        new AttributeIndexSchema("地區", AttributeIndexSchema.Type.KEYWORD),
        ...
        // 數值類型索引
        new AttributeIndexSchema("座位", AttributeIndexSchema.Type.LONG), 
        ...
        // 擴展字段,數組類型索引
        new AttributeIndexSchema("配置1", AttributeIndexSchema.Type.KEYWORD).isArray()  
    ));

數據表設計

Timestream能夠支持多個數據表的存儲,來知足不一樣的業務場景需求。另外,爲了可以利用底層引擎所作的性能優化,咱們推薦append的寫入方式,即不會對已有數據進行修改,因此在如下場景中,咱們建議業務將數據分到不一樣數據表中進行存儲:

  • 數據精度不一樣,特別是在監控場景下,這個需求更加突出。按數據精度分表便於後續數據的查詢,若是查詢長週期的數據能夠去查詢低精度的表,減小查詢的數據量,提升查詢效率
  • 須要對數據進行加工處理,也就是會對數據進行更新,建議將處理以後的數據寫到另一張表中

在共享汽車這個場景中,須要對車輛的狀態軌跡數據進行流式處理,好比檢測是否超速等違章、車輛狀態軌跡是否異常等,而後將處理以後的數據寫到另一張表中,提供給業務進行查詢。

sdk使用

前面介紹了業務模型設計須要注意的地方,對業務功能拓展能力以及性能都有必定的提高。下面介紹一下timestream sdk使用的一些性能優化點。

數據寫入

元數據

元數據寫入支持兩種方式:put和update。其中put會刪除老的記錄,而且插入一個全新行;update則是對原有記錄的部分attributes進行更新。建議儘可能使用Put的方式進行寫入。
示例代碼:

public void writeMeta() {
    TimestreamIdentifier identifier = new TimestreamIdentifier.Builder("*滴")
        .addTag("ID", carNo)
        .build();
    TimestreamMeta meta = new TimestreamMeta(identifier)
        .addAttribute("地區", "杭州")
        .addAttribute("座位", 4)
        ...
        .addAttribute("狀態", "閒置");
    // 插入車輛信息
    metaWriter.put(meta);
}

數據點

數據點寫入也提供了兩種方式:同步和異步。其中異步接口底層是經過TableStoreWriter進行異步寫入,其寫入吞吐能力更高,對寫入延時不是特別敏感的業務建議使用異步方式。
示例代碼:

public void writeData() {
    TimestreamIdentifier identifier = new TimestreamIdentifier.Builder("*滴")
        .addTag("ID", carNo)
        .build();
    Point point = new Point.Builder(1546272000, TimeUnit.SECONDS)
        .addField("位置", "30.1457580736,120.0563192368")
        .addField("車速", 30)
        .addField("續航", 100)
        .build();
    // 異步寫入
    dataWriter.asyncWrite(identifier, point);
}

數據查詢

元數據查詢

元數據查詢的時候,建議指定須要返回的列名。若是沒有顯示指定列名的話,會去讀主表以獲取完整的信息,這樣每一行元數據都會反查一次主表,查詢性能會更差一些。
示例代碼:

Filter filter = Name.equal("*滴");

// 用selectAttributes指定須要返回的attributes
Iterator<TimestreamMeta> iter = metaTable.filter(filter)
    .selectAttributes("座位", "狀態")
    .fetchAll();

總結

本文介紹了Tablestore Timestream在模型設計以及sdk使用中的一些優化點,對於業務現有功能實現、將來拓展以及性能提高都有很好的做用

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

相關文章
相關標籤/搜索