mybatis-plus內(nèi)置雪花算法主鍵重復(fù)問(wèn)題解決
問(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í)候回發(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è)核心類,獲取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)}
ties
設(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é)歡迎留言。
到此這篇關(guān)于mybatis-plus內(nèi)置雪花算法主鍵重復(fù)問(wèn)題小結(jié)的文章就介紹到這了,更多相關(guān)mybatis-plus內(nèi)置雪花算法主鍵重復(fù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java開(kāi)發(fā)分布式服務(wù)框架Dubbo原理機(jī)制詳解
這篇文章主要為大家介紹了java開(kāi)發(fā)分布式服務(wù)框架Dubbo的原理機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11Java使用Lua實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)展和腳本自動(dòng)升級(jí)
Lua是一種輕量級(jí)的腳本語(yǔ)言,常用于游戲開(kāi)發(fā)和嵌入式系統(tǒng)中,這篇文章主要介紹了Java如何調(diào)用Lua實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)展和腳本自動(dòng)升級(jí),感興趣的可以學(xué)習(xí)下2023-08-08mybatis關(guān)系映射之一對(duì)多和多對(duì)一
今天小編就為大家分享一篇關(guān)于mybatis關(guān)系映射之一對(duì)多和多對(duì)一,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01springboot多模塊項(xiàng)目mvn打包遇到存在依賴但卻無(wú)法發(fā)現(xiàn)符號(hào)問(wèn)題
在SpringBoot多模塊項(xiàng)目中,如果遇到依賴存在但無(wú)法發(fā)現(xiàn)符號(hào)的問(wèn)題,常見(jiàn)原因可能是pom.xml配置問(wèn)題,例如,如果某個(gè)模塊僅作為依賴而不是啟動(dòng)工程,不應(yīng)在其pom中配置spring-boot-maven-plugin插件,因?yàn)檫@將影響jar包的生成方式2024-09-09Java實(shí)現(xiàn)瀏覽器大文件上傳的示例詳解
文件上傳是許多項(xiàng)目都有的功能,用戶上傳小文件速度一般都很快,但如果是大文件幾個(gè)g,幾十個(gè)g的時(shí)候,上傳了半天,馬上就要完成的時(shí)候,網(wǎng)絡(luò)波動(dòng)一下,文件又要重新上傳,所以本文給大家介紹了Java實(shí)現(xiàn)瀏覽器大文件上傳的示例,需要的朋友可以參考下2024-07-07Hibernate框架數(shù)據(jù)分頁(yè)技術(shù)實(shí)例分析
這篇文章主要介紹了Hibernate框架數(shù)據(jù)分頁(yè)技術(shù),結(jié)合實(shí)例形式分析了Hibernate框架實(shí)現(xiàn)數(shù)據(jù)分頁(yè)的原理,步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-03-03SpringBoot+Redis執(zhí)行l(wèi)ua腳本的5種方式總結(jié)
Lua是一種快速、輕量級(jí)的腳本語(yǔ)言,廣泛應(yīng)用于各種領(lǐng)域,包括數(shù)據(jù)庫(kù),Redis作為一個(gè)內(nèi)嵌Lua解釋器的NoSQL數(shù)據(jù)庫(kù),允許通過(guò)Lua腳本在服務(wù)器端執(zhí)行一些復(fù)雜的操作,本文給大家介紹了使用SpringBoot Redis執(zhí)行l(wèi)ua腳本的五種方式,需要的朋友可以參考下2023-11-11淺談Java double 相乘的結(jié)果偏差小問(wèn)題
下面小編就為大家?guī)?lái)一篇淺談Java double 相乘的結(jié)果偏差小問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01