Redisのを使用すると、独自の順序番号を生成し、ランダムに

項目説明

最近のプロジェクトは、このような要望を持っていた:(注文番号の概念に類似)のユニークな11ビットのコードの食事、定期的に食べるコードを生成する必要がある:11個の数字の合計は、そのような6の前のように日付2019です7月20日は、そうでない場合は、一日の量は、人々が見るために、5個の乱数が続き、190720で、自動インクリメントすることはできません。

ソリューション

5桁のランダムな番号をランダムに生成することができない、または一意ではないかもしれないので、事前に生成されたソリューションの思想:
Redisのを使用して

  • 乱数ジェネレータ

各番号キーメモリ1000のキーへとRedisのリストデータ構造90を乱す氏それぞれ〜90,000万〜99,999総数(10,000から最初の先行ゼロにわざわざ)、、。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()]));
        }

10 / QRコード::11 / QRコード:12 ... / QRコード:99各値は0〜999のインデックスキーになるように、キーはQRコードです。

  • カウントキー

次いで食事コードあたりのカウントがインクリメントされる生成するためにキーを使用し、値は、キー三のキーインデックスの代表を採取最初の2つのカウントを示すために使用され、10000から始まります。例えば、今以上の服用によって生成された12151カウントすると呼ばQRコードを拡大して:12ですので、一日90,000乱数を確保する際1000099999からのカウントは、使用することができたときにカウントを再開するために[キー値151のインデックス、およびとなりません。同じ乱数。これには、6〜7とに展開され、同じトークン百万一日が続く日、事業の90,000単一の注文を、解決することがあります。

初期化:

jedisCluster.set(qrcode:incr,9999);

      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;
    }

同時リスク

最大値までカウントが、それはカウントキーリセットする必要がある場合には(QRコードを:INCR)10000問題は、スレッドセーフではありません。
:我々は、最初のテストユニット同時方式調製
のみ乱数10000、maxincr = 19999を生成するためのテスト環境のため、
19997の後に配置された並行試験をmaxincrに近接して配置された我々の最初のキー数は、2つのQRコードを取得します10000にリセットされます。

jedisCluster.set(qrcode:incr,19997);

5スレッドの同時実行テストを開きます。

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つのスレッドの同時実行テストの結果:

  1. QRコードの場合:INCR結果が返されます10000です。
  2. 得られた結果は以下のとおりです。
    画像

5つのスレッドへの同時リードは最初に実行するので、

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

20キーバックは、唯一のQRコード:. 19を生成するためのテスト環境から、それはnullを返すように。最終的な値は19998/19999/20000/20001/20002 3つのカウントINCRました

解きます

それを分析することmaxincrに達し、10000アトミック操作にリセットする必要があります。そこでここでは、LUAスクリプトの実行モードを使用しています。

LUAスクリプトの使用Redisの
バージョン:2.6.0は、などから入手可能です。
時間計算は:スクリプトの実行に依存します。

Luaのスクリプトの利点を使用します:

  • ネットワークのオーバーヘッドを削減します。複数の要求は、ネットワークの待ち時間を短縮、スクリプトの形式で送信することができます。
    アトミック操作。全体を実行するようにRedisの全体のスクリプトは、他のコマンドの途中に挿入されないであろう。そのため、競合状態を気にせずに、スクリプトを書くプロセスでトランザクションを使用せずに、発生します。
  • 多重化。他のクライアントが同じロジックを実行するスクリプトコードを使用せずに多重化することができるように、クライアントから送信されたスクリプトは、Redisの中に恒久的に存在することになります。
  • 全体を実行するようにRedisの全体のスクリプトは、他のコマンドの途中に挿入されないであろう。そのため、競合状態を気にせずに、スクリプトを書くプロセスでトランザクションを使用せずに、発生します。

だから、QRコード変換を取得します:

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つのスレッドの同時実行テストの結果:

  1. QRコードの場合:INCR結果が返されます10003です。
  2. 得られた結果は以下のとおりです。
    画像

すべてが正常です。

参照

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

私のブログのアドレス

私のブログのアドレス

おすすめ

転載: www.cnblogs.com/yalunwang/p/11592194.html