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

一、序言

在實際業(yè)務(wù)中,單表數(shù)據(jù)增長較快,很容易達(dá)到數(shù)據(jù)瓶頸,比如單表百萬級別數(shù)據(jù)量。當(dāng)數(shù)據(jù)量繼續(xù)增長時,數(shù)據(jù)的查詢性能即使有索引的幫助下也不盡如意,這時可以引入數(shù)據(jù)分庫分表技術(shù)。

本文將基于SpringBoot+MybatisPlus+Sharding-JDBC+Mysql實現(xiàn)企業(yè)級分庫分表。

1、組件及版本選擇
SpringBoot
MybatisPlus
Sharding-JDBC
Mysql
SpringBoot 2.6.x MybatisPlus 3.5.0 Sharding-JDBC 4.1.1 Mysql 5.7.35
2、預(yù)期目標(biāo)
  • 使用上述組件實現(xiàn)分庫分表,簡化起見只討論分表技術(shù)
  • 完成分表后的邏輯表與物理表間的增刪查改
  • 引入邏輯刪除和使用MybatisPlus內(nèi)置分頁技術(shù)

完整項目源碼訪問地址。

二、代碼實現(xiàn)

為了簡化分表復(fù)雜性,專注于分表整體實現(xiàn),簡化分表邏輯:按照UserId的奇偶屬性分別進(jìn)行分表。以訂單表這一典型場景為例,一般來說有關(guān)訂單表,通常具有如下共性行為:

  • 創(chuàng)建訂單記錄
  • 查詢XX用戶的訂單列表
  • 查詢XX用戶的訂單列表(分頁)
  • 查詢XX訂單詳情
  • 修改訂單狀態(tài)
  • 刪除訂單(邏輯刪除)

接下來通過代碼實現(xiàn)上述目標(biāo)。

(一)素材準(zhǔn)備

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、保存數(shù)據(jù)

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

@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、查詢列表數(shù)據(jù)

查詢指定用戶的訂單列表。

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

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

@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、刪除數(shù)據(jù)

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

@DeleteMapping("/delete/{orderId}")
public AjaxResult delete(@PathVariable Long orderId) {
    return AjaxResult.success(orderMapper.deleteById(orderId));
}
6、修改數(shù)據(jù)

修改數(shù)據(jù)一般涉及部分列,比如修改訂單表的訂單狀態(tài)等。

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

三、理論分析

1、選擇分片列

選擇分片列是經(jīng)過精心對比后確定的,對于訂單類場景,需要頻繁以用戶ID為查詢條件篩選數(shù)據(jù),因此將同一個用戶的訂單數(shù)據(jù)存放在一起有利于提高查詢效率。

2、擴(kuò)容

當(dāng)分表后的表數(shù)據(jù)快速增長,可以預(yù)見即將達(dá)到瓶頸時,需要對分表進(jìn)行擴(kuò)容,擴(kuò)容以2倍的速率進(jìn)行,擴(kuò)容期間需要遷移數(shù)據(jù),工作量相對可控。


喜歡本文點個??贊??支持一下,如有需要,可通過微信dream4s與我聯(lián)系。相關(guān)源碼在GitHub,視頻講解在B站,本文收藏在博客天地。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容