一、序言
在實際業務中,單表資料增長較快,很容易達到資料瓶頸,比如單表百萬級別資料量。當資料量繼續增長時,資料的查詢效能
即使有索引的幫助下也不盡如意,這時可以引入資料分庫分表
技術。
本文將基於SpringBoot
+MybatisPlus
+Sharding-JDBC
+Mysql
實現企業級分庫分表。
1、元件及版本選擇
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倍
的速率進行,擴容期間需要遷移資料,工作量相對可控。