自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

實(shí)戰(zhàn):Springboot3+ShardingSphere5.2.1生產(chǎn)級(jí)分庫分表實(shí)現(xiàn)

數(shù)據(jù)庫 其他數(shù)據(jù)庫
我們完成了在DailyMart中集成分庫分表功能的實(shí)踐,大家在實(shí)施分庫分表過程中一定要結(jié)合自己的業(yè)務(wù)實(shí)際選擇合理的分片鍵,分片鍵的好壞決定了你分庫分表架構(gòu)方案的好壞。

大家好,我是飄渺。

隨著業(yè)務(wù)的不斷發(fā)展,DailyMart每天產(chǎn)生的銷售訂單已經(jīng)達(dá)到了約100萬,并且呈持續(xù)增長(zhǎng)趨勢(shì)。按照這樣的發(fā)展速度,每年的數(shù)據(jù)量將達(dá)到約4億左右。目前,DailyMart采用的是MySQL單表進(jìn)行存儲(chǔ),但鑒于業(yè)務(wù)的快速發(fā)展,我們迫切需要對(duì)其進(jìn)行分庫分表的改造。今天,我們來探討如何實(shí)現(xiàn)分庫分表功能,以及相關(guān)的步驟和注意事項(xiàng)。

這是本系列文章的第31篇,歡迎持續(xù)關(guān)注。

對(duì)于分庫分表的相關(guān)知識(shí),我的星球分庫分表專欄有詳細(xì)的介紹說明,強(qiáng)烈推薦大家加入學(xué)習(xí)。

分庫分表的核心在于合理選擇分片鍵以及快速定位非分片鍵的數(shù)據(jù)。

分片鍵的選擇

DailyMart作為一個(gè)ToC的業(yè)務(wù)系統(tǒng),大部分業(yè)務(wù)訪問都是基于用戶ID進(jìn)行的,比如登錄用戶查看自己的購買記錄等。因此,對(duì)于訂單模塊我們決定以用戶ID作為分片鍵。

在訂單模塊中,訂單主表 CUSTOMER_ORDER 和訂單明細(xì)表 ORDER_ITEM 是最核心的兩張表,由于它們經(jīng)常會(huì)一起使用,我們也需要將訂單明細(xì)表的用戶字段 CUSTOMER_ID 作為分片鍵,以確?;谟脩艟S度的查詢?cè)趩蝹€(gè)分片上完成。下面是一個(gè)示例SQL:

SELECT * FROM CUSTOMER_ORDER ORDER
LEFT JOIN ORDER_ITEM ITEM ON ORDER.order_sn = ITEM.order_sn
WHERE ORDER.customer_id = 2846741676215238657
ORDER BY create_time DESC LIMIT 10

非分片鍵查詢

既然確定使用用戶ID作為分片鍵,大部分查詢都需要帶上CUSTOMER_ID作為查詢條件。但在實(shí)際使用中,經(jīng)常會(huì)根據(jù)訂單編號(hào)ORDER_SN進(jìn)行精確查詢,比如庫存扣減、支付后的反查等。在默認(rèn)情況下,根據(jù)訂單編號(hào)(非分片鍵)進(jìn)行查詢將需要在所有分片上進(jìn)行查詢,然后對(duì)結(jié)果進(jìn)行聚合,顯然這樣的查詢效率是很低的。

為了解決這個(gè)問題,業(yè)界一般采用基因法來解決,即將分片鍵的信息保存在想要查詢的列中,這樣通過查詢的列就能直接知道數(shù)據(jù)所在的分片信息。

基因法的原理是 對(duì)一個(gè)數(shù)取余2的n次方,那么余數(shù)就是這個(gè)數(shù)的二進(jìn)制的最后n位數(shù)。

以訂單表為例,對(duì)訂單表我們根據(jù)CUSOMER_ID將其拆成16張表,采用CUSOMER_ID % 16的方式來進(jìn)行數(shù)據(jù)庫路由,這里的CUSOMER_ID % 16,其本質(zhì)是CUSOMER_ID的最后4個(gè)bit位 log(16,2) = 4 決定這行數(shù)據(jù)落在哪個(gè)分片上,這4個(gè)bit就是分片基因。

基于這一理論,基因法有兩種具體的實(shí)現(xiàn):

基因替換法

  1. 在生成訂單編號(hào)ORDER_SN時(shí),先使用一種分布式ID生成算法生成前60bit
  2. 計(jì)算出分片基因:分庫基因是CUSTOMER_ID的最后4個(gè)bit,log(16,2) = 4,即1001
  3. 將分庫基因加入到ORDER_SN的最后4個(gè)bit(上圖中粉色部分)
  4. 拼裝成最終的64bit訂單ORDER_SN(上圖中藍(lán)色部分)

