阿里云-表格存儲服務最佳實踐-D_第1頁
阿里云-表格存儲服務最佳實踐-D_第2頁
阿里云-表格存儲服務最佳實踐-D_第3頁
阿里云-表格存儲服務最佳實踐-D_第4頁
阿里云-表格存儲服務最佳實踐-D_第5頁
已閱讀5頁,還剩5頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、表格存儲最佳實踐表格存儲/最佳實踐表格存儲/最佳實踐 PAGE 9 PAGE 9最佳實踐Table Store 表的最佳實踐這一節將會提供一些關于使用Table Store表的建議。設計良好的主鍵Table Store會根據表的分片鍵將表的數據切分成多個分片,表上配置的預留讀寫吞吐量會被均勻的分攤到多個分片上。舉例來說,假設表被分成10個分片,表上的預留讀吞吐量為100,那么每個分片上分得10個預留讀吞 吐量。因此,為了充分利用表上配置的預留讀寫吞吐量,應用程序需要讓數據的分布和訪問量的分布盡可能的 均勻。Table Store會對表中的行按主鍵進行排序,合理地設計主鍵可以讓數據在分片上的分布

2、更加均勻,從而能夠充分的利用表上配置的預留讀寫吞吐量,降低成本。分片鍵的選取建議遵循以下幾個原則:單個分片鍵中的數據不宜過大(不建議超過1GB) 同一張表不同分片鍵中的數據在邏輯上獨立訪問壓力不要集中在小范圍連續的分片鍵中假設我們現在有這樣一張表,里面存儲的是某大學內所有學生使用學生卡消費的記錄,主鍵列有學生卡ID(CardID),商家ID(SellerID),消費終端ID(DeviceID),訂單號(OrderNumber)。同時我們有如下約定:每一張學生卡對應一個CardID,每一個商家對應一個SellerID。每一個消費終端對應DeviceID,DeviceID在全局是唯一的。在每一個消

3、費終端上產生的每一筆消費記錄一個OrderNumber。一個消費終端產生的OrderNumber不唯一。例如不同的消費終端有可能產生 兩條完全不同的消費記錄,但是它們的OrderNumber相同。同一個消費終端產生的OrderNumber按時間排序,新的消費記錄比老的消費記錄擁有更大的OrderNumber。每筆消費記錄均會被實時寫入這張表中。那我們該如何設計Table Store表的主鍵,才能更高效的利用Table Store呢? 考慮表的分片鍵:使用CardID作為表的分片鍵使用CardID作為表的分片鍵是一個較好的選擇。每天每張卡產生的消費記錄數從總體上來講是均勻的,每一個分片鍵中的訪問

4、壓力也應該是均勻的。以CardID作為表的分片鍵可以較好地利用預留讀寫吞 吐量資源使用SellerID作為表的分片鍵使用SellerID作為表的分片鍵不是一個較好的選擇。因為學校內的商鋪數量相對較少,同時一些商鋪 可能產生大量的消費記錄成為熱點,不利于訪問壓力的均勻分配使用DeviceID作為表的分片鍵使用DeviceID作為表的分片鍵是一個較好的選擇。盡管每家商鋪的消費記錄數可能相差較大,但是每 天每臺消費終端上產生的消費記錄數是可預期的。消費終端每天產生消費記錄的條數取決于收銀員操 作的速度。這就決定了一臺消費終端產生的消費記錄數是受限的。因此,使用DeviceID作為表的分片 鍵也可以保

5、證訪問壓力的相對均勻。使用OrderNumber作為表的分片鍵使用OrderNumber作為表的分片鍵不是一個好的選擇。因為OrderNumber是順序增長的,因此在同 一段時間內產生的消費訂單的OrderNumber的值會集中在一個較小的范圍內,這些消費訂單記錄會 集中寫入到個別的分片,預留讀寫吞吐量沒有得到高效的利用。如果必須使用OrderNumber作為分 片鍵,建議在OrderNumber上進行哈希散列,將哈希值作為OrderNumber的前綴,保證數據和訪問 壓力的均勻綜上,我們可以根據需求使用CardID和和DeviceID作為表的分片鍵,而不應該用SellerID和OrderNu

