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スクリプトの内容
        --          ,          
         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,        ,                  。