圖片圖片

這樣保證了同一個(gè)用戶創(chuàng)建的所有訂單都落到了同一個(gè)分片上,ORDER_SN的最后4個(gè)bit都相同,通過CUSTOMER_ID %16 能夠定位到分片,通過ORDER_SN % 16也能定位到分片。

基因替換法可能會(huì)導(dǎo)致ORDER_SN重復(fù),以雪花算法為例,假設(shè)同一個(gè)用戶在一毫秒內(nèi)創(chuàng)建了 2 個(gè)訂單,這樣生產(chǎn)的序列號(hào)相差1,替換掉基因后對(duì)應(yīng)的二進(jìn)制都相同了,導(dǎo)致ORDER_SN也是重復(fù)的。但這種情況非常少見,除非是機(jī)器人刷單。當(dāng)然如果要徹底杜絕訂單編號(hào)重復(fù)問題可以使用下面介紹的基因拼接法。

基因拼接法

基因拼接法更簡(jiǎn)單,就是在構(gòu)建訂單編號(hào)時(shí)直接將用戶基因拼接在生成的ID后面,即:ORDER_SN = string(ORDER_SN + CUSTOMER_ID)

假設(shè)開始生成的訂單號(hào)是3531318506608209922,用戶ID為2846741676215238658,那最終生成的編號(hào)為35313185066082099222846741676215238658。為了減少長(zhǎng)度,我們可以只取用戶ID的最后6位進(jìn)行拼接,生成的編號(hào)為3531318506608209922238658,這樣可以支持2^6=64個(gè)分片。

那么此時(shí)如果根據(jù) ORDER_SN 進(jìn)行查詢:

SELECT * FROM CUSTOMER_ORDER
WHERE ORDER_SN = '3531318506608209922238658';

由于字段 ORDER_SN 的設(shè)計(jì)中直接包含了分片鍵信息,所以我們可以直接通過分片鍵部分直接定位到分片上。

基因拼接法的缺點(diǎn)是,對(duì)應(yīng)的鍵會(huì)變大一些,存儲(chǔ)也會(huì)相應(yīng)變大,但是卻可以大大提升后續(xù)的查詢效率,這種空間換時(shí)間的設(shè)計(jì),總體上看是非常值得的。

實(shí)際上淘寶的訂單號(hào)也是這樣構(gòu)建的,如下圖所示,訂單的最后6位都是607041,所以大概率推測(cè)出:

  1. 淘寶訂單表的分片鍵是用戶 ID;
  2. 淘寶訂單表,訂單表的主鍵包含用戶 ID,也就是分片信息。這樣通過訂單號(hào)進(jìn)行查詢,可以獲得分片信息,從而查詢 1 個(gè)分片就能得到最終的結(jié)果。

圖片圖片

代碼實(shí)現(xiàn)

在DailyMart中選擇使用shardingsphere實(shí)現(xiàn)分庫分表功能,不過為了方便演示,我在這里只進(jìn)行分表操作。

1、首先,將原始訂單表和訂單明細(xì)表分別拆成4個(gè)表

圖片圖片

2、在訂單模塊基礎(chǔ)設(shè)施層中引入shardingsphere,

<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  <version>5.2.1</version>
</dependency>

3、編寫復(fù)合分片算法,實(shí)現(xiàn)基于order_sn和customer_id的查詢

public class OrderGenComplexTableAlgorithm implements ComplexKeysShardingAlgorithm<Comparable<?>> {
  ...
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Comparable<?>> shardingValue) {

        Map<String, Collection<Comparable<?>>> columnNameAndShardingValuesMap = shardingValue.getColumnNameAndShardingValuesMap();

        Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());

        if(MapUtils.isNotEmpty(columnNameAndShardingValuesMap)){
            // 獲取用戶ID
            Collection<Comparable<?>> userIdCollection = columnNameAndShardingValuesMap.get(USER_ID_COLUMN);
            //用戶分片
            if(CollectionUtils.isNotEmpty(userIdCollection)){
                userIdCollection.stream().findFirst().ifPresent(comparable -> {
                    long tableNameSuffix = (Long) comparable % shardingCount;
                    result.add(shardingValue.getLogicTableName() + "_" + tableNameSuffix);
                });
            }else {
                Collection<Comparable<?>> orderSnCollection = columnNameAndShardingValuesMap.get(ORDER_ID_COLUMN);
                orderSnCollection.stream().findFirst().ifPresent(comparable -> {
                    String orderSn = String.valueOf(comparable);
                    //獲取用戶基因
                    String substring = orderSn.substring(Math.max(0, orderSn.length() - 6));
                    long tableNameSuffix = Long.parseLong(substring) % shardingCount;
                    result.add(shardingValue.getLogicTableName() + "_" + tableNameSuffix);
                });
            }
        }
        return result;
    }
  ...
}

