美文网首页
雪花算法之【线上订单号重复了?一招搞定它!】

雪花算法之【线上订单号重复了?一招搞定它!】

作者: 小郭子 | 来源:发表于2022-02-23 10:12 被阅读0次

来源:公众号 作者:方志朋
链接:https://mp.weixin.qq.com/s/QnPKwrFPVYBaW_N0nwRs9g

问题的背景

公司老的系统原先采用的时间戳生成订单号,导致了如下情形


订单号重复

打断一下:大家知道怎么查系统某项重复的数据吧

SELECT * FROM XX表 WHERE 重复项 in( SELECT 重复项 FROM XX表 GROUP BY 重复项 HAVING count(1) >= 2)

解决方法

不得了,这样重复岂不是一单成功三方回调导致另一单也成功了。

多个服务怎么保证生成的订单号唯一呢?

先上code

package com.zhongjian.util;

public class IdWorkerUtil{

    private long workerId;
    private long datacenterId;
    private long sequence;

    public IdWorkerUtil(long workerId, long datacenterId, long sequence){
        // sanity check for workerId
        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));
        }
        System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
                timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);

        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    private long twepoch = 1288834974657L;

    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private long sequenceBits = 12L;

    private long workerIdShift = sequenceBits;
    private long datacenterIdShift = sequenceBits + workerIdBits;
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;

    public long getWorkerId(){
        return workerId;
    }

    public long getDatacenterId(){
        return datacenterId;
    }

    public long getTimestamp(){
        return System.currentTimeMillis();
    }

    public synchronized long nextId() {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            System.err.printf("clock is moving backwards.  Rejecting requests until %d.", 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 = 0;
        }

        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen(){
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        IdWorkerUtil idWorkerUtil = new IdWorkerUtil(1,1,0L);
        System.out.println(idWorkerUtil.nextId());
    }
}
雪花算法说明

以上是采用snowflake算法生成分布式唯一ID

41-bit的时间可以表示(1L<<41)/(1000L360024*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。

这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。

这种方式的优缺点是:

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分配bit位,非常灵活。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

一般来说,采用这种方案就解决了。

还有诸如,mysql的 auto_increment策略,redis的INCR,zookeeper的单一节点修改版本号递增,以及zookeeper的持久顺序节点。

相关文章

  • 雪花算法之【线上订单号重复了?一招搞定它!】

    来源:公众号 作者:方志朋链接:https://mp.weixin.qq.com/s/QnPKwrFPVYBaW...

  • Tp6 类库&雪花算法

    Tp6 类库&雪花算法 在产生订单时 订单号的生成使用雪花算法 这里通过在tp6这个场景下 通过类库的方式 来使用...

  • 雪花算法(snowflake)生成Id重复问题

    前言 最近工作上遇到一个雪花算法生成Id重复导致数据库中表主键冲突,导致入库失败的问题,所以顺便学习了一下雪花算法...

  • ID生成器

    一、交易订单号生成(雪花算法) 基本思路,int64 二进制64位。按位来划分业务字段。 从高位到地位: 几位含义...

  • 微信支付

    问题:微信支付错误:201 商户订单号重复原因:修改了支付价格解决办法:待支付的订单号(即商户订单号,out_tr...

  • 系统其实是一种算法

    人生算法,就是你面对世界不断重复的最基本的套路。 找到它,重复它,强化它。 如果有个人,已经找到了这个算法, 并且...

  • 常见排序算法之冒泡排序

    常见排序算法之冒泡排序 冒泡排序(Bubble Sort),是一种较简单的排序算法。它重复地走访过要排序的元素列,...

  • java基础17-存储的一些事

    1.订单创建和更新 创建订单预先生成订单号,前端提交的时候带上订单号提交。订单号在数据库是唯一索引,保证前端重复提...

  • 20180608人生算法-啥事都有套路

    罗振宇说:人生算法就是你面对世界不断重复的最基本的套路,找到它、重复它、强化它。我在想有什么人生算法我可以使用,昨...

  • WarMj:快速排序算法(Quick Soft)

    参考资料:白话经典算法系列之六 快速排序 快速搞定 代码分析

网友评论

      本文标题:雪花算法之【线上订单号重复了?一招搞定它!】

      本文链接:https://www.haomeiwen.com/subject/dsiolrtx.html