フラッシュ販売シナリオまたは数量限定オファーシナリオでは、販売期間の前、期間中、および期間後に発生するトラフィックのピークを処理する必要があります。 また、受け入れられた発注書の数が在庫のある商品の数を超えないようにする必要があります。 これらの課題に対処するために、Tair (Enterprise Edition) のTairStringデータ構造は、制限付きカウンターを実装するためのシンプルで効率的な方法を提供します。 このトピックで説明するソリューションは、レート制限またはスロットリングが必要な他のシナリオにも適用できます。
フラッシュ販売用の境界付きカウンター
exStringはTairの新しいデータ構造です。 ネイティブのRedis Stringデータ構造よりも強力で、ビット操作を除くRedis Stringのすべての機能を提供します。
TairStringsのEXINCRBYおよびEXINCRBYFLOATコマンドは、ネイティブRedis文字列のINCRBYおよびINCRBYFLOATコマンドと同様の機能を備えています。 これらのコマンドを使用して、値をインクリメントまたはデクリメントできます。 EXINCRBYおよびEXINCRBYFLOATコマンドは、ネイティブRedis文字列の2つのコマンドよりも多くのオプションをサポートします。 これらのオプションは、EX、NX、VER、MIN、およびMAXを含む。 詳細については、「exString」をご参照ください。 このトピックで説明するソリューションでは、MINおよびMAXオプションを使用します。 次の表に、2つのオプションを示します。
パラメーター | 説明 |
MIN | TairStringの最小値を指定します。 |
MAX | TairStringの最大値を指定します。 |
ネイティブのRedis文字列を使用してフラッシュセールスの課題を処理する場合、必要なコードは複雑で管理が困難です。 これは、アイテムがすでに売り切れた後であっても、ユーザがアイテムの購入を成功させることができる過剰な購入注文につながる可能性がある。 TairStringを使用すると、単純なコードをコンパイルして実行し、発注書の正確な数を制限できます。 サンプル疑似コード:
if(EXINCRBY(key_iphone, -1, MIN:0) == "would overflow")
run_out();
スロットル用の有界カウンター
bounded counters for flash salesと同様に、EXINCRBYコマンドのMAXオプションを指定して、スロットリング用のbounded countersを実装できます。 サンプル疑似コード:
if(EXINCRBY(rate_limitor, 1, MAX:1000) == "would overflow")
traffic_control();
スロットリング用の制限付きカウンタは、同時要求数、アクセス頻度、パスワード変更数の制限など、さまざまな目的で使用できます。 たとえば、同時実行制限シナリオでは、同時リクエストの数が突然システムパフォーマンスのしきい値を超えます。 重大な結果を引き起こすサービス障害を防ぐために、一時的な解決策として有界カウンターを使用して、同時要求の数を制御できます。 このソリューションは、同時リクエストにタイムリーに応答できます。 1秒あたりのクエリ数 (QPS) を制限する場合は、TairStringsのEXINCRBYコマンドを使用して単純なコードをコンパイルして実行し、同時リクエストの有界カウンターを設定できます。
/**
* tryAcquire is thread-safe and will increment the key from 0 to the upper bound within an interval of time,
* and return failure once it exceeds
* @param key the key
* @param upperBound the max value
* @param interval the time interval
* @return acquire success: true; fail: false
*/
public static boolean tryAcquire(String key, int upperBound, int interval) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.eval("if redis.call('exists', KEYS[1]) == 1 "
+ "then return redis.call('EXINCRBY', KEYS[1], '1', 'MAX', ARGV[1], 'KEEPTTL') "
+ "else return redis.call('EXSET', KEYS[1], 0, 'EX', ARGV[2]) end",
Arrays.asList(key), Arrays.asList(String.valueOf(upperBound), String.valueOf(interval)));
return true;
} catch (Exception e) {
if (e.getMessage().contains("increment or decrement would overflow")) {
return false;
}
e.printStackTrace();
}
return false;
}