Sharding-JDBC自定義復合分片算法


  一、背景

  最近在看 Sharding-JDBC方面的內容,此處簡單記錄一下使用Sharding-JDBC中的復合分片鍵來實現分表的方法。

  二、需求

  假設我們有一張訂單表customer_order,為了防止單表數據量太大,需要進行分表操作。

  此處需要分為3個表 customer_order_0、customer_order_1和customer_order_2

  1、對於客戶端操作而言

  1、同一個客戶的訂單,需要放到同一個表中。

  2、根據訂單號,需要知道這個訂單在哪個中。

  2、對於運營端操作而言

  由於訂單的數據量比較大,我們可以將一些需要作為搜索條件的數據保存到elasticsearch中,將訂單的完整數據保存到hive中。Mysql數據庫中的數據可以通過阿里開源的canal來同步到es中,這步操作略。

  三、分片算法

  由於同一個客戶的訂單分到同一個表,那么客戶id(customerId)需要作為一個分片鍵。

  由於需要根據訂單id(orderId)確定到那一個表,所有客戶id的分片信息需要糅合到訂單id中,所以訂單id也需要作為一個分片鍵。

  因此在Sharding-JDBC中而言,這是一個復合分片算法。

  1、客戶id和訂單id的生成規則

  客戶id: 使用雪花算法生成

  訂單id: 使用雪花算法生成 + 客戶id的后2位

  2、 確定數據落在那個表中

  截取客戶id后2位。

  將后2位和3做取模操作,獲取到表的后綴。

  和3做取模操作,是因為需求中需要分為3個表。

  將 customer_order_ 和上一步表的后綴拼接起來,就得到了一個真實表。

  3、舉例說明

  1、客戶id確定數據表

  客戶id截取后2位和3做取模操作確定表

  13970735281504296969696 % 3 = 0customer_order_0

  13970737985572085767676 % 3 = 1customer_order_1

  1397074377929003008088 % 3 = 2customer_order_2

  2、訂單id確定數據表

  訂單id截取后2位(等價於客戶id的后2位)和3做取模操作確定表

  1397073535658233856969696 % 3 = 0customer_order_0

  1397073798557208578767676 % 3 = 1customer_order_1

  139707437792900301008088 % 3 = 2customer_order_2

  四、實現步驟

  1、建表語句

  create table customer_order_0

  (

  id int auto_increment,

  order_id decimal(21) null,

  customer_id bigint null,

  saller_id bigint null,

  product_name varchar(300) null,

  constraint customer_order_pk

  primary key (id)

  )

  comment '優惠券訂單' engine = innodb character set = utf8;

  create table customer_order_1

  (

  id int auto_increment,

  order_id decimal(21) null,

  customer_id bigint null,

  saller_id bigint null,

  product_name varchar(300) null,

  constraint customer_order_pk

  primary key (id)

  )

  comment '優惠券訂單' engine = innodb character set = utf8;

  comment '優惠券訂單' engine = innodb character set = utf8;

  create table customer_order_2

  (

  id int auto_increment,

  order_id decimal(21) null,

  customer_id bigint null,

  saller_id bigint null,

  product_name varchar(300) null,

  constraint customer_order_pk

  primary key (id)

  )

  comment '優惠券訂單' engine = innodb character set = utf8;

  2、引入Sharding-JDBC的jar包

  org.apache.shardingsphere

  sharding-jdbc-spring-boot-starter

  4.1.1

  cn.hutool

  hutool-all

  5.6.5

  3、編寫分片算法

  package com.huan.study.sharding.algorithm;

  import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;

  import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;

  import java.math.BigDecimal;

  import java.util.ArrayList;

  import java.util.Collection;

  import java.util.List;

  import java.util.Objects;

  import java.util.stream.Collectors;

  /**

  * 復合分片算法

  * 根據訂單id(orderId)和客戶id(customerId)后2位計算

  * 訂單id 包含客戶id 的后2位

  * 以客戶id的后2位來確定是路由到那個表中

  * 1、目前處理 = 和 in 操作,其余的操作,比如 >、< 等不支持。

  *

  * @author huan.fu 2021/5/25 - 上午9:48

  */

  public class OrderComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm {

  /**

  * 訂單id列名

  */

  private static final String COLUMN_ORDER_ID = "order_id";

  /**

  * 客戶id列名

  */

  private static final String COLUMN_CUSTOMER_ID = "customer_id";

  @Override

  public Collection doSharding(Collection availableTargetNames, ComplexKeysShardingValue shardingValue) {

  if (!shardingValue.getColumnNameAndRangeValuesMap().isEmpty()) {

  throw new RuntimeException("不支持除了=和in的操作");

  }

  // 獲取訂單id

  Collection orderIds = shardingValue.getColumnNameAndShardingValuesMap().getOrDefault(COLUMN_ORDER_ID, new ArrayList<>(1));

  // 獲取客戶id

  Collection customerIds = shardingValue.getColumnNameAndShardingValuesMap().getOrDefault(COLUMN_CUSTOMER_ID, new ArrayList<>(1));

  // 整合訂單id和客戶id

  List ids = new ArrayList<>(16);

  ids.addAll(ids2String(orderIds));

  ids.addAll(ids2String(customerIds));

  return ids.stream()

  // 截取 訂單號或客戶id的后2位

  .map(id -> id.substring(id.length() - 2))

  // 去重

  .distinct()

  // 轉換成int

  .map(Integer::new)

  // 對可用的表名求余數,獲取到真實的表的后綴

  .map(idSuffix -> idSuffix % availableTargetNames.size())

  // 轉換成string

  .map(String::valueOf)

  // 獲取到真實的表

  .map(tableSuffix -> availableTargetNames.stream().filter(targetName -> targetName.endsWith(tableSuffix)).findFirst().orElse(null))

  .filter(Objects::nonNull)

  .collect(Collectors.toList());

  }

  /**

  * 轉換成String

  */

  private List ids2String(Collection ids) {

  List result = new ArrayList<>(ids.size());

  ids.forEach(id -> result.add(Objects.toString(id)));

  return result;

  }

  }

  注意⚠️:

  1、此處為 訂單id和客戶id的復合分片算法。

  2、由於訂單id太長,所以使用了 BigDecimal類型。

  3、訂單id和客戶id的后2位都可以確定數據最終是路由在哪張表中。

  4、目前只實現了=和in的操作,不支持范圍操作。

  4、分表配置

  # 啟用 sharding-jdbc

  spring.shardingsphere.enabled=true

  # 配置數據源的名字

  spring.shardingsphere.datasource.names=master

  # 數據源配置

  spring.shardingsphere.datasource.master.type=com.zaxxer.hikari.HikariDataSource

  spring.shardingsphere.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver

  spring.shardingsphere.datasource.master.jdbc-url=jdbc:mysql://127.0.0.1:3306/temp_work?useUnicode=true&characterEncoding=utf8&autoReconnectForPools=true&useSSL=false

  spring.shardingsphere.datasource.master.username=root

  spring.shardingsphere.datasource.master.password=root

  # 配置默認數據源為 master,即沒有配置分表的數據,使用次數據源

  spring.shardingsphere.sharding.default-data-source-name=master

  # 數據庫中實際的表

  spring.shardingsphere.sharding.tables.customer_order.actual-data-nodes=master.customer_order_$->{0..2}

  # 分片列

  spring.shardingsphere.sharding.tables.customer_order.table-strategy.complex.sharding-columns=order_id,customer_id

  # 分片算法

  spring.shardingsphere.sharding.tables.customer_order.table-strategy.complex.algorithm-class-name=com.huan.study.sharding.algorithm.OrderComplexKeysShardingAlgorithm

  # 顯示sql

  spring.shardingsphere.props.sql.show=true

  spring.shardingsphere.sharding.tables.customer_order: 我們自己在程序中寫sql時,訂單表直接使用邏輯表customer_order即可,而不要使用真實的表,比如(customer_order_0等)。

  spring.shardingsphere.sharding.tables.customer_order.table-strategy.complex.sharding-columns:指定需要分表的列。

  spring.shardingsphere.sharding.tables.customer_order.table-strategy.complex.algorithm-class-name:指定復合分表算法類,指定的類需要有一個無參的構造方法。

  5、mapper文件寫法

  insert into customer_order(order_id,customer_id,saller_id,product_name) values (#{orderId},#{customerId},#{sallerId},#{productName})

  需要注意,此處寫的是邏輯表(customer_order),這個表在數據庫中是不存在的,是在分表配置時指定的邏輯表。

  五、完整代碼

  完整代碼: https://gitee.com/huan1993/spring-cloud-parent/blob/master/sharding-jdbc/src/main/java/com/huan/study/sharding/algorithm/OrderComplexKeysShardingAlgorithm.java

  git提交commitId: b14c1584b89991e909bd6852b1217872414d9db7鄭州做人流哪里好http://www.zzchxb120.com/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM