SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分庫分表實踐

Java知識圖譜發表於2022-03-01

一、序言

在實際業務中,單表資料增長較快,很容易達到資料瓶頸,比如單表百萬級別資料量。當資料量繼續增長時,資料的查詢效能即使有索引的幫助下也不盡如意,這時可以引入資料分庫分表技術。

本文將基於SpringBoot+MybatisPlus+Sharding-JDBC+Mysql實現企業級分庫分表。

1、元件及版本選擇
SpringBoot2 MybatisPlus2 Sharding-JDBC Mysql
SpringBoot 2.6.x MybatisPlus 3.5.0 Sharding-JDBC 4.1.1 Mysql 5.7.35
2、預期目標
  • 使用上述元件實現分庫分表,簡化起見只討論分表技術
  • 完成分表後的邏輯表與物理表間的增刪查改
  • 引入邏輯刪除和使用MybatisPlus內建分頁技術

完整專案原始碼訪問地址

二、程式碼實現

為了簡化分表複雜性,專注於分表整體實現,簡化分表邏輯:按照UserId的奇偶屬性分別進行分表。以訂單表這一典型場景為例,一般來說有關訂單表,通常具有如下共性行為:

  • 建立訂單記錄
  • 查詢XX使用者的訂單列表
  • 查詢XX使用者的訂單列表(分頁)
  • 查詢XX訂單詳情
  • 修改訂單狀態
  • 刪除訂單(邏輯刪除)

接下來通過程式碼實現上述目標。

(一)素材準備

1、實體類
@Data
@TableName("bu_order")
public class Order {
    @TableId
    private Long orderId;
    private Integer orderType;
    private Long userId;
    private Double amount;
    private Integer orderStatus;
    @TableLogic
    @JsonIgnore
    private Boolean deleted;
}
2、Mapper類
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
3、全域性配置檔案
spring:
  config:
    use-legacy-processing: true
  shardingsphere:
    datasource:
      ds1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://127.0.0.1:3306/sharding-jdbc2?serverTimezone=UTC
        username: root
        password: 123456
      names: ds1
    props:
      sql:
        show: true
    sharding:
      tables:
        bu_order:
          actual-data-nodes: ds1.bu_order_$->{0..1}
          key-generator:
            column: order_id
            type: SNOWFLAKE
          table-strategy:
            inline:
              algorithm-expression: bu_order_${user_id%2}
              sharding-column: user_id

(二)增刪查改

1、儲存資料

由於依據主鍵的奇偶屬性對原表分表,分表後每張表的資料量是分表前的二分之一。根據需要也可以自定義分表數量(比如10張),新分表後的資料量是不分表前的十分之一。

@Test
public void addOrders() {
    for (long i = 1; i <= 10; i++) {
        Order order = new Order();
        order.setOrderId(i);
        order.setOrderType(RandomUtil.randomEle(Arrays.asList(1, 2)));
        order.setUserId(RandomUtil.randomEle(Arrays.asList(101L, 102L, 103L)));
        order.setAmount(1000.0 * i);
        orderMapper.insert(order);
    }
}
2、查詢列表資料

查詢指定使用者的訂單列表。

@GetMapping("/list")
public AjaxResult list(Order order) {
    LambdaQueryWrapper<Order> wrapper = Wrappers.lambdaQuery(order);
    return AjaxResult.success(orderMapper.selectList(wrapper));
}
3、分頁查詢資料

分頁查詢指定使用者的訂單列表

@GetMapping("/page")
public AjaxResult page(Page<Order> page, Order order) {
    return AjaxResult.success(orderMapper.selectPage(page, Wrappers.lambdaQuery(order)));
}
4、查詢詳情

通過訂單ID查詢訂單詳情。

@GetMapping("/detail/{orderId}")
public AjaxResult detail(@PathVariable Long orderId) {
    return AjaxResult.success(orderMapper.selectById(orderId));
}
5、刪除資料

通過訂單ID刪除訂單(邏輯刪除)

@DeleteMapping("/delete/{orderId}")
public AjaxResult delete(@PathVariable Long orderId) {
    return AjaxResult.success(orderMapper.deleteById(orderId));
}
6、修改資料

修改資料一般涉及部分列,比如修改訂單表的訂單狀態等。

@PutMapping("/edit")
public AjaxResult edit(@RequestBody Order order) {
    return AjaxResult.success(orderMapper.updateById(order));
}

三、理論分析

1、選擇分片列

選擇分片列是經過精心對比後確定的,對於訂單類場景,需要頻繁以使用者ID為查詢條件篩選資料,因此將同一個使用者的訂單資料存放在一起有利於提高查詢效率。

2、擴容

當分表後的表資料快速增長,可以預見即將達到瓶頸時,需要對分表進行擴容,擴容以2倍的速率進行,擴容期間需要遷移資料,工作量相對可控。


相關文章