項目説明
最近のプロジェクトは、このような要望を持っていた:(注文番号の概念に類似)のユニークな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つのスレッドの同時実行テストの結果:
- QRコードの場合:INCR結果が返されます10000です。
- 得られた結果は以下のとおりです。
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つのスレッドの同時実行テストの結果:
- QRコードの場合:INCR結果が返されます10003です。
- 得られた結果は以下のとおりです。
すべてが正常です。
参照
https://redisbook.readthedocs.io/en/latest/feature/scripting.html
http://doc.redisfans.com/script/eval.html