Ensuring Accurate Inventory Deduction in High‑Concurrency Sales with Redis

This article explains why simple GET‑modify‑SET inventory updates cause overselling in flash‑sale spikes and presents several Redis‑based solutions—including Lua scripts, WATCH‑based optimistic locks, distributed SETNX locks, and asynchronous queue processing—detailing their implementation, advantages, and trade‑offs.

Ma Wei Says
Ma Wei Says
Ma Wei Says
Ensuring Accurate Inventory Deduction in High‑Concurrency Sales with Redis

1. Inventory Deduction Problem

In flash‑sale or抢购 scenarios, many users try to buy the same item simultaneously. Using a simple GET‑modify‑SET sequence in Redis leads to lost updates and overselling because the commands are not atomic.

2. Atomic Operations with Lua Scripts

Redis can execute a Lua script as a single atomic operation. The script reads the stock, checks the requested decrement, and uses DECRBY to reduce the stock safely.

-- Get stock, default 0
local product_stock = tonumber(redis.call('GET', KEYS[1]) or 0)
-- Get decrement amount
local count = tonumber(ARGV[1])
if not count or count <= 0 then
    return -2  -- illegal decrement
end
if product_stock >= count then
    return redis.call('DECRBY', KEYS[1], count)
else
    return -1  -- insufficient stock
end

3. Optimistic Lock with WATCH and Transactions

When Lua scripts are not desired, Redis WATCH can monitor a key and MULTI/EXEC provide an optimistic lock. If the key changes before EXEC, the transaction aborts.

WATCH product_stock
product_stock = GET product_stock
if product_stock >= count then
    MULTI
    DECRBY product_stock count
    if EXEC then
        return 0   -- success
    else
        return -2  -- transaction failed
    end
else
    UNWATCH
    return -1   -- insufficient stock
end

4. Distributed Lock with SETNX (RedLock)

In a distributed environment, a lock can be created with SET key value NX PX 5000. After acquiring the lock, the stock is checked and decremented, then the lock is released.

# Try to acquire lock
SET product_lock_123 "token" NX PX 5000
if lock_success then
    product_stock = GET product_stock
    if product_stock >= count then
        DECRBY product_stock count
        DEL product_lock_123
        return 0
    else
        DEL product_lock_123
        return -1
    end
else
    return -2   # lock failed
end

5. Asynchronous Processing with Message Queues

For extreme spikes, the workflow can pre‑deduct stock in Redis, push the order to a message queue (RabbitMQ, RocketMQ), and later update the database asynchronously. Additional measures such as JVM‑level rate limiting, stock pre‑warming, queue length monitoring, scheduled tasks for order timeout rollback, and Redis persistence (AOF/RDB) are recommended.

These techniques allow a trade‑off between performance and strong consistency, and the appropriate solution should be chosen based on business volume and tolerance.

BackendconcurrencyinventoryRedisDistributed LockLua Script
Ma Wei Says
Written by

Ma Wei Says

Follow me! Discussing software architecture and development, AIGC and AI Agents... Sometimes sharing insights on IT professionals' life experiences.

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.