In gleichzeitigen Szenarien wie Flash-Verkäufen und Eilverkäufen kann es zu Überverkäufen kommen. Es gibt keine native Parallelitätslösung in der PHP-Sprache, daher sind andere Methoden erforderlich, um dies zu erreichen.
Die aufgeführten gängigen Lösungen sind:
Verwenden Sie eine Warteschlange, richten Sie einen zusätzlichen Prozess zur Bearbeitung der Warteschlange ein, stellen Sie alle gleichzeitigen Anforderungen in die Warteschlange und verarbeiten Sie sie seriell durch den zusätzlichen Prozess Das Parallelitätsproblem besteht nicht. Es erfordert jedoch zusätzliche Prozessunterstützung und erhebliche Verarbeitungsverzögerungen. In diesem Artikel wird diese Methode nicht zuerst erläutert.
Verwenden Sie die Transaktionsmerkmale der Datenbank, um atomare Aktualisierungen durchzuführen. Diese Methode basiert auf den Transaktionsmerkmalen der Datenbank.
Mit Hilfe der exklusiven Dateisperre können Sie bei der Bearbeitung einer Bestellanfrage eine Datei mit Flock sperren. Nur diejenigen, die die Sperre erfolgreich erhalten, können die Bestellung bearbeiten.
1. Redis-Transaktionsfunktionen nutzen
Redis-Transaktionen sind atomare Vorgänge, die sicherstellen können, dass die Daten während der Auftragsabwicklung nicht durch andere gleichzeitige Prozesse geändert werden.
Beispielcode:
<?php $http = new swoole_http_server("0.0.0.0", 9509); // 监听 9509 $http->set(array( 'reactor_num' => 2, //reactor thread num 'worker_num' => 4 //worker process num )); $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { $uniqid = uniqid('uid-', TRUE); // 模拟唯一用户ID $redis = new Redis(); $redis->connect('127.0.0.1', 6379); // 连接 redis $redis->watch('rest_count'); // 监测 rest_count 是否被其它的进程更改 $rest_count = intval($redis->get("rest_count")); // 模拟唯一订单ID if ($rest_count > 0){ $value = "{$rest_count}-{$uniqid}"; // 表示当前订单,被当前用户抢到了 // do something ... 主要是模拟用户抢到单后可能要进行的一些密集运算 $rand = rand(100, 1000000); $sum = 0; for ($i = 0; $i < $rand; $i++) {$sum += $i;} // redis 事务 $redis->multi(); $redis->lPush('uniqids', $value); $redis->decr('rest_count'); $replies = $redis->exec(); // 执行以上 redis 事务 // 如果 rest_count 的值被其它的并发进程更改了,以上事务将回滚 if (!$replies) { echo "订单 {$value} 回滚" . PHP_EOL; } } $redis->unwatch(); }); $http->start();
Ab-Test verwenden
$ ab -t 20 -c 10 http://192.168.1.104:9509/
2. Datei-Exklusivsperre (Blockierungsmodus) verwenden
Blockierungsmodus Wenn ein Prozess eine exklusive Dateisperre erwirbt, während andere Prozesse die Sperre belegen, bleibt der Prozess hängen und wartet darauf, dass andere Prozesse die Sperre aufheben, und erwirbt dann die Sperre selbst, bevor er fortfährt.
Beispielcode:
<?php $http = new swoole_http_server("0.0.0.0", 9510); $http->set(array( 'reactor_num' => 2, //reactor thread num 'worker_num' => 4 //worker process num )); $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { $uniqid = uniqid('uid-', TRUE); $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $fp = fopen("lock.txt", "w+"); // 阻塞(等待)模式, 要取得独占锁定(写入的程序) if (flock($fp,LOCK_EX)) { //锁定当前指针 // 成功取得锁后,放心处理订单 $rest_count = intval($redis->get("rest_count")); $value = "{$rest_count}-{$uniqid}"; if ($rest_count > 0) { // do something ... $rand = rand(100, 1000000); $sum = 0; for ($i = 0; $i < $rand; $i++) {$sum += $i;} $redis->lPush('uniqids', $value); $redis->decr('rest_count'); } // 订单处理完成后,再释放锁 flock($fp, LOCK_UN); } fclose($fp); }); $http->start();
Ab-Test verwenden
$ ab -t 20 -c 10 http://192.168.1.104:9510/
3. Datei-Exklusivsperre verwenden (nicht blockierender Modus)
Wenn im Blockierungsmodus ein Prozess eine exklusive Dateisperre erwirbt, während andere Prozesse die Sperre belegen, stellt der Prozess sofort fest, dass die Erlangung der Sperre fehlgeschlagen ist, und setzt die Ausführung fort.
Beispielcode:
<?php $http = new swoole_http_server("0.0.0.0", 9511); $http->set(array( 'reactor_num' => 2, //reactor thread num 'worker_num' => 4 //worker process num )); $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { $uniqid = uniqid('uid-', TRUE); $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $fp = fopen("lock.txt", "w+"); // 非阻塞模式, 如果不希望 flock() 在锁定时堵塞,则给 lock 加上 LOCK_NB if(flock($fp,LOCK_EX | LOCK_NB)) //锁定当前指针 { // 成功取得锁后,放心处理订单 $rest_count = intval($redis->get("rest_count")); $value = "{$rest_count}-{$uniqid}"; if($rest_count > 0){ // do something ... $rand = rand(100, 1000000); $sum=0; for ($i=0;$i<$rand;$i++){ $sum+=$i; } $redis->lPush('uniqids', $value); $redis->decr('rest_count'); } // 订单处理完成后,再释放锁 flock($fp,LOCK_UN); } else { // 如果获取锁失败,马上进入这里执行 echo "{$uniqid} - 系统繁忙,请稍后再试".PHP_EOL; } fclose($fp); }); $http->start();
Ab-Test verwenden
$ ab -t 20 -c 10 http://192.168.1.104:9511/
Abschließend werden die Testergebnisse der drei Verarbeitungsmethoden verglichen
Redis-Transaktionsmethode:
...... Concurrency Level: 10 Time taken for tests: 20.005 seconds Complete requests: 17537 Failed requests: 0 Total transferred: 2578380 bytes HTML transferred: 0 bytes Requests per second: 876.62 [#/sec] (mean) Time per request: 11.407 [ms] (mean) Time per request: 1.141 [ms] (mean, across all concurrent requests) Transfer rate: 125.86 [Kbytes/sec] received ......
Exklusive Dateisperre (Blockierungsmodus):
...... Concurrency Level: 10 Time taken for tests: 20.003 seconds Complete requests: 8205 Failed requests: 0 Total transferred: 1206282 bytes HTML transferred: 0 bytes Requests per second: 410.19 [#/sec] (mean) Time per request: 24.379 [ms] (mean) Time per request: 2.438 [ms] (mean, across all concurrent requests) Transfer rate: 58.89 [Kbytes/sec] received ......
Exklusive Dateisperre (nicht blockierender Modus):
...... Concurrency Level: 10 Time taken for tests: 20.002 seconds Complete requests: 8616 Failed requests: 0 Total transferred: 1266846 bytes HTML transferred: 0 bytes Requests per second: 430.77 [#/sec] (mean) Time per request: 23.214 [ms] (mean) Time per request: 2.321 [ms] (mean, across all concurrent requests) Transfer rate: 61.85 [Kbytes/sec] received ......
Nach dem Vergleich der Testergebnisse ist die Redis-Transaktionsmethode besser als die dateiexklusive Sperrmethode Im dateiexklusiven Sperrmodus ist der nicht blockierende Modus besser als der blockierende Modus.
Empfohlenes Tutorial: „PHP-Tutorial“
Das obige ist der detaillierte Inhalt von3 Lösungen für PHP-Parallelitätsszenarien. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!