1.现有方式:通过redis incry函数自增 redis控制并发

2.需求:订单编号为30位左右的数字编号
不再采用自增方式(自增方式容易被利用)
订单编号具有基本的可读性(如年月日的可读性)
企业标识(2位数字)+ 业务标识(2位数字)+ 自定义(n位数字)
无论什么情况都不能重复(高并发等)

3.备选方案1
来源:由Snowflake改写

企业标识(2位)+业务标识(2位)+ 年月日(6位)+ 时分秒毫秒(unix时间戳 8位)+ 自增id(2位(随不同毫秒刷新)) = 20位
备注:一秒最大生成订单量9.9万,一毫秒内如果自增id达到峰值,则等待下一毫秒到来

4.备选方案2(理论)
来源:博主臆想(灵感源于约瑟夫环)

企业标识(2位)+业务标识(2位)+ 年月日时分秒(10)+ 环终结个数和随机数(6位) = 20位
备注:理论一秒最大生成订单量≈70-80万(由于弃环有最坏情况,弃环条件决定了环的利用率(一般环的利用率低于70%是小概率事件))环的效率有待考虑

随机数环思想:定义一个数组(使用位运算)环放入redis中 每次取的随机数要先去数组对应下标位中判断下标位的数是否为0,若为0则直接返回 并把这个位置置1,如果是1,则寻找1前面的那个位,如果找了一定的数量N都是1那么将该随机数返回,环终结个数+1,数组元素全部置0,将新数组该随机下标位置1。(可进一步引用布隆过滤器)

基本代码如下(java)

import java.util.Random;

/**
 * Created by Kowalski on 2017/1/22.
 * 
 */
public class MyIdWorker {


    public static void main(String... args) {
        for(int i=0;i<1000000;i++) {
            System.out.println(System.currentTimeMillis());
        }
    }

    public String generateOrderId(int arrLength){
        int[] arr = new int[arrLength];
        Random random = new Random();
        int bitNum = arrLength*4*8;
        int[] arr_32 = {
                0x80000000,0x40000000,0x20000000,0x10000000,
                0x08000000,0x04000000,0x02000000,0x01000000,
                0x00800000,0x00400000,0x00200000,0x00100000,
                0x00080000,0x00040000,0x00020000,0x00010000,
                0x00008000,0x00004000,0x00002000,0x00001000,
                0x00000800,0x00000400,0x00000200,0x00000100,
                0x00000080,0x00000040,0x00000020,0x00000010,
                0x00000008,0x00000004,0x00000002,0x00000001,
        };
        for(int i=0;i<100;i++){
            int ran = random.nextInt(bitNum);
            int temp = ran;
            while (ran < temp + (bitNum << 1)) {
                if ((arr[ran / 32] & arr_32[ran % 32]) == 0) {
                    arr[ran / 32] = arr[ran / 32] | arr_32[ran % 32];
                    break;
                } else {
                    if(ran >= bitNum-1){
                        temp = bitNum << 1 - (ran - temp);
                        ran = 0;
                    }else {
                        ran++;
                    }
                }
            }
            System.out.println(ran);
        }
        return null;
    }

}

环的碰撞率与使用率研究代码:

import java.util.Random;

/**
 * Created by Kowalski on 2017/1/25.
 * 环
 */
public class Main {

    public static void main(String... args){
        /**随机数范围*/
        int n = 10000;
        Random ran = new Random();
        int[] arr = new int[n];
        for(int m=0;m<n;m++){
            while (true) {
                int r = ran.nextInt(n);
                if (arr[r] == 0) {
                    arr[r] = 1;
                } else {
                    boolean flag = false;
                    for (int i = 0; i < m; i++) {
                        r--;
                        if (r < 0) {
                            r = n-1;
                        }
                        if (arr[r] == 0) {
                            arr[r] = 1;
                            flag = true;
                            break;
                        }
                    }
                    if(flag){
                        continue;
                    }
                    break;
                }
            }
            int res_1 = 0;
            for(int i=0;i<n;i++){
                if(arr[i] == 1){
                    res_1++;
                    arr[i] = 0;
                }
            }
            System.out.print(m+"\t");
            System.out.println(res_1);
        }
    }
}

优缺:
优:通过位数组保证编号唯一性,效率待验证
后几位数字采用随机数,不连续,难以破解
缺:碰撞率及数组利用率取决于N的大小,N越大碰撞率越高,循环次数也越高,但数组利用率高,相反N越小,碰撞率越低,循环次数越小,数组利用率越低。

有小伙伴有更好的方法 欢迎一块研究呀~