Mybatis-Plus使用ID_WORKER生成主鍵id重復的解決方法
問題描述
目前項目使用的id是mybatis-plus 內(nèi)置的主鍵生成策略 ID_WORKER ,最近測試在做性能壓測,部署架構(gòu)是單服務集群的部署方式,然后就發(fā)現(xiàn)了id重復的異常,異常如下
問題分析
首先分析的是id生成是不是就是重復了,先關(guān)掉其中一臺機器,單機跑,這個時候發(fā)現(xiàn)壓到1000的并發(fā)都沒有出現(xiàn)過id重復,這個說明單機情況下不存在id重復問題,說明只有集群的情況下才會出現(xiàn)。
再分析一下id生成的幾個要素,雪花算法的核心能影響到id生成的幾個因素:1.服務器時間2.workId(機器 ID 部分)3.datacenterId(數(shù)據(jù)標識 ID 部分)。先檢查了一下服務器時間,都是一樣的,然后再看一下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); //代表正在運行的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主要核心影響就是獲取運行的虛擬機名稱,現(xiàn)在猜測就是有可能是由于兩臺機的虛擬機名稱可能一樣,導致拿到的workerId一樣。這個沒有具體去驗證,猜測是這樣的。那我們嘗試修改一下這個workerId的值。
問題解決
1.先設(shè)置workerId為隨機數(shù),這樣保證每個機器部署的時候拿到的都是隨機數(shù)
# 設(shè)置隨機 mybatis-plus.global-config.worker-id: ${random.int}
這里你啟動項目的時候會發(fā)現(xiàn)一個異常
異常很明顯,這里的worer id 必選小于31,那我們就需要修改一下隨機數(shù)
# 設(shè)置隨機 mybatis-plus.global-config.worker-id: ${random.int(1,31)}
這個時候我們先看一下我們設(shè)置參數(shù)有沒有生效,為了比較明顯看到效果,我們直接設(shè)置worker-id為一個固定值20,再斷點看一下,我們找到
com.baomidou.mybatisplus.core.toolkit.IdWorker
這個核心類,獲取id的核心方法是com.baomidou.mybatisplus.core.toolkit.IdWorker#getId
,那我們就在這里加一個斷點看下
public class IdWorker { /** * 主機和進程的機器碼 */ 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 工作機器 ID * @param datacenterId 序列號 */ 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); } }
斷點后的結(jié)果是:
這個時候看到workerId沒有生效,我們繼續(xù)分析下源碼。
項目啟動的時候,mybatis-plus 會調(diào)用一個com.baomidou.mybatisplus.core.MybatisConfiguration#init
方法來初始化配置信息,我們看下代碼
public void init(GlobalConfig globalConfig) { // 初始化 Sequence //這里需要同時設(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è)置的值。
那我們就修改配置一下
# 設(shè)置隨機 mybatis-plus.global-config.worker-id: ${random.int(1,31)} mybatis-plus.global-config.datacenter-id: ${random.int(1,31)}
這樣設(shè)置以后發(fā)現(xiàn)終于生效了,然后部署一下,問題終于解決了。這里問題雖然解決了,但是workerId重復其實沒有實際驗證過,如果有驗證過的同學歡迎留言。值得一提的是現(xiàn)在mybatis-plus官方以及不提倡使用ID_WORKER和ID_WORKER_STR策略生成id了。
到此這篇關(guān)于Mybatis-Plus使用ID_WORKER生成主鍵id重復的解決方法的文章就介紹到這了,更多相關(guān)Mybatis-Plus ID_WORKER生成主鍵id內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于IDEA創(chuàng)建SpringMVC項目流程圖解
這篇文章主要介紹了基于IDEA創(chuàng)建SpringMVC項目流程圖解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-10-10說一說java關(guān)鍵字final和transient
這篇文章主要和大家說一說java關(guān)鍵字final和transient,感興趣的小伙伴們可以參考一下2016-06-06SpringBoot在RequestBody中使用枚舉參數(shù)案例詳解
這篇文章主要介紹了SpringBoot在RequestBody中使用枚舉參數(shù)案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-09-09Java如何通過反射獲取私有構(gòu)造、私有對象、私有字段、私有方法
這篇文章主要介紹了Java如何通過反射獲取私有構(gòu)造、私有對象、私有字段、私有方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12