分庫分表ShardingJDBC最佳實踐

2023-03-20 15:00:50

1 新增依賴

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>${sharding.version}</version>
</dependency>

2 分庫分表數選擇

根據未來兩年的業務量,估算兩年的業務總量M,單表資料量不能超過N(需要看具體業務場景,欄位少的可以適量多一些,可與架構師及部門經驗豐富的同事探討,最大不要超過1000W);

總的分表數量KM/N,且K值向上取接近的最小2的次冪。

例如業務總量M=10億,單表數量N≤700W,則M/N≈143,向上取最小的2次冪為:143<2的8次方=256,故總的分表數量為256。

可將分表數設定的儘可能的小,一臺伺服器存放多個庫,業務增長後,磁碟不足時,可將該伺服器上的整個庫的資料遷移到新的伺服器。

如總的分表數量為1024,可分為32個庫:db0~db31,每個庫分32張表:tb0~tb31,共16臺伺服器:server0~server15。

具體劃分如下:

  • server0上有db0庫和db16庫
  • server1上有db1庫和db17庫
  • ……
  • server15上有db15庫和db31庫

如果業務發展一段時間後出現磁碟不足,可在申請16臺伺服器:server16~server31

  • server0上的db16庫遷移至server16
  • server1上的db17庫遷移至server17
  • ……
  • server15上的db31庫遷移至server31

遷移後磁碟釋放50%空間

3 分片鍵(拆分鍵)選擇

分片鍵的選擇一定要具有明顯的業務特徵,具體表現為:

  • 常用的已知的具體值,查詢條件中要包含分片鍵,需要根據分片鍵確定在哪個庫表,如果查詢條件裡沒有分片鍵,將會遍歷所有庫表,這種情況是不允許的;
  • 分片鍵分辨度較高,比較分散,不能選擇分辨度不高的的欄位作為分片鍵,容易造成資料分佈不均勻;
  • 分片鍵不能有偏移,比如分片鍵某個值不能遠遠大於其他值,這種情況也會造成資料分佈不均勻;
  • 大小寫,如果分片鍵在某些表裡不區分大小寫,那麼分片鍵要統一大寫或小寫後用於分片,因此其他條件要考慮統一大小寫後的情況;

目前專案裡使用的分片鍵為商家編碼,並統一為大寫,因為:

  • 在查詢分庫分表資料時,商家編碼是已知的,所以查詢條件中包含商家編碼;
  • 商家編碼較多,能夠保證按照商家編碼分片後資料基本是均勻分佈的;
  • 系統中不存在某個或某幾個商家編碼的資料量佔據大部分的資料量;

4 分片演演算法

inline: Groovy的Inline表示式,可以支援SQL語句中的=和IN的操作,InlineShardingStrategy只支援單分片鍵;這種方式是最常用的,也是目前專案中在使用的。

其他分片策略參考:Sharding-JDBC分片策略

4.1 分庫演演算法

以32個庫為例:Math.abs(庫分片鍵.toUpperCase().hashCode()) % 32,將分片鍵商家編碼統一大寫後,取hashCode的絕對值,對32取模,32為分庫數;

通用公式為:Math.abs(庫分片鍵.toUpperCase().hashCode()) % 分庫數

4.2 分表演演算法

以每個庫32張表為例:(Math.abs(表分片鍵.toUpperCase().hashCode()) % 1024).intdiv(32),要保證分到某個庫的資料,均勻分佈到該庫的每個表;

注意:此處不能直接用分庫的公式,因為某個商家計算後分配到第i庫,如果分表用同樣的公式,則也只能落到第i表,導致一個庫中只有一個表有資料。因此,需要先對一個較大的數取模,該較大的數為分庫數*分表數,記為m,本例中=32*32=1024,將hash後的值分散到0-1023之間,在除以32,則最終結果被分配到0-31之間。

通用公式為:(Math.abs(表分片鍵.toUpperCase().hashCode()) % 分庫數*分表數).intdiv(分庫數)

5 全域性(分散式)主鍵

分庫分表,要求主鍵全域性唯一,因為存在資料彙總的場景,如存放到ES、巨量資料等;因此不能使用資料庫自增方式,需要一種全域性唯一的主鍵生成方式;

5.1 shardingjdbc提供的主鍵

1. 新增maven依賴

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>sharding-jdbc-self-id-generator</artifactId>
    <version>1.4.2</version>
</dependency>

2. 在@Configuration類中建立Bean物件

@Bean
public IdGenerator getIdGenerator() { 
    return new CommonSelfIdGenerator(); 
}

3. 使用

@Autowired 
private IdGenerator idGenerator; 
@Test 
public void generateId(){ 
    long id = idGenerator.generateId().longValue(); 
    ......
}

5.2 雪花演演算法生成主鍵

很多外掛如sharding-jdbc, mybatis-plus提供了雪花演演算法生成器,目前常用的是這種方式,生產的id時間上基本是有序的,使用很方便。

6 查詢條件沒有分片鍵如何處理

不允許沒有分片鍵的查庫操作;

查詢或更新庫操作條件裡必須有分片鍵,否則就需要將分庫分表資料存放到ES或建立查詢條件與分片鍵的中間表;

ES或分片鍵中間表是為個別場景如頁面多條件查詢,使用者獲取不到分片鍵時採取的折中方案,絕大多數場景是能獲取到分片鍵的,如果獲取不到則可認為分片鍵的選取是不合理的,需要根據實際場景調整分片鍵。