Preventing Overselling in High‑Concurrency PHP E‑commerce: Test Cases and Solutions

This article evaluates several PHP approaches—including plain inserts, unsigned mode, MySQL transactions, file locks, and Redis queues—to prevent product inventory overselling under high concurrency, presenting test setups, results, and a final recommendation favoring Redis for its superior performance.

php Courses
php Courses
php Courses
Preventing Overselling in High‑Concurrency PHP E‑commerce: Test Cases and Solutions

The article investigates how to avoid product inventory overselling in a PHP e‑commerce system when handling high concurrency, using a product with initial stock of 15 and 30 total requests (10 concurrent per batch) as the test scenario.

Test 1 – Plain Order : Executed with ab -n 30 -c 10 http://xxxxx.top/code/the_limit/add_order.php. Result: 15 successful stock deductions, inventory dropped to -7, and 8 requests correctly reported insufficient stock, indicating the plain method allows negative inventory.

Test 2 – Unsigned Mode : Executed with ab -n 30 -c 10 http://xxxxx.top/code/the_limit/unsigned.php. Result: 15 successful deductions, inventory reached -6, and 9 insufficient‑stock detections; adding FOR UPDATE lock on the query had little effect.

Test 3 – MySQL Transaction (Row Lock) : Executed with ab -n 30 -c 10 http://xxxxx.top/code/the_limit/ACID.php. Result: 15 successful deductions, inventory never became negative, and 15 correctly reported insufficient stock; the transaction handled 3000 requests with 1000 concurrent threads without failure.

Test 4 – Non‑Blocking File Exclusive Lock : Compared blocking and non‑blocking file locks; both prevented negative inventory, but performance ranked as Transaction > Blocking Lock > Non‑Blocking Lock.

Test 5 – Redis Queue with Optimistic Lock (code excerpt):

<?php
$redis = new Redis();
$redis->connect("127.0.0.1", 6379);
$redis->auth('PASSWORD');
$redis->watch('sales'); // optimistic lock
$sales = $redis->get('sales');
$n = 15;
if ($sales >= $n) {
    insertLog('库存为0 秒杀失败');
    exit('秒杀结束');
}
$redis->multi();
$redis->incr('sales');
$res = $redis->exec(); // 1 on success, 0 on failure
if ($res) {
    // proceed with MySQL update
    $con = new mysqli('localhost','root','PASSWORD','dev');
    $product_id = 1;
    $buy_num = 1;
    sleep(1);
    $sql = "update products set store=store-$buy_num where id=$product_id";
    if (mysqli_query($con, $sql)) {
        echo "秒杀完成";
        insertLog('恭喜 秒杀成功');
    }
} else {
    insertLog('抱歉 秒杀失败');
    exit('抢购失败');
}
function db(){
    global $con;
    $con = new mysqli('localhost','root','WOrd1234.*','dev');
    if(!$con){ echo "数据库连接失败"; }
}
function insertLog($content){
    global $con;
    $sql = "INSERT INTO `order_log` (content) values('$content')";
    mysqli_query($con,$sql);
}
?>

The corresponding load test command was

ab -n 30 -c 10 http://xxxxxx.top/code/the_limit/optimistic_redis_lock.php

. This approach kept inventory from going negative and demonstrated the best performance among all methods.

Final Conclusion : For high‑concurrency flash‑sale scenarios, using Redis with an optimistic lock provides the most reliable and fastest solution to prevent overselling.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

transactionconcurrencyinventorymysqlPHP
php Courses
Written by

php Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.