雪花算法实现原理和精度失效问题

一、雪花算法的实现原理

        雪花算法是一个全局唯一算法,它主要出现在像分库分表场景中作为业务主键、 或者作为一些像订单号这类的 id 生成器。 所以单纯就全局唯一性质来说,有很多的实现方式,比如  UUID , Redis 的原子递增 ,数据库全局表的自增 id ,等等。 但是在实际应用中,还需要满足有序递增、高性能、带时间戳等。

        雪花算法作为一种生成分布式全局唯一 ID 的算法,由一个 64 位的 long 类型数字组成,分为四个部分。
①、第一部分,用 1 个 bit 表示符号位,一般情况下是 0(因为第一个 bit 位是符号位,因为 id 不会是负数,所以它一般是 0)
②、第二部分,接着用 41 个 bit 位来表示毫秒单位的时间戳,使用系统时间的毫秒数
③、第三部分,用 10 个 bit 来记录工作机器 id,这样就可以保证在多个服务器上生成的 id 的唯一性。
如果存在跨机房部署,我们还可以把它分成两个 5bit,前面 5 个 bit 可以表示机房 id,后面 5 个 bit 可以表示机器 id。
④、第四个部分,用 12 个 bit 表示序列号,表示一个递增序列,用来记录同毫秒内产生的不同 id
把这 64 个 bit 位拼接成一个 long 类型的数字,就是雪花算法的实现。
        

二、使用过程中精度丢失的问题 

        MyBatis-Plus在实现增删改查时,会默认将id作为主键列,并在插入数据时,默认基于雪花算法的策略生成id 。但是在使用过程中却不注意就会遇到精度丢失的问题。

CREATE TABLE user
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
        #其他字段省略
);
@Data
public class User {
    private Long id;
//其他成员变量省略

        使用Long 类型对应数据库ID数据。雪花算法生成的就是一串数字,Long类型属于标准答案!在后端下断点。看到数据响应以JSON响应给前端,

{
id:1297873308628307970,
//其他属性省略
}

最后,这条数据返回给前端,前端接收到之后,修改这条数据,后端再次接收回来。

后端重新接收回来的id变成了:12978733086283000000,不再是1297873308628307970

解决办法:

①:id字段由Long类型改成String类型。(不推荐,String做ID查询性能会下降

②:  前端用String类型雪花ID保持精度,后端数据库继续使用Long(BigINT)类型  (可以 !)

后端的ID(Long) ==> Jackson(Long转String) ==> 前端使用String类型的ID,前端使用js string精度就不会丢失了
@Configuration
public class JacksonConfig {

  @Bean
  @Primary
  @ConditionalOnMissingBean(ObjectMapper.class)
  public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
  {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();

    // 全局配置序列化返回 JSON 处理
    SimpleModule simpleModule = new SimpleModule();
    //JSON Long ==> String
    simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
    objectMapper.registerModule(simpleModule);
    return objectMapper;
  }
}
③:放弃使用雪花算法,改用MySQL自带的生成的唯一自增id。(也行,自己选择吧)
        当使用@TableId(value = "id",type = IdType.AUTO)语句时,代表着使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,否则无效。
        当实体类类型的类名和要操作的表的表名不一致时,就会报错,而注解@TableName就可以帮助我们解决这个问题。我的数据库表名是t_user,实体类名是User,只需要在类名上写入@TableName("t_user")就可以了
//设置实体类对应的表名
@TableName("t_user")
public class User {
    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

当使用@TableId(value = "id")语句时,若实体类和表中表示主键的不是id,而是其他字段,例如代码中的uid,MyBatis-Plus会自动识别uid为主键列,否则就会报这样的错误:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值