成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

警惕!MyBatis-Plus 主鍵生成策略的隱藏坑,踩過都哭了!

開發
如果你在 MyBatis-Plus 的 集群環境 中遇到了 主鍵重復 的問題,不要只是修修補補,而是 直接換用 Seata 的雪花算法,徹底解決 ID 生成沖突,避免線上事故!

在 MyBatis-Plus 的使用過程中,我們經常會享受到其便捷的 CRUD 操作,特別是內置的主鍵生成策略,省去了手動管理 ID 的繁瑣。然而,當項目進入生產環境,特別是在 集群部署 或 K8S 容器化部署 后,你可能會遇到一個令人頭疼的問題——主鍵重復。

這并不是一個小概率事件,而是許多開發者在 高并發分布式環境 下都會踩中的坑。一旦主鍵重復,數據庫插入操作將直接失敗,影響正常業務流程,甚至可能導致整個系統不可用。更糟糕的是,很多開發者在調試時可能并沒有意識到問題的根源,導致線上 Bug 難以復現,排查困難。

本篇文章將深入剖析 MyBatis-Plus 主鍵生成策略的機制,探討其在 Docker、K8S 及集群環境下為何會導致主鍵沖突,并提供一個更加穩定、高效的 分布式 ID 生成方案。如果你在 MyBatis-Plus 項目中使用了默認的主鍵策略,強烈建議閱讀本文,否則你很可能在未來的某一天,因主鍵重復問題而陷入崩潰的境地!

以下是一個典型的錯誤日志:

Mybatis-Plus 啟動時會通過 com.baomidou.mybatisplus.core.toolkit.Sequence 類的
getMaxWorkerId() 和 getDatacenterId() 方法來初始化 workerId 和 dataCenterId。

讓我們來看一下 MyBatis-Plus 生成 workerId 和 dataCenterId 的關鍵代碼:

  • Worker ID 生成邏輯
protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {
    StringBuilder mpid = new StringBuilder();
    mpid.append(datacenterId);
    String name = ManagementFactory.getRuntimeMXBean().getName();
    if (StringUtils.isNotBlank(name)) {
        mpid.append(name.split("@")[0]);
    }
    return (long)(mpid.toString().hashCode() & '\uffff') % (maxWorkerId + 1L);
}
  • Data Center ID 生成邏輯
protected long getDatacenterId(long maxDatacenterId) {
    byte[] mac = network.getHardwareAddress();
    if (null != mac) {
        id = (255L & (long)mac[mac.length - 2] | 65280L & (long)mac[mac.length - 1] << 8) >> 6;
        id %= maxDatacenterId + 1L;
    }
    return id;
}

從代碼可以看出,workerId 由 JVM 進程名稱生成,dataCenterId 由 MAC 地址計算。然而,在 Docker 環境下,容器的 JVM 進程名稱可能重復,MAC 地址也可能被橋接網絡共享,這導致 ID 生成可能發生沖突,進而引發主鍵重復問題。

替代方案:更可靠的雪花算法

與其糾結于如何修復 MyBatis-Plus 的 ID 生成邏輯,不如直接采用 更優化的雪花算法,該算法不僅能解決 ID 沖突,還能 提升數據庫性能。

為什么需要優化 ID 生成策略?

在數據庫設計中,分布式 ID 需要滿足以下特性:

  • 全局唯一性:防止 ID 沖突
  • 遞增趨勢:減少 MySQL 數據頁分裂,提高性能
  • 高效生成:保證高并發環境下 ID 生成的速度

常見的分布式 ID 方案:

  • 百度 UidGenerator
  • 滴滴 TinyID
  • 美團 Leaf
  • Twitter 雪花算法(SnowFlake)

雖然這些方案都能滿足分布式 ID 需求,但大部分需要依賴 數據庫或 Redis,對于中小型項目而言,額外的組件依賴可能帶來運維成本。

因此,我們更推薦 Seata 改進版雪花算法,它不僅優化了 標準版雪花算法的“時鐘回撥”問題,而且實現更加簡潔。

標準版雪花算法的缺陷

傳統雪花算法的 ID 格式如下:

| 時間戳(41位) | 機器 ID(10位) | 序列號(12位) |

時間戳依賴系統時間,如果服務器時鐘回撥,可能會導致 ID 生成沖突。

同一毫秒內的序列號最多 4096(2^12)個,超出后需要等待下一個毫秒。

Seata 優化方案

Seata 對 雪花算法的 ID 結構進行了改造,使其不再依賴系統時間,而是使用 內存中的時間戳遞增,避免了時鐘回撥問題:

核心代碼

/**
 * timestamp 和 sequence 合并存儲在一個 Long 類型中
 * 最高 11 位:未使用
 * 中間 41 位:時間戳
 * 最低 12 位:序列號
 */
private AtomicLong timestampAndSequence;

/**
 * 序列號占用的位數
 */
private final int sequenceBits = 12;

/**
 * 初始化時間戳和序列號
 */
private void initTimestampAndSequence() {
    long timestamp = getNewestTimestamp();
    long timestampWithSequence = timestamp << sequenceBits;
    this.timestampAndSequence = new AtomicLong(timestampWithSequence);
}

代碼解析:

  • 時間戳和序列號合并存儲,通過 AtomicLong 保證線程安全。
  • 時間戳不會直接綁定操作系統,而是采用 內部遞增機制 避免時鐘回撥問題。

Worker ID 生成方式

Seata 還改進了 Worker ID 生成邏輯,避免 MyBatis-Plus 依賴 MAC 地址的問題:


/**
 * 初始化 WorkerId
 * @param workerId 如果為空,則自動生成
 */
private void initWorkerId(Long workerId) {
    if (workerId == null) {
        workerId = generateWorkerId();
    }
    if (workerId > maxWorkerId || workerId < 0) {
        throw new IllegalArgumentException("WorkerId 超出范圍:" + maxWorkerId);
    }
    this.workerId = workerId << (timestampBits + sequenceBits);
}

/**
 * 生成 Worker ID,優先使用 MAC 地址,否則隨機生成
 */
private long generateWorkerId() {
    try {
        return generateWorkerIdBaseOnMac();
    } catch (Exception e) {
        return generateRandomWorkerId();
    }
}

/**
 * 獲取 MAC 地址生成 Worker ID
 */
private long generateWorkerIdBaseOnMac() throws Exception {
    Enumeration<NetworkInterface> all = NetworkInterface.getNetworkInterfaces();
    while (all.hasMoreElements()) {
        NetworkInterface networkInterface = all.nextElement();
        if (networkInterface.isLoopback() || networkInterface.isVirtual()) {
            continue;
        }
        byte[] mac = networkInterface.getHardwareAddress();
        return ((mac[4] & 0B11) << 8) | (mac[5] & 0xFF);
    }
    throw new RuntimeException("沒有可用的 MAC 地址");
}

優化點:

  • Worker ID 優先使用 MAC 地址,無法獲取時則隨機生成,避免 Docker 容器 MAC 共享問題。
  • 時間戳與系統時間解耦,不再受 時鐘回撥影響。
  • 序列號遞增機制優化,保證 高并發環境下的唯一性。

總結

如果你在 MyBatis-Plus 的 集群環境 中遇到了 主鍵重復 的問題,不要只是修修補補,而是 直接換用 Seata 的雪花算法,徹底解決 ID 生成沖突,避免線上事故!

Seata 方案的優勢:

  • 無外部依賴,適合中小型項目
  • 避免時鐘回撥問題,提高 ID 生成穩定性
  • 高性能,支持高并發

如果你的項目仍然采用 MyBatis-Plus 默認的 ID 生成策略,建議盡快引入 Seata 雪花算法,讓你的系統更健壯!

責任編輯:趙寧寧 來源: 路條編程
相關推薦

2022-04-26 21:49:55

Spring事務數據庫

2023-10-31 08:01:48

Mybatis參數jdbcurl?

2019-10-30 14:44:41

Prometheus開源監控系統

2024-04-01 08:05:27

Go開發Java

2024-12-20 16:49:15

MyBatis開發代碼

2017-07-17 15:46:20

Oracle并行機制

2025-04-14 09:31:03

2018-01-10 13:40:03

數據庫MySQL表設計

2025-06-03 06:30:05

2025-05-27 08:45:00

2020-11-03 06:57:10

MyBatis數據庫

2025-04-03 12:30:00

C 語言隱式類型轉換代碼

2025-02-27 09:45:47

2024-05-06 00:00:00

緩存高并發數據

2015-03-24 16:29:55

默認線程池java

2019-09-18 15:20:16

MyBatisSQL數據庫

2025-04-29 10:17:42

2023-06-07 08:00:00

MySQL批量插入

2023-03-13 13:36:00

Go擴容切片

2018-09-11 09:14:52

面試公司缺點
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国精产品一品二品国精在线观看 | 91成人在线视频 | 久久久999成人 | 国产精品久久久久久久久久久免费看 | 亚洲综合二区 | 夜夜骑首页 | 黄色亚洲| 国产精品日韩 | 欧美视频在线看 | 精品日韩| 91色视频在线观看 | 成人小视频在线观看 | 天天插天天操 | 日韩一级黄色毛片 | 国产精品一卡 | 国产999精品久久久久久 | 久久久毛片 | 亚洲精品久久久久久久不卡四虎 | 免费在线一区二区 | 亚洲天堂一区 | 人碰人操 | 亚洲日本中文字幕在线 | 久久久久国产 | 天天操天天天 | 久久免费视频1 | 丁香久久 | 国产福利在线 | 高清人人天天夜夜曰狠狠狠狠 | 青青久视频 | 色久电影| 日韩精品久久久久 | 91久久北条麻妃一区二区三区 | 草草视频在线观看 | 精品国产一区二区三区久久久久久 | 成人h动漫精品一区二区器材 | 久久久资源 | 中文字幕亚洲视频 | 天天干国产 | 成人国产免费观看 | 在线观看视频亚洲 | 免费国产视频在线观看 |