Mybatis-Plus使用ID_WORKER生成主键id重复的解决方法

 更新时间:2022年07月08日 10:16:58   作者:BUG指挥官  
本文主要介绍了Mybatis-Plus使用ID_WORKER生成主键id重复的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

问题描述

目前项目使用的id是mybatis-plus 内置的主键生成策略 ID_WORKER ,最近测试在做性能压测,部署架构是单服务集群的部署方式,然后就发现了id重复的异常,异常如下

问题分析

首先分析的是id生成是不是就是重复了,先关掉其中一台机器,单机跑,这个时候发现压到1000的并发都没有出现过id重复,这个说明单机情况下不存在id重复问题,说明只有集群的情况下才会出现。

再分析一下id生成的几个要素,雪花算法的核心能影响到id生成的几个因素:1.服务器时间2.workId(机器 ID 部分)3.datacenterId(数据标识 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主要核心影响就是获取运行的虚拟机名称,现在猜测就是有可能是由于两台机的虚拟机名称可能一样,导致拿到的workerId一样。这个没有具体去验证,猜测是这样的。那我们尝试修改一下这个workerId的值。

问题解决

1.先设置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 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>
     * 有参构造器
     * </p>
     *
     * @param workerId     工作机器 ID
     * @param datacenterId 序列号
     */
    public static void initSequence(long workerId, long datacenterId) {
        WORKER = new Sequence(workerId, datacenterId);
    }
 
    /**
     * <p>
     * 使用ThreadLocalRandom获取UUID获取更优的效果 去掉"-"
     * </p>
     */
    public static String get32UUID() {
        ThreadLocalRandom random = ThreadLocalRandom.current();
        return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);
    }
 
}

断点后的结果是:

这个时候看到workerId没有生效,我们继续分析下源码。

项目启动的时候,mybatis-plus 会调用一个com.baomidou.mybatisplus.core.MybatisConfiguration#init
方法来初始化配置信息,我们看下代码

public void init(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重复其实没有实际验证过,如果有验证过的同学欢迎留言。值得一提的是现在mybatis-plus官方以及不提倡使用ID_WORKER和ID_WORKER_STR策略生成id了。

到此这篇关于Mybatis-Plus使用ID_WORKER生成主键id重复的解决方法的文章就介绍到这了,更多相关Mybatis-Plus ID_WORKER生成主键id内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Java如何利用数字描述更多的信息

    详解Java如何利用数字描述更多的信息

    在数据库里面 ,通常我们会用数字的递进来描述状态等信息 , 但是如果想进行更复杂的操作 , 就有必要对二进制有一定理解了。本文就来趣味性的探讨一下 , 如何通过更少的空间描述更多的信息
    2022-09-09
  • 基于IDEA创建SpringMVC项目流程图解

    基于IDEA创建SpringMVC项目流程图解

    这篇文章主要介绍了基于IDEA创建SpringMVC项目流程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • 说一说java关键字final和transient

    说一说java关键字final和transient

    这篇文章主要和大家说一说java关键字final和transient,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • SpringBoot在RequestBody中使用枚举参数案例详解

    SpringBoot在RequestBody中使用枚举参数案例详解

    这篇文章主要介绍了SpringBoot在RequestBody中使用枚举参数案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • Springboot配置图片虚拟映射示例代码

    Springboot配置图片虚拟映射示例代码

    这篇文章主要给大家介绍了关于Springboot配置图片虚拟映射的相关资料,文中通过实例代码介绍的非常详细,对大家学习或者使用springboot具有一定的参考学习价值,需要的朋友可以参考下
    2021-11-11
  • java 2d画图示例分享(用java画图)

    java 2d画图示例分享(用java画图)

    这篇文章主要介绍了java 2D画图示例(用java画图),需要的朋友可以参考下
    2014-04-04
  • Sa-Token记住我模式实现七天免登录

    Sa-Token记住我模式实现七天免登录

    这篇文章主要为大家介绍了Sa-Token记住我模式实现七天免登录示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • 服务器CPU高居不下,JAVA线程占用排查方式

    服务器CPU高居不下,JAVA线程占用排查方式

    这篇文章主要介绍了服务器CPU高居不下,JAVA线程占用排查方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Java如何通过反射获取私有构造、私有对象、私有字段、私有方法

    Java如何通过反射获取私有构造、私有对象、私有字段、私有方法

    这篇文章主要介绍了Java如何通过反射获取私有构造、私有对象、私有字段、私有方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringSecurity中的表单认证详细解析

    SpringSecurity中的表单认证详细解析

    这篇文章主要介绍了SpringSecurity中的表单认证详细解析,在上一篇文章中,我们初步引入了 Spring Security,并使用其默认生效的 HTTP 基本认证保护 URL 资源,在本篇文章中我们使用表单认证来保护 URL 资源,需要的朋友可以参考下
    2023-12-12

最新评论