在秒杀,抢购等并发场景下,可能会出现超卖的现象,在 PHP 语言中并没有原生提供并发的解决方案,因此就需要借助其他方式来实现并发控制。
列出常见的解决方案有:
使用队列,额外起一个进程处理队列,并发请求都放到队列中,由额外进程串行处理,并发问题就不存在了,但是要额外进程支持以及处理延迟严重,本文不先不讨论这种方法。
利用数据库事务特征,做原子更新,此方法需要依赖数据库的事务特性。
借助文件排他锁,在处理下单请求的时候,用 flock 锁定一个文件,成功拿到锁的才能处理订单。
一、利用 Redis 事务特征
redis 事务是原子操作,可以保证订单处理的过程中数据没有被其它并发的进程修改。
示例代码:
<?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 测试
$ ab -t 20 -c 10 http://192.168.1.104:9509/
二、利用文件排他锁 (阻塞模式)
阻塞模式下,如果进程在获取文件排他锁时,其它进程正在占用锁的话,此进程会挂起等待其它进程释放锁后,并自己获取到锁后,再往下执行。
示例代码:
<?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 测试
$ ab -t 20 -c 10 http://192.168.1.104:9510/
三、利用文件排他锁 (非阻塞模式)
非阻塞模式下,如果进程在获取文件排他锁时,其它进程正在占用锁的话,此进程会马上判断获取锁失败,并且继续往下执行。\
示例代码:
<?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 测试
$ ab -t 20 -c 10 http://192.168.1.104:9511/
最后给出三种处理方式的测试结果比较
redis 事务方式:
...... 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 ......
文件排他锁(阻塞模式):
...... 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 ......
文件排他锁(非阻塞模式):
...... 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 ......
经测试结果对比,redis 事务方式优于文件排他锁方式,而文件排他锁方式中,非阻塞模式优于阻塞模式。
相关教程推荐:《PHP教程》
Atas ialah kandungan terperinci 分享几种PHP并发场景的解决方案. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

PhpSSsionsTrackUserDataacrossmultiplePagerequestSuseUniquidStoredinacookie.here'ShoWtomanAgeThemEffectely: 1) startAnSessionWithSession_Start () danStoRedatain $ _Session.2)

Dalam PHP, iterating melalui data sesi dapat dicapai melalui langkah -langkah berikut: 1. Mulakan sesi menggunakan session_start (). 2. ITERATE melalui gelung foreach melalui semua pasangan nilai utama dalam array $ _Session. 3. Apabila memproses struktur data kompleks, gunakan fungsi is_array () atau is_object () dan gunakan print_r () untuk mengeluarkan maklumat terperinci. 4. Apabila mengoptimumkan traversal, paging boleh digunakan untuk mengelakkan memproses sejumlah besar data pada satu masa. Ini akan membantu anda mengurus dan menggunakan data sesi PHP dengan lebih cekap dalam projek sebenar anda.

Sesi ini menyedari pengesahan pengguna melalui mekanisme pengurusan negara pelayan. 1) Penciptaan sesi dan penjanaan ID unik, 2) IDS diluluskan melalui kuki, 3) kedai pelayan dan mengakses data sesi melalui ID, 4) Pengesahan pengguna dan pengurusan status direalisasikan, meningkatkan keselamatan aplikasi dan pengalaman pengguna.

TOSTOREAUSER'SNAMEINAPHPSESSION, startTheSessionWithSsion_Start (), thenassignthenameto $ _Session ['username']

Sebab -sebab kegagalan phpsession termasuk kesilapan konfigurasi, isu cookie, dan tamat tempoh sesi. 1. Ralat Konfigurasi: Semak dan tetapkan session.save_path yang betul. Masalah 2.Cookie: Pastikan kuki ditetapkan dengan betul. 3.Session Expires: Laraskan Nilai Sesi.GC_MAXLifetime untuk melanjutkan masa sesi.

Kaedah untuk masalah sesi debug dalam PHP termasuk: 1. Periksa sama ada sesi dimulakan dengan betul; 2. Sahkan penghantaran ID sesi; 3. Semak penyimpanan dan bacaan data sesi; 4. Semak konfigurasi pelayan. Dengan mengeluarkan ID dan data sesi, melihat kandungan fail sesi, dan lain-lain, anda boleh mendiagnosis dan menyelesaikan masalah yang berkaitan dengan sesi.

Pelbagai panggilan ke session_start () akan menghasilkan mesej amaran dan kemungkinan penggantian data. 1) PHP akan mengeluarkan amaran, menyebabkan sesi telah dimulakan. 2) Ia boleh menyebabkan penggantian data sesi yang tidak dijangka. 3) Gunakan session_status () untuk memeriksa status sesi untuk mengelakkan panggilan berulang.

Mengkonfigurasi kitaran hayat sesi dalam PHP boleh dicapai dengan menetapkan sesi.gc_maxlifetime dan session.cookie_lifetime. 1) session.gc_maxlifetime mengawal masa survival data sesi pelayan, 2) session.cookie_lifetime mengawal kitaran hayat kuki klien. Apabila ditetapkan ke 0, kuki tamat apabila penyemak imbas ditutup.


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

SublimeText3 Linux versi baharu
SublimeText3 Linux versi terkini

MinGW - GNU Minimalis untuk Windows
Projek ini dalam proses untuk dipindahkan ke osdn.net/projects/mingw, anda boleh terus mengikuti kami di sana. MinGW: Port Windows asli bagi GNU Compiler Collection (GCC), perpustakaan import yang boleh diedarkan secara bebas dan fail pengepala untuk membina aplikasi Windows asli termasuk sambungan kepada masa jalan MSVC untuk menyokong fungsi C99. Semua perisian MinGW boleh dijalankan pada platform Windows 64-bit.

Muat turun versi mac editor Atom
Editor sumber terbuka yang paling popular

mPDF
mPDF ialah perpustakaan PHP yang boleh menjana fail PDF daripada HTML yang dikodkan UTF-8. Pengarang asal, Ian Back, menulis mPDF untuk mengeluarkan fail PDF "dengan cepat" dari tapak webnya dan mengendalikan bahasa yang berbeza. Ia lebih perlahan dan menghasilkan fail yang lebih besar apabila menggunakan fon Unicode daripada skrip asal seperti HTML2FPDF, tetapi menyokong gaya CSS dsb. dan mempunyai banyak peningkatan. Menyokong hampir semua bahasa, termasuk RTL (Arab dan Ibrani) dan CJK (Cina, Jepun dan Korea). Menyokong elemen peringkat blok bersarang (seperti P, DIV),

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)
