mybatis-plus中的雪花算法

简介: 主要介绍mybatis-plus中用到的雪花算法,如有错误或未考虑完全的地方,望不吝赐教。这里默认大家对雪花算法有一定的了解哈

mybatis-plus 中的主键策略

大家一定都使用过下面的注解

@TableId(value="id", type=IdType.ASSIGN_ID)
privateStringid;

type属性支持多种主键策略,其中IdType.ASSIGN_ID就是使用基于雪花算法的策略生成id

image.png

mybatis-plus源码处理

上面的注解设置了值,对应在MybatisDefaultParameterHandler中有判断这个类型的处理,判断tableInfo.getIdType() == IdType.ID_WORKER时,使用IdWorker.getId()创建了一个雪花算法ID

这里查看使用的是 3.1.0版本源码,所以是 ID_WORKER

protectedstaticObjectpopulateKeys(MetaObjectHandlermetaObjectHandler, TableInfotableInfo,
MappedStatementms, ObjectparameterObject, booleanisInsert) {
if (null==tableInfo) {
/* 不处理 */returnparameterObject;
        }
/* 自定义元对象填充控制器 */MetaObjectmetaObject=ms.getConfiguration().newMetaObject(parameterObject);
// 填充主键if (isInsert&&!StringUtils.isEmpty(tableInfo.getKeyProperty())
&&null!=tableInfo.getIdType() &&tableInfo.getIdType().getKey() >=3) {
ObjectidValue=metaObject.getValue(tableInfo.getKeyProperty());
/* 自定义 ID */if (StringUtils.checkValNull(idValue)) {
if (tableInfo.getIdType() ==IdType.ID_WORKER) {
metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getId());
                } elseif (tableInfo.getIdType() ==IdType.ID_WORKER_STR) {
metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getIdStr());
                } elseif (tableInfo.getIdType() ==IdType.UUID) {
metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.get32UUID());
                }
            }
        }
if (metaObjectHandler!=null) {
if (isInsert&&metaObjectHandler.openInsertFill()) {
// 插入填充metaObjectHandler.insertFill(metaObject);
            } elseif (!isInsert) {
// 更新填充metaObjectHandler.updateFill(metaObject);
            }
        }
returnmetaObject.getOriginalObject();
    }


image.png

其中 Sequence 类是雪花算法真正实现类

雪花算法简单介绍

雪花算法是Twitter设计的根据时间戳、机器标识码和序列号生成的唯一长整型数。

使用一个 64 bit 的 long 型的数字作为全局唯一 id。这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。

2021060411414999.png

  1. 1bit,不用,因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所以最高位固定为0。
  2. 41bit-时间戳,用来记录时间戳,毫秒级。
  3. 10bit-工作机器id,用来记录工作机器id。
  4. 12bit-序列号,序列号,用来记录同毫秒内产生的不同id。即可以用0、1、2、3、…4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。

SnowFlake 算法的优点:

  1. 高性能高可用:生成时不依赖于数据库,完全在内存中生成
  2. 高吞吐:每秒钟能生成数百万的自增 ID
  3. ID 自增:存入数据库中,索引效率高

SnowFlake 算法的缺点:

  1. 依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成 ID 冲突或者重复
  2. 其中10bit-工作机器id,如果手动设置重复也可能会造成 ID 冲突或者重复

Sequence

针对雪花算法中存在的问题,mybatis-plus做了相应的优化

针对时钟回拨

允许一定的回拨范围

image.png


针对工作机器id

提供了一个无参数构造

image.png

在没有设置机器id时,会通过当前物理网卡地址和jvm的进程id自动生成。一般在一个集群中,MAC+JVM进程PID一样的几率非常小

