ホームページ >バックエンド開発 >PHPチュートリアル >mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

WBOY
WBOYオリジナル
2016-07-06 13:53:231467ブラウズ

注文の簡単な例 (コードがアップロードされ、トランザクションが追加されていない場合):

リーリー

デフォルトで 100 アイテムの在庫があります:
mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

ログテーブル:
mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

Apache ab ツール同時実行:
ab -n 1200 -c 1200 -w http://localhost/queue/index.php >> D:/1.html

その結果、同時実行性の問題が発生します (非常に自然です):
mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

その後、トランザクション制御を追加した後:

リーリー

再度同時実行性をテストします:
ab -n 1200 -c 1200 -w http://localhost/queue/index.php >> D:/1.html
結果は、出力に問題はありません (実際には同時実行の問題は解決されましたか?):
mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

それを行うにはRedisキューを使用するという人がたくさんいますが、具体的な実装についてはまだわかりません。

返信内容:

注文の簡単な例 (コードがアップロードされ、トランザクションが追加されていない場合):

リーリー

デフォルトで 100 アイテムの在庫があります:


mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

ログテーブル:


mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

Apache ab ツール同時実行:

ab -n 1200 -c 1200 -w http://localhost/queue/index.php >> D:/1.html

その結果、同時実行性の問題が発生します (非常に自然です):


mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

その後、トランザクション制御を追加した後:

リーリー

再度同時実行性をテストします:

ab -n 1200 -c 1200 -w http://localhost/queue/index.php >> D:/1.html
結果は、出力に問題はありません (実際には同時実行の問題は解決されましたか?):

mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

それを行うにはRedisキューを使用するという人がたくさんいますが、具体的な実装についてはまだわかりません。

トランザクションは同時実行性とは何の関係もありません。トランザクションを使用すると、このロジック部分の成功または失敗が保証されるだけで、同時実行中にプログラム ロジックを制御できることは保証されません。

同時実行性を制御するにはロックが依然として必要です。たとえば、mysql は前述のように楽観的ロックを実装します。

たとえば、upadte table set a = a - 1 where a = 5; a=5 の場合にのみ、この更新によりデータが実際に変更され、変更されるデータのバージョンが予想したものと異なることが確認されます。の場合、操作は実行されず、影響を受ける行の数を使用して変更があるかどうかが判断され、次の操作を続行するか終了します。

単一のマシンがロックをブロックする目的を達成するために flock を直接使用できる場合は、排他的ロックを使用することもできます。

または、ロックを実装するための Redis と memcache。

リクエストが多い場合は、ロック操作に redis と memcache を使用するか、同時実行性を処理するためにメッセージ キューの使用を検討することをお勧めします。

4000 の同時実行でテストしましたが、依然として問題が発生します:

mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

mysql - PHP での同時実行性の高い注文はトランザクションを使用して解決できますか?

PSがトランザクションを利用する場合は必ず行ってください

更新するには、A=X 制限 1 の表から * を選択してください;

;

务必使用FOR UPDATE更新に使用しない場合は、必ず負の数値が表示されます。

データテーブルをロックすることもできます (

エンジンはテーブルロックを使用します)

InnoDB引擎就用行锁;MyISAM

コアはまだデータベース内にあります。悲観的ロックまたは楽観的ロックを作成する必要があります。

表面的には問題は解決したように見えますが、実際には特殊な状況下では売られ過ぎが依然として発生する可能性があります。


トランザクションは均一に送信することで解決できますが、一般的にこの問題はロックすることで解決する必要があります。

ロックを使用しない場合は、キューに入れられた挿入であるキューを使用し、非同期を同期に変更する必要があります。これが最も安全な方法です。


私の回答を参照してください。

https://segmentfault.com/q/1010000005105041/a-1020000005106490

队列的方法可以是,一个商品库存(也可以所有商品一起,跑一个下单队列)在后台有一个脚本在跑,然后把请求变成串行。这个方案会被推崇是因为可控性,我们可以根据系统需要控制处理的频率。

缓存的做法是,定时将商品库存更新到缓存里面去,利用缓存的原子读写,对缓存里的库存进行自减操作,如果自减后大于零,就可以走后面的下单流程(下单流程仍然需要完整的事务加锁来保证一致性),缓存的目的在于,避免流量冲击,只有有效流量进入db。

把第一个例子中的$condition['id'] = 1;换成"id=1 and stock_left > 0"的等效条件就解决问题了,不需要事务,事务在这个时候起不到什么作用。后面的逻辑当然也要相应调整,因为setDec肯定成功,但是不一定真有记录被修改了,所以伪码示例:

<code>$sql = "update table set num = num - 1 where num > 0";
$updatedRows = get_updated_rows($db->exec($sql));
if ($updatedRows > 0) {
    //成功
} else {
    //失败
}</code>
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。