Redis同時原子性原理


Redis原子の原理
要約:
1、Redisは単一プロセスの単一スレッドのネットワークモデルであり、epollネットワークモデルを用い、ネットワークモデルはすべて単一スレッドの非同期非ブロック処理ネットワーク要求である
2.Redisのシングルスレッドはすべてのクライアント接続要求を処理し、コマンド読み書き要求を行う.(rdbやaofなどのタスクはforkサブプロセスで処理され、redisプライマリスレッドがクライアントを処理するコマンドには影響しません)
3、Redisが提供するすべてのAPI操作は、サービス側に対してoneby oneが実行し、コマンドは次から次へと実行され、並列実行は存在しない.
4、Redisクライアントでは、高同時エラーの読み書きデータが発生する可能性があります.以下、電子商取引の秒殺の例を挙げて説明します.
Redisの同時表現
RedisのAPIは原子的な操作ですが、複数のコマンドは同時でも原子的ですか?
次のコードを見てください.
   $num = 10;//システム在庫$user_id =  \Session::get('user_id');//現在のユーザID$len=Redis::llen('order:1');//在庫をチェックし、order:1健名if($len>=$num)return'が売り切れたと定義します.
$result =\Redis::lpush('order:1',$user_id);//奪ったユーザーをリストにif($result)return'おめでとうございます!奪ったよ';
コードが正常に動作している場合、リストorder:1には、在庫が10個しかないため、最大10人のユーザーのidしか格納できません.しかしながら、jmeterツールを使用してマルチユーザ同時要求をシミュレートすると、order:1では常に5人以上のユーザ、すなわち「オーバーハンド/オーバーハンド」が発生していることが分かった.解析の問題はこのコードにあります.
 $len = \Redis::llen('order:1');  //    ,order:1      
 if($len >= $num)
   return '      ';

llenとlpushの2つの操作は単独で実行すると原子性を備えているが,上記の業務の2つのコマンドを組み合わせて1つの業務を完了すると言えるが,2つのコマンドを組み合わせると原子性を備えず,2つのコマンドの間に他のクライアントが汚れたデータを読み書きする場合がある.
ある程度、現在9人が買い占めに成功し、3人のユーザーが同時に買い占めに来た場合、3人のユーザーは前後順があると仮定するが、最初のユーザーlpushがredisに着く前に、他の2人のユーザーがpushを取得する前の長さ9になった場合、if条件は迂回され(条件が同時に満たされた)、この3人のユーザーはいずれも買い占めに成功した.実際には在庫が1つしか残っていません.
高並発下では、問題に見えないものが多く、実際に発生した問題となっている.「オーバー・セール」の問題を解決するには、在庫検査時の操作が順次実行されることを保証することが核心であり、イメージ的には「マルチスレッド」を「単一スレッド」に変えることである.多くのユーザーが同時に到着しても、チェックして買い占め資格を与え、在庫がなくなると、後のユーザーは継続できません.この「単一スレッド」を実現するためにredisの原子操作を用いる必要がある.まず在庫をgoods_に預けますstore:1このリストでは、10件の在庫があると仮定して、リストに10個の数をpushします.この数は実際の意味がなく、1件の在庫を表すだけです.買い占め開始後、ユーザーが1人来るたびにgoods_からstore:1のpopの数は、ユーザーの買い占めに成功したことを示しています.リストが空の場合は、すでに奪われたことを示します.リストのpop操作は原子的であるため,多くのユーザが同時に到着しても順次実行される.買い占めのサンプルコードは以下の通りです.例えば、ここで在庫(利用可能な在庫、ここで強調します.一般的には商品詳細ページの買い占めですが、後者が入ってきて見た在庫はバックグラウンドシステム構成の10個の在庫数ではない可能性があります)をredisキューに入れます.
 
 $num=10; //  
 $len=\Redis::llen('goods_store:1'); //    ,goods_store:1      
 $count = $num-$len; //    -       =       
 for($i=0;$i

 
はい、買い占めの時間です.
 /*       ,     redis      */
 $count=\Redis::lpop('goods_store:1');//lpop              。
 if(!$count)
    return '      ';
 /*            */
\DB::table('goods')->decrement('num', 1);//  num    

ユーザーの買い占めに成功した後、上記のように、order:1リストにユーザーIDを格納することができます.次に、これらのユーザーをデータベースとのインタラクションに関連する注文の他のステップに導くことができます.最終的には少ない人しかここまで来ないでしょう.データベースの圧力の問題も解決します.上のコードを変更します.
 
$user_id =  \Session::get('user_id');//      id
/*       ,     redis      */
$count=\Redis::lpop('goods_store:1');
if(!$count)
  return '      ';

$result = \Redis::lpush('order:1',$user_id);
if($result)
  return '   !    ';


 
実際の効果を検出するために、私はjmeterツールを使って100、200、1000人のユーザーをシミュレートして同時に買い占めを行い、大量のテストを経て、最終的に買い占めに成功したユーザーは終始10で、「超買い占め/超販売」は現れなかった.