protectedstaticlonggetDatacenterId(longmaxDatacenterId) {
longid=0L;
try {
InetAddressip=InetAddress.getLocalHost();
NetworkInterfacenetwork=NetworkInterface.getByInetAddress(ip);
if (network==null) {
id=1L;
            } else {
byte[] mac=network.getHardwareAddress();
if (null!=mac) {
id= ((0x000000FF& (long) mac[mac.length-1]) | (0x0000FF00& (((long) mac[mac.length-2]) <<8))) >>6;
id=id% (maxDatacenterId+1);
                }
            }
        } catch (Exceptione) {
logger.warn(" getDatacenterId: "+e.getMessage());
        }
returnid;
    }
protectedstaticlonggetMaxWorkerId(longdatacenterId, longmaxWorkerId) {
StringBuildermpid=newStringBuilder();
mpid.append(datacenterId);
Stringname=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);
    }

针对获取时间

高并发场景下System.currentTimeMillis()的性能问题

Sequence中获取当前时间是通过 SystemClock.now(),SystemClock类中主要是通过使用单个调度线程来按毫秒更新时间戳

publicclassSystemClock {
privatefinallongperiod;
privatefinalAtomicLongnow;
privateSystemClock(longperiod) {
this.period=period;
this.now=newAtomicLong(System.currentTimeMillis());
scheduleClockUpdating();
    }
privatestaticSystemClockinstance() {
returnInstanceHolder.INSTANCE;
    }
publicstaticlongnow() {
returninstance().currentTimeMillis();
    }
privatevoidscheduleClockUpdating() {
ScheduledExecutorServicescheduler=Executors.newSingleThreadScheduledExecutor(runnable-> {
Threadthread=newThread(runnable, "System Clock");
thread.setDaemon(true);
returnthread;
        });
scheduler.scheduleAtFixedRate(() ->now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);
    }
}


相关文章
|
1月前
|
算法 Java
雪花算法生成id
雪花算法生成id
|
2月前
|
存储 算法 数据库
C++ “雪花算法“原理
C++ “雪花算法“原理
|
28天前
|
缓存 算法 关系型数据库
深度思考:雪花算法snowflake分布式id生成原理详解
雪花算法snowflake是一种优秀的分布式ID生成方案,其优点突出:它能生成全局唯一且递增的ID,确保了数据的一致性和准确性;同时,该算法灵活性强,可自定义各部分bit位,满足不同业务场景的需求;此外,雪花算法生成ID的速度快,效率高,能有效应对高并发场景,是分布式系统中不可或缺的组件。
深度思考:雪花算法snowflake分布式id生成原理详解
|
1月前
|
算法 数据库 索引
什么是雪花算法?啥原理?
什么是雪花算法?啥原理?
34 0
什么是雪花算法?啥原理?
|
1月前
|
算法 Java 数据中心
分布式ID生成系统之雪花算法详解
在当今的云计算和微服务架构盛行的时代,分布式系统已成为软件开发的重要组成部分。随着系统规模的扩大和业务的复杂化,对数据一致性和唯一性的要求也越来越高,尤其是在全局唯一标识符(ID)的生成上。因此,分布式ID生成系统应运而生,成为保证数据唯一性和提高系统可扩展性的关键技术之一。雪花算法(Snowflake)是Twitter开源的一种算法,用于生成64位的全局唯一ID,非常适用于分布式系统中生成唯一标识符。下面我们将深入探讨雪花算法的原理、结构和实现方式。
98 2
 分布式ID生成系统之雪花算法详解
|
2月前
|
算法
雪花算法id生成util
雪花算法id生成util
17 0
|
3月前
|
算法 Java 数据库连接
实现 MyBatis-Plus 中的配置加密功能(使用 AES 算法)
实现 MyBatis-Plus 中的配置加密功能(使用 AES 算法)
162 0
|
3月前
|
存储 算法 NoSQL
细说雪花算法
细说雪花算法
|
4月前
|
算法
雪花算法id生成器
雪花算法id生成器
63 0
|
4月前
|
算法 Scala 数据库
MyBatisPlus-ASSIGN_ID、ASSIGN_UUID策略、雪花算法及简化配置
MyBatisPlus-ASSIGN_ID、ASSIGN_UUID策略、雪花算法及简化配置
208 0