redis+luaと秒殺--lua入門
この2,3日同僚たちは外国へ旅行に行きました.私のこのオタクは出かけるのが好きではありません.しかし、私がまだ時間通りに出勤しなければならないとは思わなかった.会社で暇で用事がなく、luaについての内容を理解して、簡単にメモを取った.
redisとは何か、言うまでもない.luaスクリプト言語、接着言語.redisは2.6バージョンからluaをサポートしています.redisサーバをインストールするとluaを実行できます.ではredisはなぜluaが必要なのでしょうか.簡単に言えば、パフォーマンスとトランザクションの原子性のためです.redisが提供してくれたトランザクションの機能が悪いからです.以下、電子商取引の秒殺シーンを簡単に説明します.
秒殺シーン
ユーザが秒殺し、バックエンドサービスが要求を受信した後の操作手順:検証要求パラメータ 解析ユーザ情報 解析所秒商品情報、及び秒商品数 ユーザが制限時間内に 秒を過ぎるかどうかを検証する.在庫が十分かどうかを確認する .インベントリ ユーザ秒単位記録 を記録する.非同期書き込み注文記録 は、要求 を返す.
に質問
上記の手順で在庫と在庫を検証し、前後順に存在するが、原子性はない.リレーショナル・データベースでは、トランザクションによってこの問題を解決できますが、リレーショナル・データベースのパフォーマンスにはボトルネックがあります.もちろん要求量を制御できる場合は,リレーショナル・データベースの楽観的なロックを用いてもよい.今、私が会社で使用しているように、秒殺単一インタフェースのストリーム制限+データベースの楽観的なロックを行うことで、この問題を完全に解決します.しかし,これはストリーム制限によって要求のピークを解決した.在庫を検証し、在庫を差し引く論理をredis上で実行すると、原子的な問題もあります.redisはトランザクション機能も提供してくれましたが、このトランザクション機能はあまりにも悪いです.そこで本稿では、redis+luaによってこの問題を解決することを望んでいる.
ソリューション
redisを使用してluaの原子性を実行し、上記の手順をredisで実行します.前提条件は、商品の秒殺と在庫データのすべてをredisにプッシュすることです.準備:必要秒殺商品及び在庫データをredisにプッシュする.
ビジネスコードロジック:検証要求パラメータ 解析ユーザ情報 解析所秒商品情報、及び所秒商品数 luaを使用して対応するスクリプトを作成し、redisに置いて実行し、0を返すと秒が過ぎ、2在庫が不足し、1控除在庫が成功したことを示す. 非同期書き込みオーダーレコード は、要求 を返す.
luaスクリプトに必要なビジネスロジック:ユーザが秒オーダー判定 を記憶するか否か検査在庫 インベントリ ユーザーID を設定は結果 を返す.
luaスクリプトの内容
もちろん、このluaをjavaコードに入れることができます.
redisとは何か、言うまでもない.luaスクリプト言語、接着言語.redisは2.6バージョンからluaをサポートしています.redisサーバをインストールするとluaを実行できます.ではredisはなぜluaが必要なのでしょうか.簡単に言えば、パフォーマンスとトランザクションの原子性のためです.redisが提供してくれたトランザクションの機能が悪いからです.以下、電子商取引の秒殺シーンを簡単に説明します.
秒殺シーン
ユーザが秒殺し、バックエンドサービスが要求を受信した後の操作手順:
に質問
上記の手順で在庫と在庫を検証し、前後順に存在するが、原子性はない.リレーショナル・データベースでは、トランザクションによってこの問題を解決できますが、リレーショナル・データベースのパフォーマンスにはボトルネックがあります.もちろん要求量を制御できる場合は,リレーショナル・データベースの楽観的なロックを用いてもよい.今、私が会社で使用しているように、秒殺単一インタフェースのストリーム制限+データベースの楽観的なロックを行うことで、この問題を完全に解決します.しかし,これはストリーム制限によって要求のピークを解決した.在庫を検証し、在庫を差し引く論理をredis上で実行すると、原子的な問題もあります.redisはトランザクション機能も提供してくれましたが、このトランザクション機能はあまりにも悪いです.そこで本稿では、redis+luaによってこの問題を解決することを望んでいる.
ソリューション
redisを使用してluaの原子性を実行し、上記の手順をredisで実行します.前提条件は、商品の秒殺と在庫データのすべてをredisにプッシュすることです.準備:
ビジネスコードロジック:
luaスクリプトに必要なビジネスロジック:
luaスクリプトの内容
-- ,
local hasSecKill=redis.call('sismember',ARGV[1],KEYS[1])
if hasSecKill ~=0 then
return 0;
end
--
--redis.call('set',KEYS[1],1);
--
--redis.call('expire',KEYS[1],30000);
--check
for goodsNum=2,#KEYS do
local goodsStock=redis.call('get',KEYS[goodsNum]);
if goodsStock< ARGV[goodsNum] then
return 2;
end
end
--
for goodsNum=2,#KEYS do
redis.call('DECRBY',KEYS[goodsNum],ARGV[goodsNum]);
end
--
redis.call('sadd',ARGV[#ARGV],KEYS[1]);
return 1;
もちろん、このluaをjavaコードに入れることができます.
public boolean orderSecKill(SecKillPara para, Jedis jedis) {
/* 0,
* 2.
* 1.
*/
Long customerId = para.getCustomerId();
String script =
" local ismeber=redis.call('sismember',ARGV[1], KEYS[1]) "
+" if ismeber ~= 0 then " // -- ,
+ " return 0 "
+ " end "
// + " redis.call('set',KEYS[1],1) " // --
// + " redis.call('expire',KEYS[1],2000) " // --
// --check
+ " for goodsNum=2,#KEYS do "
+ " local goodsStock=redis.call('get',KEYS[goodsNum]) "
+ " if goodsStock < ARGV[goodsNum] then "
+ " return 2;"
+ " end "
+ " end "
// --
+ " for goodsNum=2,#KEYS do "
+ " redis.call('DECRBY',KEYS[goodsNum],ARGV[goodsNum]) "
+ " end "
// --
+ " redis.call('sadd',ARGV[1],KEYS[1])"
+ " return 1;";
List keys = new ArrayList();
List args = new ArrayList();
keys.add(customerId.toString());
args.add("all_order_user");//
for (Map.Entry goods : para.getGoodsWithAmount().entrySet()) {
keys.add(goods.getKey().toString());
args.add(goods.getValue().toString());
}
Object o = jedis.eval(script, keys, args);
Long result=Long.valueOf(o.toString());
if(result==1){
return true;
}
return false;
}
public static final class SecKillPara{
private final Long customerId;//
private final Map goodsWithAmount;
public SecKillPara(Long customerId, Map goodsWithAmount) {
super();
this.customerId = customerId;
this.goodsWithAmount = goodsWithAmount;
}
public Long getCustomerId() {
return customerId;
}
public Map getGoodsWithAmount() {
return goodsWithAmount;
}
}
demo, , 。