Redisロックメカニズム高同時処理
文章の本文
ここでは主にRedisのsetnxのコマンドを用いて高同時性を処理する.
setnxには2つのパラメータがあります.最初のパラメータはキーを表します.2番目のパラメータは値を表します.現在のキーが存在しない場合は、現在のキーが挿入され、2番目のパラメータが値になります.は、1を返します.現在のキーが存在する場合、0が返されます.
在庫表の作成
初期在庫を10に設定
オーダー表の作成
テストがロックされていない場合
abテストシミュレーションが同時発生し,在庫が正しいことが分かった.
注文書を見ています
いくつかの注文が同じ在庫データを操作していることがわかり、超過販売を引き起こす可能性があります.
コードを変更してredisロックに参加してデータ制御を行う
abテストを再度行い、テスト結果を表示する
受注表が同じ在庫データを操作していないことが判明しました.従って、
ここではロックをかけるときに期限切れを判断する必要はありません.ここではデッドロックを避けるために、期限切れの判断を追加します.期限が切れたときにロックを自発的に削除します.
ここでは主にRedisのsetnxのコマンドを用いて高同時性を処理する.
setnxには2つのパラメータがあります.最初のパラメータはキーを表します.2番目のパラメータは値を表します.現在のキーが存在しない場合は、現在のキーが挿入され、2番目のパラメータが値になります.は、1を返します.現在のキーが存在する場合、0が返されます.
在庫表の作成
CREATE TABLE `storage` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`number` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
初期在庫を10に設定
オーダー表の作成
CREATE TABLE `order` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`number` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
テストがロックされていない場合
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'root');
$sql="select `number` from storage where id=1 limit 1";
$res = $pdo->query($sql)->fetch();
$number = $res['number'];
if($number>0)
{
$sql ="insert into `order` VALUES (null,$number)";
$order_id = $pdo->query($sql);
if($order_id)
{
$sql="update storage set `number`=`number`-1 WHERE id=1";
$pdo->query($sql);
}
}
abテストシミュレーションが同時発生し,在庫が正しいことが分かった.
mysql> select * from storage;
+----+--------+
| id | number |
+----+--------+
| 1 | 0 |
+----+--------+
1 row in set (0.00 sec)
注文書を見ています
mysql> select * from `order`;
+----+--------+
| id | number |
+----+--------+
| 1 | 10 |
| 2 | 10 |
| 3 | 9 |
| 4 | 7 |
| 5 | 6 |
| 6 | 5 |
| 7 | 5 |
| 8 | 5 |
| 9 | 4 |
| 10 | 1 |
+----+--------+
10 rows in set (0.00 sec)
いくつかの注文が同じ在庫データを操作していることがわかり、超過販売を引き起こす可能性があります.
コードを変更してredisロックに参加してデータ制御を行う
_redis = new Redis();
$this->_redis ->connect('127.0.0.1');
}
public static function getInstance()
{
if(self::$_instance instanceof self)
{
return self::$_instance;
}
return self::$_instance = new self();
}
/**
* @function
* @param $key
* @param $expTime
*/
public function set($key,$expTime)
{
//
$isLock = $this->_redis->setnx($key,time()+$expTime);
if($isLock)
{
return true;
}
else
{
// 。 , , 。
$val = $this->_redis->get($key);
if($val&&$val
abテストを再度行い、テスト結果を表示する
mysql> select * from `order`;
+----+--------+
| id | number |
+----+--------+
| 1 | 10 |
| 2 | 9 |
| 3 | 8 |
| 4 | 7 |
| 5 | 6 |
| 6 | 5 |
| 7 | 4 |
| 8 | 3 |
| 9 | 2 |
| 10 | 1 |
+----+--------+
10 rows in set (0.00 sec)
受注表が同じ在庫データを操作していないことが判明しました.従って、
redis
ロックを用いることは、高同時性を効率的に処理することができる.ここではロックをかけるときに期限切れを判断する必要はありません.ここではデッドロックを避けるために、期限切れの判断を追加します.期限が切れたときにロックを自発的に削除します.