long 雪花算法_雪花算法(snowflake)

简单描述

最高位是符号位,始终为0,不可用。

41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 后得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序SnowFlake类的START_STMP属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

10位的机器标识,10位的长度最多支持部署1024个节点。

12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

加起来刚好64位,为一个Long型。这个算法很简洁,但依旧是一个很好的ID生成策略。其中,10位器标识符一般是5位IDC+5位machine编号,唯一确定一台机器。

算法实现

public classSnowFlake {//起始的时间戳

private final static long START_STMP = 1577808000000L; //2020-01-01//每一部分占用的位数,就三个

private final static long SEQUENCE_BIT = 12; //序列号占用的位数

private final static long MACHINE_BIT = 5; //机器标识占用的位数

private final static long DATACENTER_BIT = 5; //数据中心占用的位数//每一部分最大值

private final static long MAX_DATACENTER_NUM = -1L ^ (-1L <

private final static long MACHINE_LEFT =SEQUENCE_BIT;private final static long DATACENTER_LEFT = SEQUENCE_BIT +MACHINE_BIT;private final static long TIMESTMP_LEFT = DATACENTER_LEFT +DATACENTER_BIT;private long datacenterId; //数据中心

private long machineId; //机器标识

private long sequence = 0L; //序列号

private long lastStmp = -1L; //上一次时间戳

public SnowFlake(long datacenterId, longmachineId) {if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");

}if (machineId > MAX_MACHINE_NUM || machineId < 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");

}this.datacenterId =datacenterId;this.machineId =machineId;

}//产生下一个ID

public synchronized longnextId() {long currStmp =timeGen();if (currStmp

}if (currStmp ==lastStmp) {//if条件里表示当前调用和上一次调用落在了相同毫秒内,只能通过第三部分,序列号自增来判断为唯一,所以+1.

sequence = (sequence + 1) &MAX_SEQUENCE;//同一毫秒的序列数已经达到最大,只能等待下一个毫秒

if (sequence == 0L) {

currStmp=getNextMill();

}

}else{//不同毫秒内,序列号置为0//执行到这个分支的前提是currTimestamp > lastTimestamp,说明本次调用跟上次调用对比,已经不再同一个毫秒内了,这个时候序号可以重新回置0了。

sequence = 0L;

}

lastStmp=currStmp;//就是用相对毫秒数、机器ID和自增序号拼接

return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分

| datacenterId << DATACENTER_LEFT //数据中心部分

| machineId << MACHINE_LEFT //机器标识部分

| sequence; //序列号部分

}private longgetNextMill() {long mill =timeGen();while (mill <=lastStmp) {

mill=timeGen();

}returnmill;

}private longtimeGen() {returnSystem.currentTimeMillis();

}

}

当增加一秒生成ID的时候就是增加10位的机器标识+12位序列+约2的10次方(1000毫秒),最终就是增加一个2的32次方4 294 967 296就是42亿左右

但是这里有一个坑,雪花算法产生的长整数的精度可能超过javascript能表达的精度,这会导致js获取的id与雪花算法算出来的id不一致,如雪花算法得到的是36594866121080832,但是因为javascript丢失精度后只获取到36594866121080830, 这会导致对数据的所有操作都失效。

解决办法:后端的语言获取到雪花算法的id后将其转换为String类型,这样js也会当做字符串来处理,就不会丢失精度了。

配置方法

@Configurationpublic class WebMvcConfig implementsWebMvcConfigurer {

@Autowiredpublic void configureMessageConverters(List>converters) {

converters.add(toStringConverter());

}/*** BigDecimal Long 转化为String

*

*@return

*/@BeanpublicMappingJackson2HttpMessageConverter toStringConverter() {

MappingJackson2HttpMessageConverter converter= newMappingJackson2HttpMessageConverter();

ObjectMapper mapper= newObjectMapper();

SimpleModule simpleModule= newSimpleModule();

simpleModule.addSerializer(BigDecimal.class, BigDecimalToStringSerializer.instance);

simpleModule.addSerializer(Long.class, ToStringSerializer.instance);

simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);

simpleModule.addSerializer(long.class, ToStringSerializer.instance);

mapper.registerModule(simpleModule);

// Include.Include.ALWAYS 默认

// Include.NON_DEFAULT 属性为默认值不序列化

// Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量

// Include.NON_NULL 属性为NULL 不序列化

mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);// 允许出现特殊字符和转义符

mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); // 允许出现单引号

converter.setObjectMapper(mapper);

returnconverter;

}

@JacksonStdImplstatic class BigDecimalToStringSerializer extendsToStringSerializer {public final static BigDecimalToStringSerializer instance = newBigDecimalToStringSerializer();publicBigDecimalToStringSerializer() {super(Object.class);

}public BigDecimalToStringSerializer(Class>handledType) {super(handledType);

}

@Overridepublic booleanisEmpty(SerializerProvider prov, Object value) {if (value == null) {return true;

}

String str=((BigDecimal) value).stripTrailingZeros().toPlainString();returnstr.isEmpty();

}

@Overridepublic voidserialize(Object value, JsonGenerator gen, SerializerProvider provider)throwsIOException {

gen.writeString(((BigDecimal) value).stripTrailingZeros().toPlainString());

}

@Overridepublic JsonNode getSchema(SerializerProvider provider, Type typeHint) throwsJsonMappingException {return createSchemaNode("string", true);

}

@Overridepublic voidserializeWithType(Object value, JsonGenerator gen,

SerializerProvider provider, TypeSerializer typeSer)throwsIOException {//no type info, just regular serialization

serialize(value, gen, provider);

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值