Mybatis-Plus使用ID_WORKER生成主鍵id重復(fù)的解決方法
問(wèn)題描述
目前項(xiàng)目使用的id是mybatis-plus 內(nèi)置的主鍵生成策略 ID_WORKER ,最近測(cè)試在做性能壓測(cè),部署架構(gòu)是單服務(wù)集群的部署方式,然后就發(fā)現(xiàn)了id重復(fù)的異常,異常如下
問(wèn)題分析
首先分析的是id生成是不是就是重復(fù)了,先關(guān)掉其中一臺(tái)機(jī)器,單機(jī)跑,這個(gè)時(shí)候發(fā)現(xiàn)壓到1000的并發(fā)都沒(méi)有出現(xiàn)過(guò)id重復(fù),這個(gè)說(shuō)明單機(jī)情況下不存在id重復(fù)問(wèn)題,說(shuō)明只有集群的情況下才會(huì)出現(xiàn)。
再分析一下id生成的幾個(gè)要素,雪花算法的核心能影響到id生成的幾個(gè)因素:1.服務(wù)器時(shí)間2.workId(機(jī)器 ID 部分)3.datacenterId(數(shù)據(jù)標(biāo)識(shí) ID 部分)。先檢查了一下服務(wù)器時(shí)間,都是一樣的,然后再看一下workId的生成,我們先看一下源碼。
public Sequence() {
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
//獲取workerId
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuilder mpid = new StringBuilder();
mpid.append(datacenterId);
//代表正在運(yùn)行的Java虛擬機(jī)的名稱。
String name = ManagementFactory.getRuntimeMXBean().getName();
if (StringUtils.isNotEmpty(name)) {
/*
* GET jvmPid
*/
mpid.append(name.split(StringPool.AT)[0]);
}
/*
* MAC + PID 的 hashcode 獲取16個(gè)低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
這里生成的workerId主要核心影響就是獲取運(yùn)行的虛擬機(jī)名稱,現(xiàn)在猜測(cè)就是有可能是由于兩臺(tái)機(jī)的虛擬機(jī)名稱可能一樣,導(dǎo)致拿到的workerId一樣。這個(gè)沒(méi)有具體去驗(yàn)證,猜測(cè)是這樣的。那我們嘗試修改一下這個(gè)workerId的值。
問(wèn)題解決
1.先設(shè)置workerId為隨機(jī)數(shù),這樣保證每個(gè)機(jī)器部署的時(shí)候拿到的都是隨機(jī)數(shù)
# 設(shè)置隨機(jī)
mybatis-plus.global-config.worker-id: ${random.int}這里你啟動(dòng)項(xiàng)目的時(shí)候會(huì)發(fā)現(xiàn)一個(gè)異常

異常很明顯,這里的worer id 必選小于31,那我們就需要修改一下隨機(jī)數(shù)
# 設(shè)置隨機(jī)
mybatis-plus.global-config.worker-id: ${random.int(1,31)}這個(gè)時(shí)候我們先看一下我們?cè)O(shè)置參數(shù)有沒(méi)有生效,為了比較明顯看到效果,我們直接設(shè)置worker-id為一個(gè)固定值20,再斷點(diǎn)看一下,我們找到
com.baomidou.mybatisplus.core.toolkit.IdWorker
這個(gè)核心類(lèi),獲取id的核心方法是com.baomidou.mybatisplus.core.toolkit.IdWorker#getId
,那我們就在這里加一個(gè)斷點(diǎn)看下
public class IdWorker {
/**
* 主機(jī)和進(jìn)程的機(jī)器碼
*/
private static Sequence WORKER = new Sequence();
//獲取id
public static long getId() {
return WORKER.nextId();
}
public static String getIdStr() {
return String.valueOf(WORKER.nextId());
}
/**
* <p>
* 有參構(gòu)造器
* </p>
*
* @param workerId 工作機(jī)器 ID
* @param datacenterId 序列號(hào)
*/
public static void initSequence(long workerId, long datacenterId) {
WORKER = new Sequence(workerId, datacenterId);
}
/**
* <p>
* 使用ThreadLocalRandom獲取UUID獲取更優(yōu)的效果 去掉"-"
* </p>
*/
public static String get32UUID() {
ThreadLocalRandom random = ThreadLocalRandom.current();
return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);
}
}斷點(diǎn)后的結(jié)果是:

