In gleichzeitigen Szenarien wie Flash-Sales und Rush-Sales 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, starten Sie einen zusätzlichen Prozess zur Bearbeitung der Warteschlange, und gleichzeitige Anforderungen werden in die Warteschlange gestellt und seriell von der verarbeitet Bei einem zusätzlichen Prozess besteht kein Parallelitätsproblem, es sind jedoch zusätzliche Prozessunterstützung und schwerwiegende Verarbeitungsverzögerungen erforderlich. 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
Wenn im Blockierungsmodus 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, die Sperre selbst erwerben und dann fortfahren.
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 anderer Prozess die Sperre belegt, während der Prozess die exklusive Dateisperre erhält, stellt der Prozess sofort fest, dass die 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 wird der Vergleich der Testergebnisse der drei Verarbeitungsmethoden angegeben
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 Die Redis-Transaktionsmethode ist überlegen. Im dateiexklusiven Sperrmodus ist der nicht blockierende Modus besser als der blockierende Modus.
Empfohlene verwandte Tutorials: „PHP-Tutorial“
Das obige ist der detaillierte Inhalt vonTeilen Sie Lösungen für mehrere PHP-Parallelitätsszenarien. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!