在上述代碼中,當(dāng)通過用戶ID進(jìn)行查詢時(shí)直接通過分片鍵取模定位分片,如果是基于訂單查詢先獲取用戶基因,再根據(jù)用戶基因取模定位分片。

4、在application.yaml中配置分庫分表

spring:
  shardingsphere:
    datasource:
      names: ds0
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: org.mariadb.jdbc.Driver
    rules:
      sharding:
        sharding-algorithms:
          order-gen-complex-sharding:
            type: CLASS_BASED
            props:
              strategy: COMPLEX
              algorithmClassName: com.jianzh5.dailymart.module.order.infrastructure.config.OrderGenComplexTableAlgorithm
              sharding-count: 4
        tables:
          customer_order:
            actual-data-nodes: ds0.customer_order_$->{0..3}
            table-strategy:
              complex:
                sharding-algorithm-name: order-gen-complex-sharding
                sharding-columns: order_sn,customer_id
          order_item:
            actual-data-nodes: ds0.order_item_$->{0..3}
            table-strategy:
              complex:
                sharding-algorithm-name: order-gen-complex-sharding
                sharding-columns: order_sn,customer_id

通過上述步驟,在訂單模塊中已經(jīng)集成了分庫分表功能,接下來編寫兩個(gè)接口對(duì)其進(jìn)行測(cè)試。

測(cè)試

在訂單模塊的接口層我們定義了兩個(gè)接口用于模擬實(shí)際的業(yè)務(wù)場(chǎng)景:1、獲取指定用戶的訂單分頁列表;2、根據(jù)訂單編號(hào)獲取訂單詳情。

接口定義如下:

@Operation(summary = "根據(jù)用戶ID分頁查詢訂單")
@GetMapping("/api/pd/order/page")
public PageResponse<OrderRespDTO> pageQuery(@Valid OrderPageQueryDTO orderPageQueryDTO) {
  return orderService.findListByUserId(orderPageQueryDTO);
}


@Operation(summary = "根據(jù)訂單號(hào)查詢訂單詳情")
@GetMapping("/api/pd/order/{orderSn}")
public OrderRespDTO getOrderBySn(@PathVariable("orderSn") String orderSn) {
  return orderService.getOrderBySn(orderSn);
}

通過運(yùn)行結(jié)果可知,根據(jù)用戶訂單獲取分頁列表時(shí)直接根據(jù)Customer_id取模,只需要一次查詢即可定位。

圖片圖片

當(dāng)根據(jù)訂單號(hào)查詢訂單詳情時(shí),根據(jù)用戶基因取模,同樣也只需要一次查詢即可定位。

圖片圖片

小結(jié)

通過以上步驟,我們完成了在DailyMart中集成分庫分表功能的實(shí)踐,大家在實(shí)施分庫分表過程中一定要結(jié)合自己的業(yè)務(wù)實(shí)際選擇合理的分片鍵,分片鍵的好壞決定了你分庫分表架構(gòu)方案的好壞。

責(zé)任編輯:武曉燕 來源: JAVA日知錄
相關(guān)推薦

2024-03-08 08:43:30

2023-08-11 08:59:49

分庫分表數(shù)據(jù)數(shù)據(jù)庫

2020-11-17 08:08:34

分庫分表

2022-10-10 17:37:59

分庫分表訂單業(yè)務(wù)

2020-07-30 17:59:34

分庫分表SQL數(shù)據(jù)庫

2022-06-30 07:34:46

分庫分表外賣訂單系統(tǒng)

2011-08-16 14:41:33

蘋果iPad3

2019-11-12 09:54:20

分庫分表數(shù)據(jù)

2021-09-08 09:48:39

數(shù)據(jù)庫工具技術(shù)

2022-10-11 17:51:49

分庫分表數(shù)據(jù)庫

2022-07-01 10:37:18

分庫分表數(shù)據(jù)庫

2021-08-31 20:21:11

VitessMySQL分庫

2020-11-18 09:39:02

MySQL數(shù)據(jù)庫SQL

2025-02-10 08:20:09

2022-07-11 08:16:47

NewSQL關(guān)系數(shù)據(jù)庫系統(tǒng)

2020-07-28 09:04:09

NewSQL分庫分表

2021-01-26 05:37:08

分庫分表內(nèi)存

2022-10-13 17:43:10

MySQL存放數(shù)據(jù)

2021-07-28 15:44:52

Java開發(fā)數(shù)據(jù)庫

2022-07-04 23:24:28

sql優(yōu)化監(jiān)控
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)