6、mber。 之后再根據應用的實際需求來設計剩余的主鍵列。通過拼接的方式使用分片鍵Table Store建議單個分片下的數據量大小不超過1GB。如果您的表中單個分片鍵的所有行的總數據量大小可能超過1GB,在設計表時可以將原來的多個主鍵列拼接成分片鍵。如上一小節中提到的學生卡消費記錄表的例子,假設主鍵為DeviceID, SellerID, CardID, OrderNumber。DeviceID是該表的分片鍵,單個DeviceID中所有行的數據量總大小可能超過1GB,可以將DeviceID,SellerID,CardID拼接作為表的第一個主鍵列(也就是分片鍵)。原來的表如下:DeviceIDSe

7、llerIDCardIDOrderNumberattrs16a10066661200001.54a1006777200003.54a10016777200004.167a101283408200002.將DeviceID,SellerID,CardID拼接成分片鍵后的表如下:CombineDeviceIDSellerIDCar dIDOrderNumberattrs16:a100:66661200001.167:a101:283408200002.54:a1001:6777200004.54:a100:6777200003.在原來的表中,Device=54的兩行是屬于同一個分片鍵為54下的兩條

8、消費記錄。在新的表中,這兩條消費記錄 擁有不同的分片鍵。通過拼接多列主鍵列形成分片鍵的表減少了單個分片鍵下的總數據量大小。選擇將DeviceID,SellerID,CardID拼接成分片鍵,不選擇DeviceID,SellerID進行拼接的原因是,前一節提 到的消費記錄表約定所有DeviceID相同的消費記錄其SellerID也相同,僅僅拼接DeviceID和SellerID并不能解 決單個分片鍵的數據量過大的問題。但是拼接主鍵列形成表有一些小瑕疵,DeviceID是一個Integer類型主鍵列。在原來的表中,DeviceID=54的 消費記錄在DeviceID=167的前面。將前三列主鍵列拼

9、接成String類型的主鍵列后,DeviceID=54的消費記錄 在DeviceID=167的后面。假如應用程序需要范圍讀取DeviceID在15, 100)之間所有的消費記錄,上面的表無法滿足需求。為了應對這種狀況,可以在DeviceID高位補0。補0的個數取決于DeviceID最大位數。假設DeviceID的取值范 圍是0,999999,可以將DeviceID高位補0至6位后再進行拼接,得到的表如下:CombineDeviceiDSellerIDCar dIDOrderNumberattrs000016:a100:66661200001.000054:a1001:6777200004.00

10、0054:a100:6777200003.000167:a101:283408200002.經過高位補0后的表依然有一些問題,在原來的表中,DeviceID=54的兩行,SellerID=a1001的行應該在SellerID=a100的后面。產生這種現象的原因是,000054:a1001的字典序小于000054:a100:,但是ASCII碼 都小的字符作為連接符。在該表中,SellerID的取值為數字、大小寫英文字母。我們可以使用,作為連接符,因為,比所有SellerID可用字符的ASCII碼小。使用,拼接后的表如下:CombineDeviceiDSellerIDCar dIDOrderNum

11、berattrs000016,a100,66661200001.000054,a100,6777200003.000054,a1001,6777200004.000167,a101,283408200002.上面的經過拼接形成的分片鍵的表的記錄順序就和原來的表保持一致了。綜上,當表中單個分片鍵的所有行的數據量總大小可能超過1GB時,可以使用將多個主鍵列拼接成分片鍵的方 法避免單分片鍵的數據量大小限制。在拼接分片鍵時需要注意以下事項:選取需要拼接的多個主鍵列必須能有效地將原來表中相同的分片鍵的記錄形成擁有不同分片鍵的記錄。拼接Integer類型主鍵列時可以在高位補0,保持記錄的順序一致。選取連接

12、符時需要考慮連接符對新的分片鍵的字典序的影響,選取比所有可用字符都小的連接符是一 個比較安全的選擇。在分片鍵中加入哈希前綴在設計良好的主鍵一節中已經提到,盡量不要使用OrderNumber作為表的分片鍵。因為OrderNumber是順序 增長的,消費記錄總是被寫入最新的OrderNumber范圍之內,舊的OrderNumber不再有寫入壓力,產生訪問 壓力的不均勻的現象,預留讀寫吞吐量得不到高效利用。如果必須使用順序增長的鍵值作為分片鍵,我們可以 對分片鍵拼接哈希前綴,讓相連的OrderNumber在表中隨機分布,使訪問壓力分布均勻。以OrderNumber為分片鍵的消費記錄表如下:Order

