thinkphp6でmysqlの悲観的ロックを使って商品の売れ残り問題を解決するための実装
ペシミスティック・ロック(wikipedia)の紹介です。
悲観的ロックとは、その名の通り、データが部外者(このシステムで現在行われている他のトランザクションや、外部システムから処理されるトランザクションを含む)によって変更されることを保守的に考え、データ処理中はデータをロック状態にしておくことを指します。悲観的ロックの実装は、データベースが提供するロック機構に依存することが多い(データアクセスの排他性を真に保証できるのは、データベース層が提供するロック機構だけであり、そうでなければ、本システムにロック機構を実装しても、外部システムがデータを修正しない保証はない)。
利用シーン例 MySQL InnoDBを例として
商品商品テーブルで、商品IDが1、購入回数が1、ステータスが1なら棚にある、2なら棚にない、と仮定する。さて、ユーザーがこの商品を購入し、高連続性でないシナリオでの処理ロジックは次のようになります。
- この商品に関する情報を検索します。
- 購入した数量以上の在庫があるかどうかを確認します。
- アイテムの在庫と売上を変更する。
この上記のシナリオは、同時アクセス数が多い場合に問題が発生する可能性が高いです。商品在庫が100の場合、同時アクセス数が多いシナリオでは1000の同時アクセスがあるかもしれませんが、ステップ2に到達するまでにすべて検出され、パスされるでしょう。その結果、商品在庫が-900という状況になります。明らかに需要が満たされていないのです
商品テーブルの構造。
CREATE TABLE `goods` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`status` tinyint(1) NOT NULL DEFAULT '1',
`total` int(11) NOT NULL DEFAULT '0',
`sell` int(11) NOT NULL DEFAULT '100',
`price` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `test`. `goods`(`id`, `name`, `status`, `total`, `sell`, `price`) VALUES (1, 'goods', 1, 0, 100, 15.00);
オーダーテーブルの構造。
CREATE TABLE `orders` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL DEFAULT '0',
`create_time` datetime NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
`goods_id` int(11) NOT NULL DEFAULT '0',
`order_no` varchar(200) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
悲観的なロック処理を使用する。
グッズ情報を照会した後に現在のデータをロックすると、修正が終わるまでロックが解除されます。そうすると、その過程で、グッズがロックされているので、第三者が変更することができなくなります。
注:悲観的なロックを使うには、mysqlデータベースのオートコミットプロパティをオフにする必要があります。MySQLはデフォルトでオートコミットモードを使用しており、更新操作を行うと、MySQLはすぐに結果をコミットします。thinkphp6では、コミットロールバックを手動で行うためにトランザクションを使用しています。
<?php
namespace app\controller;
use app\BaseController;
use think\facade\Db;
class Test extends BaseController
{
/**
* without locking
* @return string|void
*/
public function test_1()
{
$num = 1;
$goods_id = 1;
Db::startTrans();
try {
$where = [];
$where['id'] = $goods_id;
$where['status'] = 1;
$goods_info = Db::table('goods')->where($where)->find();
if (empty($goods_info)) {
return 'Item does not exist';
}
$total = $goods_info['total'];
$sell = $goods_info['sell'];
if ($total < $num) {
return 'Out of stock';
}
$data['total'] = $total-$num;
$data['sell'] = $sell+$num;
$res = Db::table('goods')->where(['id'=>$goods_id])->update($data);
$order_data = [];
$order_data['uid'] = rand(1000,9999);
$order_data['status'] = 1;
$order_data['create_time'] = date('Y-m-d H:i:s');
$order_data['goods_id'] = $goods_id;
$order_data['order_no'] = date('YmdHis').rand(1000,10000);
$order_res = Db::table('orders')->insert($order_data);
Db::commit();
} catch (\Exception $e) {
// Roll back the transaction
Db::rollback();
echo $e->getMessage();
exit('rollback');
}
echo 'Request successful';
}
/**
* locking - pessimistic locking
* @return string|void
*/
public function test_2()
{
$num = 1;
$goods_id = 1;
Db::startTrans();
try {
$where = [];
$where['id'] = $goods_id;
$where['status'] = 1;
$goods_info = Db::table('goods')->lock(true)->where($where)->find();
if (empty($goods_info)) {
return 'Item does not exist';
}
$total = $goods_info['total'];
$sell = $goods_info['sell'];
if ($total < $num) {
return 'Out of stock';
}
$data['total'] = $total-$num;
$data['sell'] = $sell+$num;
$res = Db::table('goods')->where(['id'=>$goods_id])->update($data);
$order_data = [];
$order_data['uid'] = rand(1000,9999);
$order_data['status'] = 1;
$order_data['goods_id'] = $goods_id;
$order_data['order_no'] = date('YmdHis').rand(1000,10000);
$order_data['create_time'] = date('Y-m-d H:i:s');
$order_res = Db::table('orders')->insert($order_data);
Db::commit();
} catch (\Exception $e) {
// Roll back the transaction
Db::rollback();
echo $e->getMessage();
exit('rollback');
}
echo 'Request successful';
}
}
jmeter ツールを使用してテストを行い、スレッドテストグループを作成します。
jmeterを使った並行性の高いテストの作成例は、こちらをご覧ください。 JMeterを使った高同時性テスト_左右 ... 's blog - CSDN Blog_jmeter High Concurrency Testing
1秒間に1000人の同時ユーザーアクセスを想定したシミュレーションです。
httpリクエストを作成します。
結果ツリーの外観を追加します。
からテストが始まる。
ロック無しで100件分の結果
在庫100点、受注187点、売れ残り87点、これはプロジェクト開発では絶対ダメなことです。
ロック結果が100アイテム
ロックされ、解決されました。
この記事はthinkphp6が商品の売れ残りの問題を解決するためにmysql悲観的なロックを使用して、ここに導入され、より関連thinkphp6商品の売れ残りの内容は、スクリプトハウスの以前の記事を検索してくださいまたは次の関連記事を閲覧を継続し、より将来のスクリプトハウスをサポートして願っています!.
関連
-
php generate unique uid solution 詳細
-
WeChatの小さなプログラムは、ログイン後に携帯電話番号を取得するためにThinkPHP5の承認と組み合わせます。
-
Thinkphpが無効化された機能をバイパスするプロジェクト
-
学生の成績を取得するためのPHPメソッド
-
Laravelで認証を使用するとトップページにジャンプする問題を解決
-
AliCloudのossファイルアップロード機能をlaravelで実装した例
-
オンデマンドアップロードを実現するLaravelフレームワーク Aliyun機能
-
配列の要素が空白でないかどうかを判断する PHP のサンプルコード
-
laravelのユーザーのパスワード変更とメールボックスの結合の詳細操作
-
PHPのオートロード機構の事例を詳しく解説
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン