當前位置: 妍妍網 > 碼農

MyBatis-Plus 內建雪花演算法主鍵重復問題

2024-05-31碼農

目前計畫使用的id是mybatis-plus 內建的主鍵生成策略 ID_WORKER ,最近測試在做效能壓測,部署架構是單服務集群的部署方式,然後就發現了id重復的異常,異常如下

問題分析

首先分析的是id生成是不是就是重復了,先關掉其中一台機器,單機跑,這個時候發現壓到1000的並行都沒有出現過id重復,這個說明單機情況下不存在id重復問題,說明只有集群的情況下才會出現。

再分析一下id生成的幾個要素,雪花演算法的核心能影響到id生成的幾個因素:

  1. 伺服器時間

  2. workId(機器 ID 部份)

  3. datacenterId(數據標識 ID 部份)。

先檢查了一下伺服器時間,都是一樣的,然後再看一下workId的生成,我們先看一下源碼。

publicSequence(){
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
//獲取workerId
protectedstaticlonggetMaxWorkerId(long datacenterId, long maxWorkerId){
StringBuilder mpid = new StringBuilder();
mpid.append(datacenterId);
//代表正在執行的Java虛擬機器的名稱。
String name = ManagementFactory.getRuntimeMXBean().getName();
if (StringUtils.isNotEmpty(name)) {
/*
* GET jvmPid
*/

mpid.append(name.split(StringPool.AT)[0]);
}
/*
* MAC + PID 的 hashcode 獲取16個低位
*/

return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}

這裏生成的workerId主要核心影響就是獲取執行的虛擬機器名稱,現在猜測就是有可能是由於兩台機的虛擬機器名稱可能一樣,導致拿到的workerId一樣。這個沒有具體去驗證,猜測是這樣的。那我們嘗試修改一下這個workerId的值。

問題解決

先設定workerId為隨機數,這樣保證每個機器部署的時候拿到的都是隨機數

# 設定隨機
mybatis-plus.global-config.worker-id: ${random.int}

這裏你啟動計畫的時候回發現一個異常

異常很明顯,這裏的worer id 必選小於31,那我們就需要修改一下隨機數

# 設定隨機
mybatis-plus.global-config.worker-id: ${random.int(1,31)}

這個時候我們先看一下我們設定參數有沒有生效,為了比較明顯看到效果,我們直接設定worker-id為一個固定值20,再斷點看一下,我們找到 com.baomidou.mybatisplus.core.toolkit.IdWorker 這個核心類,獲取id的核心方法是 com.baomidou.mybatisplus.core.toolkit.IdWorker#getId ,那我們就在這裏加一個斷點看下

public classIdWorker{
/**
* 主機和行程的機器碼
*/

privatestatic Sequence WORKER = new Sequence();
//獲取id
publicstaticlonggetId(){
return WORKER.nextId();
}
publicstatic String getIdStr(){
return String.valueOf(WORKER.nextId());
}
/**
* <p>
* 有參構造器
* </p>
*
@param workerId 工作機器 ID
@param datacenterId 序列號
*/

publicstaticvoidinitSequence(long workerId, long datacenterId){
WORKER = new Sequence(workerId, datacenterId);
}
/**
* <p>
* 使用ThreadLocalRandom獲取UUID獲取更優的效果 去掉"-"
* </p>
*/

publicstatic String get32UUID(){
ThreadLocalRandom random = ThreadLocalRandom.current();
returnnew UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);
}
}



斷點後的結果是:

這個時候看到workerId沒有生效,我們繼續分析下源碼。

計畫啟動的時候,mybatis-plus 會呼叫一個 com.baomidou.mybatisplus.core.MybatisConfiguration#init 方法來初始化配置資訊,我們看下程式碼

publicvoidinit(GlobalConfig globalConfig){
// 初始化 Sequence
//這裏需要同時設定workerId和datacenterId
if (null != globalConfig.getWorkerId()
&& null != globalConfig.getDatacenterId()) {
IdWorker.initSequence(globalConfig.getWorkerId(), globalConfig.getDatacenterId());
}
// 打印 Banner
if (globalConfig.isBanner()) {
System.out.println(" _ _ |_ _ _|_. ___ _ | _ ");
System.out.println("| | |\\/|_)(_| | |_\\ |_)||_|_\\ ");
System.out.println(" / | ");
System.out.println(" "+MybatisPlusVersion.getVersion()+" ");
}
}

我們發現workerId和datacenterId必須同時設定才會獲取我們設定的值。

那我們就修改配置一下

# 設定隨機
mybatis-plus.global-config.worker-id: ${random.int(1,31)}
mybatis-plus.global-config.datacenter-id: ${random.int(1,31)}

這樣設定以後發現終於生效了,然後部署一下,問題終於解決了。這裏問題雖然解決了,但是workerId重復其實沒有實際驗證過,如果有驗證過的同學歡迎留言。

來源 :blog.csdn.net/wagnteng/article/details/117064242

>>

END

精品資料,超贊福利,免費領

微信掃碼/長按辨識 添加【技術交流群

群內每天分享精品學習資料

最近開發整理了一個用於速刷面試題的小程式;其中收錄了上千道常見面試題及答案(包含基礎並行JVMMySQLRedisSpringSpringMVCSpringBootSpringCloud訊息佇列等多個型別),歡迎您的使用。

👇👇

👇點選"閱讀原文",獲取更多資料(持續更新中