13、NumberDeviceIDSellerIDCardIDattrs20000116a10066661.200002167a101283408.20000354a1006777.20000454a10016777.20000566b304178994.對OrderNumber使用md5算法計算前綴(您也可以采取其他哈希散列算法),拼接成HashOrderNumber。因為md5算法計算得到的哈希字符串可能過長,我們只需要取前幾位就能達到讓OrderNumber相連的記錄在表中 隨機分布的目的。這個例子中我們取前4位:HashOrderNum berDeviceIDSellerIDCardIDat

14、trs2e3820000454a10016777.a5a920000354a1006777.c33520000566b304178994.db6e200002167a101283408.ddba20000116a10066661.在后續訪問消費記錄時,使用相同的算法對OrderNumber計算哈希前綴,即可得到對應消費記錄的HashOrderNumber。在分片鍵中加入哈希前綴的弊端是,原來連續的記錄會被打散,無法再使用GetRange操作讀取一段范圍內在邏輯上連續的記錄。并行寫入數據Table Store表會被切分成多個分片,這些分片被分散在多個Table Store服務器上。如果有一批數據

15、要上傳到Table Store中,同時這批數據是按主鍵排好順序的,按順序寫入數據,可能會導致寫入壓力集中在某個分片中,而其他的分片處于空閑狀態,無法有效利用預留讀寫吞吐量,影響數據導入速度。可以采取以下任一措施來提升導入數據的速率:將原始數據順序打亂后再進行導入。保證寫入數據均勻的分配在各個分片鍵上。使用多個工作線程并行導入數據。把大的數據集合切分成很多個小集合。工作線程隨機選取小集合進 行數據導入。區分冷數據和熱數據數據往往是具有時效性的。例如在設計良好的主鍵一節中提到的存儲消費記錄的表。近期產生的消費記錄被訪 問的可能性較大,因為應用程序需要及時地對消費記錄進行處理和統計,或者查詢最近的消

16、費記錄。但是年代 久遠的消費記錄被查詢的可能性不大,這些數據漸漸成為冷數據,但仍然占用存儲空間。其次,表中存在大量冷數據會導致數據訪問壓力不均勻,從而導致表上配置的預留讀寫吞吐量無法被充分利用。例如,已經畢業的學生的卡片,不會再產生消費記錄。假如CardID是隨著卡片申請時間遞增的,以CardID作 為分片鍵,會導致已經畢業的學生的CardID沒有訪問壓力卻被分配到預留讀寫吞吐量,造成浪費。為了解決這種問題,可以用不同的表來區分冷熱數據,并設置不同的預留讀寫吞吐量。例如,將消費記錄按月 份分表,每一個新的自然月就換一張新的表。當月的消費記錄表需要不停寫入新的消費記錄,同時有查詢操作。當月的消費

17、記錄表可以設置一個較大的預留讀寫吞吐量配置來滿足訪問需求。前幾個月的表由于不再寫入新數據或者寫入的新數據量較少,查詢的請求較多,因此前幾個月的消費記錄表可以設置較小的預留寫吞吐量,較大的預留讀吞吐量。而歷史超過一年的消費記錄表,由于再被使用的可能性不大,可以設置較小的預留讀 寫吞吐量配置。已經超出維護年限的消費記錄表可以將數據導出,存入OSS(Open Storage Service)歸檔,或直接刪除。Table Store數據操作的最佳實踐這一節將會提供一些關于Table Store數據操作的建議拆分屬性列訪問熱度差異大的表如果行的屬性列較多,但是每次操作只訪問一部分屬性列,可以考慮將表拆分

