mybatis plus雪花算法原理 mybatis-plus雪花算法自动生成机器id原理及源码
叁滴水 人气:0想了解mybatis-plus雪花算法自动生成机器id原理及源码的相关内容吗,叁滴水在本文为您仔细讲解mybatis plus雪花算法原理的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:mybatis,plus雪花id,mybatis,plus自动生成机器id,mybatis,plus雪花算法原理,下面大家一起来学习吧。
1、雪花算法原理
雪花算法使用一个 64 bit 的 long 型的数字作为全局唯一 id。这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。
- 1bit,不用,因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所以最高位固定为0。
- 41bit-时间戳,用来记录时间戳,毫秒级。
- 10bit-工作机器id,用来记录工作机器id。
- 12bit-序列号,序列号,用来记录同毫秒内产生的不同id。即可以用0、1、2、3、…4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。
SnowFlake可以保证:
所有生成的id按时间趋势递增
整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分)
如上大概了解了雪花算法的原理,而且也知道机器号对于雪花算法的重要性。如果机器号一样,可能会出现id重复的情况。
mybatis-plus自3.3.0开始,默认使用雪花算法+UUID(不含中划线),但是它并没有强制让开发者配置机器号。这一点很是疑惑,这样可能会让不了解雪花算法的人埋下了一个坑。
但是这么强大的一个框架难道真的没有做优化吗?带着问题,查看了下mybatis-plus雪花算法源码com.baomidou.mybatisplus.core.toolkit.Sequence
。最终发现在没有设置机器号的情况下,会通过当前物理网卡地址和jvm的进程ID自动生成。这真的是一个较好的解决方案。一般在一个集群中,MAC+JVM进程PID
一样的几率非常小。
2、自动生成唯一机器号源码
核心代码。有两个构造方法,一个无参构造,一个有参构造。
public Sequence() { //通过当前物理网卡地址获取datacenterId this.datacenterId = getDatacenterId(maxDatacenterId); //物理网卡地址+jvm进程pi获取workerId this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); } /** * 有参构造器 * * @param workerId 工作机器 ID * @param datacenterId 序列号 */ public Sequence(long workerId, long datacenterId) { Assert.isFalse(workerId > maxWorkerId || workerId < 0, String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0, String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); this.workerId = workerId; this.datacenterId = datacenterId; }
- 无参构造 开发者没有设置机器号时
- 有参构造 开发者自行设置机器号
protected static long getDatacenterId(long maxDatacenterId) { long id = 0L; try { //获取本机(或者服务器ip地址) //DESKTOP-123SDAD/192.168.1.87 InetAddress ip = InetAddress.getLocalHost(); NetworkInterface network = NetworkInterface.getByInetAddress(ip); //一般不是null会进入else if (network == null) { id = 1L; } else { //获取物理网卡地址 byte[] mac = network.getHardwareAddress(); if (null != mac) { id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6; id = id % (maxDatacenterId + 1); } } } catch (Exception e) { logger.warn(" getDatacenterId: " + e.getMessage()); } return id; }
/** * 获取 maxWorkerId */ protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { StringBuilder mpid = new StringBuilder(); mpid.append(datacenterId); //获取jvm进程信息 String name = ManagementFactory.getRuntimeMXBean().getName(); if (StringUtils.isNotBlank(name)) { /* * 获取进程PID */ mpid.append(name.split(StringPool.AT)[0]); } /* * MAC + PID 的 hashcode 获取16个低位 */ return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); }
加载全部内容