全部產品
Search
文件中心

Lindorm:如何設計寬表主鍵

更新時間:Jul 06, 2024

Lindorm寬表引擎是一款分布式資料引擎,寬表引擎中的資料均按照主鍵進行分布。在執行查詢時,如果表中存在多列主鍵,系統會從最左邊的主鍵開始匹配。如果主鍵設定不當,則可能導致主鍵無法被有效利用,進而產生熱點問題,影響查詢效能。因此,在資料分區和資料查詢中,主鍵的設計至關重要。本文介紹設計主鍵前需要考慮的一些問題以及設計樣本。

問題考慮

主鍵是唯一的嗎?

相同的主鍵在Lindorm中被認為是同一條資料的多個版本,查詢時預設返回最新版本的資料,所以通常主鍵都需要保證唯一,除非用到多版本特性。

最佳設計樣本:主鍵可以是一列,也可以是多列的組合。每個主鍵表示一條記錄。

  • [userid]:表示主鍵只有一列,每個使用者只有一條記錄。

  • [userid][orderid]:表示主鍵為兩列的組合,每個使用者有多條記錄。

基於主鍵可以滿足哪種查詢情境?

主鍵的設計限制了資料的查詢方式,一條SELECT查詢語句,Lindorm伺服器端會編譯為兩種查詢方式。

  • 根據完整的主鍵查詢(get方式),例如SELECT * FROM table WHERE userid='abc' AND orderid=123

    說明

    get方式需要知道所有的主鍵列,即組成主鍵所有欄位的值都是確定的。

  • 根據主鍵的範圍查詢(scan方式),例如SELECT * FROM table WHERE userid='abc' AND 123<orderid<456

    說明

    scan方式需要指定第一列主鍵的範圍,否則Lindorm預設會拒絕低效的全表掃描查詢,具體說明,請參見低效查詢語句及說明

最佳設計樣本:在有限的查詢方式下如何?複雜查詢?以下方法可以幫您實現。

  • 再建立一張表作為索引表。

  • 查詢條件給定非主鍵列範圍,服務端會使用Filter過濾不需要的資料。

  • 使用二級索引。

  • 使用ORDER BY方法實現倒序(將新資料排在前面),例如SELECT * FROM table WHERE userid='abc' AND 123<orderid<456 ORDER BY orderid DESC

    說明

    由於表欄位原始順序的倒序效能比正序效能差,如果大部分資料是倒序情境,可以體現在主鍵設計上,主鍵設計為[userid][orderid DESC]

設計主鍵應該考慮哪些因素?

需要考慮主鍵列值的長度和主鍵列的個數。

  • 主鍵列值的長度:主鍵列值的長度建議盡量短小,建議您採用固定長度的類型,例如長整型。對於非固定長度的類型,主鍵列值的長度控制在2 KB之內,有利於減少儲存成本,提升寫效能。

  • 主鍵列的個數:主鍵列越少,寫入效能越高,同時可以降低儲存成本。建議將主鍵列的數量控制在1~3個。

設計主鍵應該避免哪些情況?

Lindorm是一個分散式資料庫,資料按照主鍵分布。如果存在多列主鍵,則按照資料庫的最左匹配原則分布。為避免產生寫入熱點問題,建議您遵循以下條件:

  • 主鍵的第一列盡量分散,不建議主鍵名使用相同的首碼。

  • 避免使用共同首碼或者自增的資料作為主鍵的第一列或者索引列(例如時間戳記列)。

  • 避免使用有明顯首碼的欄位或者枚舉(比如order_type)作為主鍵的第一列。

如果有類似的情況無法避免,可以利用Hash演算法進行打散。例如:

假設原始主鍵pk是遞增的字串,可以設定新主鍵pk1 = hash(pk).substring(0,4)+pk,即選取原始主鍵pk經過Hash演算法計算後的結果前4位作為首碼,拼接原始主鍵pk,最終形成新的主鍵pk1。

資料足夠分散,會存在堆積的熱點現象嗎?

散列的目的是將資料分散到不同的分區,不至於產生熱點使某一台伺服器終止,其他伺服器空閑,充分發揮分布式和並發的優勢。

最佳設計樣本:

  • 設計md5散列演算法,主鍵設計為[md5(userid).subStr(0,4)][userId][orderid]

  • 設計反轉,主鍵設計為[reverse(userid)][orderid]

  • 設計模數,主鍵設計為[bucket][timestamp][hostname][log-event]; long bucket = timestamp % numBuckets

  • 增加隨機數,主鍵設計為[userId][orderid][random(100)]

主鍵可以再精簡點嗎?

精簡的主鍵列可以減少資料量,提高資料查詢和資料寫入效率。

最佳設計樣本:

  • 使用Long或Int代替String,例如'2015122410' => Long(2015122410)

  • 使用編碼代替名稱,例如'淘寶'=> 'tb'

常見設計樣本

  • 日誌類、時間序列資料。列舉出三個情境設計主鍵。

    • 查詢某台機器某個指標某段時間內的資料,主鍵設計為[hostname][log-event][timestamp]

    • 查詢某台機器某個指標最新的幾條資料,主鍵設計為[hostname][log-event][timestamp DESC]

    • 查詢的資料存在只有時間一個維度或某一個維度資料量巨大的情況,主鍵設計為long bucket = timestamp % numBuckets; [bucket][timestamp][hostname][log-event]

  • 交易類資料。列舉出四個情境設計主鍵。

    • 查詢某個賣家某段時間內的交易記錄,主鍵設計為[seller_id][timestamp][order_number]

    • 查詢某個買家某段時間內的交易記錄,主鍵設計為[buyer_id][timestamp][order_number]

    • 根據訂單號查詢,主鍵設計為[order_number]

    • 查詢中同時滿足三張表,一張買家維度資料表主鍵設計為[buyer_id][timestamp][order_number]。一張賣家維度資料表主鍵設計為[seller_id][timestamp][order_number]。一張訂單索引表主鍵設計為[order_number]