概述
分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。
有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。
而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。
结构
snowflake的结构如下(每部分用-分开):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)
一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)
snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。
源码
public class SnowflakeIdWorker {
// ==============================Fields===========================================
private final long twepoch = 1420041600000L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
//==============================Constructors=====================================
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
// ==============================Methods==========================================
public synchronized long nextId() {
long timestamp = timeGen();
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
//如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
//==============================Test=============================================
public static void main(String[] args) {
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
for (int i = 0; i < 1000; i++) {
long id = idWorker.nextId();
System.out.println(Long.toBinaryString(id));
System.out.println(id);
}
}
}
以上资料转载自该博客,点我!
Mybatis-Plus使用雪花id
注意:以下配置是在SpringBoot2.1.4版本以及在mybatis已经可以使用的基础上做的升级配置!
1.引入Mybatis-Plus依赖(3.1.1版本目前有些问题,建议使用3.1.0版本)
com.baomidou mybatis-plus-boot-starter3.1.0
2.在application.yml配置文件中增加如下配置项
mybatis-plus: #mapper-locations: classpath:mybatis*Mapper.xml # 在classpath前添加星号可以使项目热加载成功 mapper-locations: classpath*:mybatis*Mapper.xml #实体扫描,多个package用逗号或者分号分隔 typeAliasesPackage: com.nis.project global-config: #主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID"; id-type: 3 #机器 ID 部分(影响雪花ID) workerId: 1 #数据标识 ID 部分(影响雪花ID)(workerId 和 datacenterId 一起配置才能重新初始化 Sequence) datacenterId: 18 #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断" field-strategy: 2 #驼峰下划线转换 db-column-underline: true #刷新mapper 调试神器 refresh-mapper: true #数据库大写下划线转换 #capital-mode: true #序列接口实现类配置 #key-generator: com.baomidou.springboot.xxx #逻辑删除配置(下面3个配置) logic-delete-value: 0 logic-not-delete-value: 1 #自定义SQL注入器 #sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector #自定义填充策略接口实现 #meta-object-handler: com.baomidou.springboot.xxx configuration: map-underscore-to-camel-case: true cache-enabled: false # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.原有的mapper接口增加继承baseMapper接口
public interface UserMapper extends baseMapper
4.实体类增加注解
在User实体类上添加@TableId的注解用来标识实体类的主键,以便插件在生成主键雪花Id的时候找到哪个是主键。
@TableId @JsonFormat(shape = JsonFormat.Shape.STRING) private Long userId;
5.分页配置
5.1 添加mybatis的一个配置类(不添加,则分页数据中的page参数会异常)
package com.nis.framework.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
@MapperScan({"com.nis.project.*.mapper","com.nis.project.*.*.mapper"})
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
5.2 java代码示例
Controller类中添加page分页对象。
@GetMapping("/list")
@ResponseBody
public Msg list(User user, HttpServletRequest request) {
Map params = MapDataUtil.convertDataMap(request);
user.getParams().putAll(params);
Page page = startMybatisPlusPage(user);
IPage userIPage = userService.selectUserList(page, user);
return Msg.success(userIPage);
}
ServiceImpl中的具体方法
@Override @DataScope(tableAlias = "b") public IPageselectUserList(Page page, User user) { return UserMapper.selectUserList(page, user); }
mapper接口中的代码
IPageselectUserList(@Param("pg") Page page, @Param("ps") User user);
mybaits的xml文件中写的sql代码
生成雪花ID以及解析雪花ID的机器ID和数据中心ID
1.生成雪花ID
这里不再阐述,直接贴上生成的一个雪花ID;1146667501642584065
2.解析雪花ID的机器ID和数据中心ID
SELECT (1146667501642584065>>12)&0x1f as workerId,(1146667501642584065>>17)&0x1f as datacenterId;
结果图:
到此这篇关于Mybatis-Plus雪花id的使用以及解析机器ID和数据标识ID实现的文章就介绍到这了,更多相关Mybatis-Plus雪花id内容请搜索考高分网以前的文章或继续浏览下面的相关文章希望大家以后多多支持考高分网!