這個(gè)時(shí)候看到workerId沒(méi)有生效,我們繼續(xù)分析下源碼。
項(xiàng)目啟動(dòng)的時(shí)候,mybatis-plus 會(huì)調(diào)用一個(gè)com.baomidou.mybatisplus.core.MybatisConfiguration#init
方法來(lái)初始化配置信息,我們看下代碼
public void init(GlobalConfig globalConfig) {
// 初始化 Sequence
//這里需要同時(shí)設(shè)置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()+" ");
}
}我們發(fā)現(xiàn)workerId和datacenterId必須同時(shí)設(shè)置才會(huì)獲取我們?cè)O(shè)置的值。
那我們就修改配置一下
# 設(shè)置隨機(jī)
mybatis-plus.global-config.worker-id: ${random.int(1,31)}
mybatis-plus.global-config.datacenter-id: ${random.int(1,31)}這樣設(shè)置以后發(fā)現(xiàn)終于生效了,然后部署一下,問(wèn)題終于解決了。這里問(wèn)題雖然解決了,但是workerId重復(fù)其實(shí)沒(méi)有實(shí)際驗(yàn)證過(guò),如果有驗(yàn)證過(guò)的同學(xué)歡迎留言。值得一提的是現(xiàn)在mybatis-plus官方以及不提倡使用ID_WORKER和ID_WORKER_STR策略生成id了。
到此這篇關(guān)于Mybatis-Plus使用ID_WORKER生成主鍵id重復(fù)的解決方法的文章就介紹到這了,更多相關(guān)Mybatis-Plus ID_WORKER生成主鍵id內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PowerJobAutoConfiguration自動(dòng)配置源碼流程解析
這篇文章主要為大家介紹了PowerJobAutoConfiguration自動(dòng)配置源碼流程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Maven發(fā)布項(xiàng)目 (jar包) 到Nexus私服中的操作
這篇文章主要介紹了Maven發(fā)布項(xiàng)目 (jar包) 到Nexus私服中的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
Maven學(xué)習(xí)教程之搭建多模塊企業(yè)級(jí)項(xiàng)目
本篇文章主要介紹了Maven學(xué)習(xí)教程之搭建多模塊企業(yè)級(jí)項(xiàng)目 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
如何用java計(jì)算兩個(gè)時(shí)間相差多少小時(shí)
最近工作中遇到需要計(jì)算時(shí)間差,下面這篇文章主要給大家介紹了關(guān)于如何用java計(jì)算兩個(gè)時(shí)間相差多少小時(shí)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
java中如何判斷JSONObject是否存在某個(gè)Key
這篇文章主要介紹了java中如何判斷JSONObject是否存在某個(gè)Key,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Spring 整合Shiro 并擴(kuò)展使用EL表達(dá)式的實(shí)例詳解
Shiro是一個(gè)輕量級(jí)的權(quán)限控制框架,應(yīng)用非常廣泛。本文的重點(diǎn)是介紹Spring整合Shiro,并通過(guò)擴(kuò)展使用Spring的EL表達(dá)式。需要的朋友可以參考下2018-03-03
MyBatis中的表關(guān)聯(lián)查詢實(shí)現(xiàn)示例
這篇文章主要介紹了MyBatis中的表關(guān)聯(lián)查詢實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Java基礎(chǔ) Servlet監(jiān)聽(tīng)器詳解
這篇文章主要介紹了Java基礎(chǔ) Servlet監(jiān)聽(tīng)器詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07
SpringBoot集成tensorflow實(shí)現(xiàn)圖片檢測(cè)功能
TensorFlow名字的由來(lái)就是張量(Tensor)在計(jì)算圖(Computational?Graph)里的流動(dòng)(Flow),它的基礎(chǔ)就是前面介紹的基于計(jì)算圖的自動(dòng)微分,本文將給大家介紹Spring?Boot集成tensorflow實(shí)現(xiàn)圖片檢測(cè)功能,需要的朋友可以參考下2024-06-06

