Using redis generate a unique order number and randomly

project description

Recently did a project have such a demand: the need to generate a unique 11-bit code meals (similar to the concept of order number), regular eating code is: a total of 11 numbers, such as in front of 6 is the date 2019 July 20 is 190,720, followed by five random numbers and can not be auto-incremented, otherwise the amount of a single day to see people get.

solution

Five-digit random number can not be randomly generated, or may not be unique, so the thought of pre-generated solutions:
using redis

  • Random Number Generator

Mr ~ 10,000 to 99,999 90,000 total number (from 10,000 initially bother to the leading zero), respectively, into and disrupting the redis list data structure 90 the key of each number key memory 1000. Take the time to read through LINDEX.

        List<String> numList=new ArrayList<>();
        //90万个数 每个redis key 1000个数,要存90个key.
        for (int i=10000;i<=99999;i++){
            numList.add(String.valueOf(i));

        }
        //打乱顺序
        Collections.shuffle(numList);
        //生成key
        for (int j=10;j<=99;j++){
            String redisKey="qrcode:"+j;
            List<String> newList= test.subList((j-10)*1000,(j-10)*1000 + 1000);
           jedisCluster.rpush(redisKey,newList.toArray(new String[newList.size()]));
        }

So that each value is the index key of 0 ~ 999, key is qrcode: 10 / qrcode: 11 / qrcode: 12 ... / qrcode: 99.

  • Count key

Then use a key to generate a count per meal code is incremented, the value also starts from 10000, which is used to indicate the first two counts taken the key, the key index representative of three. For example, now referred to to count 12151 that is generated by taking the above qrcode: 12 is Key index in the value 151, and then to resume counting when the count from when 1000099999, thus ensuring day 90,000 random numbers can be used and does not take the same random number. This may solve a day, 90,000 single order of business, followed by the same token one million one day be expanded to 6 7 and so on.

Initialized:

jedisCluster.set(qrcode:incr,9999);

Examples

      public String getOneQrCode() {
        Long incr = jedisCluster.incr("qrcode:incr");
        //测试环境生成到19999
        int maxIncr=19999
        //int maxIncr = 99999;
        //后期单量过猛时需要考虑--并发风险导致的就餐码重复 todo
        if (incr == maxIncr) {
            jedisCluster.set("qrcode:incr", String.valueOf(10000));
           }
        System.out.println("incr:"+incr);
        //取前两位
        String key = incr.toString().substring(0, 2);
        //取后三位作为list里的index
        Integer index = NumberUtil.getIntValue(incr.toString().substring(2));
        //获得5位随机数
        String qrcode = jedisCluster.lIndex("qrcode:"+ key, index);
        return qrcode;
    }

Concurrent risk

When the count to the maximum value, it is necessary to reset the counting key (qrcode: incr) 10000 problem will not thread-safe.
We first prepared a test unit concurrent method:
Test environment since only generates random numbers 10000, maxincr = 19999, so
we first key count disposed proximate to maxincr concurrent test, arranged after 19997 will acquire two qrcode reset to 10000.

jedisCluster.set(qrcode:incr,19997);

Open 5 thread concurrency testing:

private static final int threadNum=5;
    //倒计数器,用于模拟高并发
    private CountDownLatch countDownLatch=new CountDownLatch(threadNum);
    @Test
    public void benchmark() {
        Thread[] threads=new Thread[threadNum];
        for (int i = 0; i <threadNum ; i++) {
            final int j=i;
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("qrcode"+getOneQrCode());
                }
            });
            threads[i]=thread;
            thread.start();

            countDownLatch.countDown();
        }
        for (Thread thread :threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

5 thread concurrency test results:

  1. For qrcode: incr results get returned is 10000.
  2. The results obtained are:
    image

Since Concurrent lead to 5 threads to execute first

 Long incr = jedisCluster.incr("qrcode:incr");

The final values ​​were incr 19998/19999/20000/20001/20002 three counts so that the back key 20, since the test environment to generate only qrcode:. 19, it returns null.

solve

Analyzing it reaches maxincr and should be reset to 10000 atomic operation. So here we are using lua script execution mode.

Redis using lua script
Version: 2.6.0 is available as from.
Time Complexity: Depends on the script execution.

Using Lua script benefits:

  • Reduce network overhead. The multiple requests can be sent in the form of a script, reduce network latency.
    Atomic operation. redis entire script as a whole will perform, will not be inserted into the middle of other commands. Therefore, in the process of writing the script without worrying about race conditions occur, without the use of a transaction.
  • Multiplexing. Scripts sent by the client will be permanently present in redis, so that other clients can be multiplexed without using the script code that perform the same logic.
  • redis entire script as a whole will perform, will not be inserted into the middle of other commands. Therefore, in the process of writing the script without worrying about race conditions occur, without the use of a transaction.

So to get qrcode transformation:

public String getOneQrcodeLua(){
        String lua="local key = KEYS[1]\n" +
            "local incr=redis.call('incr',key) \n"+
            "if incr == tonumber(ARGV[1]) \n" +
            "then\n" +
            "    redis.call('set',key,ARGV[2])\n" +
            "    return incr\n" +
            "else\n" +
            "    return incr\n" +
            "end";
        List<String> keys = new ArrayList<>();
        keys.add("qrcode:incr");
        List<String> argv = new ArrayList<>();
        argv.add("19999");
        argv.add("10000");
        Object o= jedisCluster.eval(lua,keys,argv);
       // System.out.println("incr"+o);
        //取前两位
        String key = o.toString().substring(0, 2);
        //取后三位作为list里的index
        Integer index = NumberUtil.getIntValue(o.toString().substring(2));
        //获得5位随机数
        String qrcode = jedisCluster.lIndex("qrcode:"+ key, index);
        return qrcode;
    }

5 thread concurrency test results:

  1. For qrcode: incr results get returned is 10003.
  2. The results obtained are:
    image

everything is normal.

reference

https://redisbook.readthedocs.io/en/latest/feature/scripting.html
http://doc.redisfans.com/script/eval.html

My blog address

My blog address

Guess you like

Origin www.cnblogs.com/yalunwang/p/11592194.html