販売期間または製品数量が限られているフラッシュ販売シナリオでは、販売期間の前、期間中、および期間後に発生するトラフィックのピークを処理する必要があります。 また、受け入れられた発注書の数が在庫のある商品の数を超えないようにする必要があります。 これらの課題に対処するために、TairのTairStringデータ構造は、有界カウンターを実装するためのシンプルで効率的な方法を提供します。 制限付きカウンターを使用して、承認された購入注文が上限を超えないようにすることができます。 このトピックで説明するソリューションは、レート制限またはスロットリングが必要な他のシナリオにも適用できます。
フラッシュ販売用の境界付きカウンター
exStringデータ構造は、ネイティブのRedis Stringデータ構造よりも強力です。 TairStringは、ビット操作を除く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を使用すると、単純なコードをコンパイルして実行し、発注書の正確な数を制限できます。 サンプル疑似コード:
(EXINCRBY(key_iphone、-1、MIN:0) == "would overflow") の場合は
if(EXINCRBY(key_iphone, -1, MIN:0) == "would overflow")
run_out();
スロットル用の有界カウンター
フラッシュセールスの制限付きカウンターと同様に、EXINCRBYコマンドのMAXオプションを指定して、制限付きカウンターをスロットリングに実装できます。 サンプル疑似コード:
(EXINCRBY(rate_limitor, 1, MAX:1000) == "would overflow") の場合は
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;
}