目录
1、案例场景
2、雪花(snowflake)Id,Long id前端精度损失原因
3、解决JavaScript Number长整型精度丢失问题
4、成果展现
5、总结
6、参考文章
1、案例场景
我们项目之中可能大多数都使用自增长id作为主键id,这样对于中小型系统来说一般没有啥问题。但是随着各种业务发展以及后续可能分库分表情况;我们有些项目或者项目之中的独立微服务可能使用的是雪花算法生成的主键id(19位)长整型的。于是最近我们在项目对接其他微服务(使用的雪花主键id)之后,在前端调用接口之后发现返回结果id被裁减了。经过组内同事定位📌Long型精度损失。具体原因下面原理部分分析。
2、雪花(snowflake)Id,Long id前端精度损失原因
本人试验以及结合网上的文章,发现一个问题,在PostMan里面请求接口能够完全正确的返回雪花算法生成的主键id,但是在Swagger或者前端接口请求 浏览器之中就会出现雪花算法精度损失问题。于是查找原因如下:
JavaScript 不支持后台返回的 Long 类型,JavaScript 的 number 类型的数值范围是 -2^53~2^53(不包含边界) 所以大于 9007199254740991 的数,进制转换会存在精度问题,而雪花ID生成的数值过大,导致 JavaScript 不能正常存储导致。
同时注意:建议雪花ID在数据库中使用 bigint 来存储,而不是使用 varchar;这样可以提高这样数据库的速度,使用索引的时候少一步字符串转换成数字的操作。
于是我的实际项目之中返回的雪花算法的主键id
JavaScript的Number最大id | 9007199254740991 |
项目主键id | 1511972392982179840 |
浏览器或者swagger返回 | 1511972392982179800 |
于是就出现了 前端传递的id,找寻不到对应的记录问题。
3、解决JavaScript Number长整型精度丢失问题
解决方案1
使用Jackson注解,我们也可以用@JsonFormat做类型转换
在VO类中,id上添加注释
@ApiModelProperty(name = "id", value = "消息id")
@JsonProperty("id")
//此句为问题关键 相当于吧Long转换为String
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
//此处标识得在序列化的时候转换为字符串
private Long id;
其他相关:
@JSONField(serializeUsing= ToStringSerializer.class)
这个注解是Fastjson的,旨在让系统系列化时,保留相关精度。
解决方案2
写一个 JSON 配置类,将 Long 类型序列化成 JSON 的时候自动转为 String 类型
@Configuration
public class JacksonConfiguration {
@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
// Long 会自定转换成 String
builder.serializerByType(Long.class, ToStringSerializer.instance);
};
}
}
4、成果展现
5、总结
在解决的问题的过程之中一定要细致分析两者的之间不同的细小差异,这样才能发现问题得蛛丝马迹。比如本问题之中发现最后Id最后两位的id字段丢失。这样才能作为解决问题得突破口。
6、参考文章
后端传Long类型至前端js会出现精度丢失问题
优雅解决后台返回 Long 类型,前台精度丢失导致数据不一致的问题
后端Issue-01-Swagger中Long类型精度丢失问题