18、成多個表,不同訪問頻率的屬性 列放到不同的表中。如在商品管理系統中,每行存放商品數量、商品價格和商品簡介。商品數量和商品價格均 為占用空間較小的Integer類型,商品簡介是String類型占用空間較大。大多數操作僅更新商品數量與商品價格,而不修改商品簡介。商品簡介的修改頻率較低。這時候可以考慮將這個表拆分為兩個,一個表存儲商品數量 和商品價格,另一個表存儲商品簡介。Table Store使用行數據大小計算服務能力單元。如果該行較大,但寫入操作僅僅更新一行中小部分數據,訪問操作仍然根據該行的總大小計算服務能力單元。拆分表后能減少被訪問行的數據大小,減少消耗的服務能力單 元。從而降低使用Tabl

19、eStore的成本。壓縮較大的屬性列文本如果屬性列是較大的文本,應用程序可以考慮將屬性列壓縮之后再以Binary類型存儲到Table Store中。這樣做節省了空間,減少了訪問的服務能力單元消耗,從而降低使用Table Store的成本。將數據量超出限制的屬性列存儲到OSS中Table Store限制單個屬性列值不超過64KB。如果需要存儲單個值超過64KB的需求,如圖片、音樂、文件等,可以使用OSS(Open Storage Service)對其進行存儲。OSS是阿里云提供的開放存儲服務,用以應對海量數據的存儲和訪問。OSS的存儲單價比Table Store更低,更適合存儲文件。如果應用程序

20、不方便使用OSS,可以將超過64KB的單個值拆分成多個行存儲在Table Store中。錯誤重試加入時間間隔Table Store可能遇到軟硬件問題,導致應用程序的部分請求失敗并返回可重試的錯誤(詳情見Table Store錯誤條件更新條件更新功能是指只有在滿足條件時才對表中的數據進行更改,當不滿足條件時更新失敗,支持算術運算(=、!=、=、 100) CompositeColumnValueCondition composite1 = newCompositeColumnValueCondition(CompositeColumnValueCondition.LogicOperator.AN

21、D); SingleColumnValueCondition single1 = new SingleColumnValueCondition(Col0, SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(0); SingleColumnValueCondition single2 = new SingleColumnValueCondition(Col1, / composite1 條件為 (Col0 = 0) AND (Col1 100) CompositeColumnValueCondition

22、composite1 = newCompositeColumnValueCondition(CompositeColumnValueCondition.LogicOperator.AND); SingleColumnValueCondition single1 = new SingleColumnValueCondition(Col0, SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(0); SingleColumnValueCondition single2 = new SingleColumnVa

23、lueCondition(Col1, SingleColumnValueCondition.CompareOperator.GREATER_THAN, ColumnValue.fromLong(100); composite1.addCondition(single1);composite1.addCondition(single2);/ composite2 條 件 為 ( (Col0 = 0) AND (Col1 100) ) OR (Col2 = 10) CompositeColumnValueCondition composite2 = new CompositeColumnValue

24、Condition(CompositeColumnValueCondition.LogicOperator.OR); SingleColumnValueCondition single3 = new SingleColumnValueCondition(Col2, SingleColumnValueCondition.CompareOperator.LESS_EQUAL, ColumnValue.fromLong(10);composite2.addCondition(composite1); composite2.addCondition(composite1); composite2.ad

25、dCondition(single3);通過Condition實現樂觀鎖機制, 遞增一列.private static void updateRowWithCondition(SyncClient client, String pkValue) / 構造主鍵PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(pkVal

26、ue); PrimaryKey primaryKey = primaryKeyBuilder.build();/ 讀一行SingleRowQueryCriteriacriteria=newSingleRowQueryCriteria(TABLE_NAME,primaryKey); criteria.setMaxVersions(1);GetRowResponsegetRowResponse=client.getRow(newGetRowRequest(criteria); Row row =getRowResponse.getRow();long col0Value = row.getLate

27、stColumn(Col0).getValue().asLong();/ 條件更新Col0這一列, 使列值+1RowUpdateChangerowUpdateChange=newRowUpdateChange(TABLE_NAME,primaryKey); Condition condition = new Condition(RowExistenceExpectation.EXPECT_EXIST); ColumnCondition columnCondition = new SingleColumnValueCondition(Col0, SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(col0Value); condition.setColumnCondition(columnCondition); rowUpdateChange.setCondition(condition);rowUpdateChange.put(new Column(Col0, ColumnValue.fromLong(col0Value + 1);try client.updateRow(new UpdateRowRequest(rowUpdateChange); c

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論