高合併電子商取引の在庫はどのようにしていますか?

3456 ワード

Redis incrementの原子操作により在庫数の安全を確保
  • まずredisに在庫情報があるかどうかを調べ、なければデータベースに行って調べることで、データベースへのアクセス回数を減らすことができます.取得後にredisに数値を記入し,商品idをkey,数量をvalueとする.シーケンス化方式をStringRedisSerializerに設定することに注意してください.そうしないとvalueを加減することはできません.また、redisがこのkeyに対応するタイムアウト時間を設定して、すべての商品在庫データがredisに含まれないようにする必要があります.
  • 注文数の大きさを比較し、十分であれば後続の論理を行う.
  • redisクライアントのincrementを実行し、パラメータが負の場合は減算します.redisは単一スレッド処理であり,incrementがkeyに対応するvalueを減少させたため修正後の値を返す.在庫が1の場合、3を減らしたり、30を減らしたりすることが多いので、実際には足りないので、無駄に減らしています.
  • データベースの在庫を差し引くと、このときselectクエリーを必要とせず、updateを楽観的にロックし、在庫フィールド値を1に減らします.
  • 在庫を差し引いて注文システムで注文します.

  • サンプルシーン:
  • は、2人のユーザが第1のステップで在庫が10に等しいと仮定し、Aユーザは第2のステップ10に進み、同時に1秒以内にBユーザは第2のステップ3に進む.
  • redis単一スレッド処理のため、Aユーザラインプログラムが先にredis文を実行すると、現在在庫が0に等しくなり、Bは失敗するしかなく、データベースの更新は行われません.
  •     public void order(OrderReq req) {
            String key = "product:" + req.getProductId();
            //    :          
            Integer num = (Integer) redisTemplate.get(key);
              if (num == null){
              //         
              //          set redis,    NX         redis     key    set     redis
              //           StringRedisSerializer,     value     
              //         ,     redis          ,      。
               if (count >=0) {
                //        
                redisTemplate.expire(key, 60*10+       , TimeUnit.SECONDS);
            }
              //          ,             
            }
            if (num < req.getNum()) {
                logger.info("    ");
            }
            //    :    
            long value = redisTemplate.increment(key, -req.getNum().longValue());
            //     
            if (value >= 0) {
                logger.info("    ");
                // update                ,        
                //          ,   LCN      ,                 
                //              ,       ,  mq,          。
                boolean res= updateProduct(req);
                  if (res)
                    createOrder(req);
            } else {
                //       0 ,           ,  A         10   ,  B  9       ,
                //   B    10-9=1, A    1-10=-9,              ,      1 
                redisTemplate.increment(key, req.getNum().longValue());
                logger.info("  redis  ");
            }
        }

    update楽観ロックの使用
    updateProductメソッドで実行されるsqlは、次のとおりです.
    update Product set count = count - #{    } where id = #{id} and count - #{    } >= 0;

    redisはすでに超過販売を防止しているが、データベースレベルでは、すべての商品がredisを使用するとは限らないため、超過販売を防止するために、redisが崩壊したときに使用できない場合や、redis処理を必要としない場合には、楽観的なロックを使用する.
    sqlを使用する各文にはトランザクションがあるため、2つのsqlが同時に実行されると、1つのsqlが先に実行され、もう1つの後に実行されるだけで、前述したシーンと同じです.
    分散トランザクションについて簡単に説明します.
    2つのシステムを分けて在庫と受注を処理する場合、LCNフレームワークで分散トランザクションを行うことができますが、httpリクエストのため、受注システムのネットワークがタイムアウトし、結果が返されない確率もあります.
    実際には、最終的な一貫性を使用して、データテーブルにインタラクティブな流水記録を記録し、在庫の更新に成功した後、このインタラクティブな流水記録を更新する在庫操作フィールドを処理済みとし、受注処理フィールドを処理中とし、mqを送信し、受注作成のコールバックを待つこともできます.結果が戻らないように、注文システムの結果をアクティブに照会するタイミングタスクも行います.
    シナリオのメリット
  • データベースの商品在庫に頻繁にアクセスする必要はありません
  • 他のユーザ
  • をブロックする.
  • 安全扣减库存量
  • メモリアクセス在庫数、データベースインタラクションの減少
  • 高同時追加最適化
  • ユーザーが注文にアクセスするのは、フロントエンドuiがユーザーに決済をトリガーした後、ボタンをグレーにして、繰り返しトリガーを防止することができるからです.
  • 在庫数に応じてredisを使用するかどうかを選択できます.在庫数が少ない場合、または最近注文回数が少ない商品は、redisを置く必要がありません.少ない人が見たり買ったりする場合、redisを置く必要がなく、メモリを消費するためです.
  • 時間になって買い占めをする場合、mqキュー形式を使用することができ、ユーザーが商品の購入をトリガーした後、キューに入り、ユーザーのページをずっと回転させ、彼が買う番になったら決済ページに入り、決済ページの後続の流れは本文と一致する.