How Redis Powers High‑Concurrency Flash Sales (秒杀)
This article explains why Redis is essential for flash‑sale systems, detailing load characteristics, the three sale phases, Redis features such as high concurrency and atomic operations, and practical implementations using Lua scripts and distributed locks to ensure safe stock checking and deduction.
Flash Sale Load Characteristics
Flash‑sale items have far fewer stock units than the number of users attempting to purchase them, which creates two dominant load traits.
Extremely High Instant Concurrency
Typical relational databases can handle only thousands of requests per second, while Redis can sustain millions. Therefore, Redis must intercept the majority of incoming requests to protect the database from overload.
Read‑Heavy, Write‑Light
Clients first read the stock (a simple key‑value query). Only when stock is available does the system decrement the stock and create an order. A local in‑memory flag can cache the "zero‑stock" state to avoid unnecessary Redis reads.
Flash Sale Phases
Pre‑Sale
Users constantly refresh the product page, causing a surge of page‑view requests. Staticizing page elements and serving them via CDN or browser cache prevents these requests from reaching the backend.
During Sale
When the sale starts, many users click the “Buy Now” button, generating massive concurrent stock‑check requests. If a request finds available stock, the system immediately decrements the stock, creates an order, and proceeds with payment and logistics. If no stock is found, the user retries.
Critical operations in this phase are:
Stock verification
Stock decrement
Order processing
Because every request must read stock, Redis stores the current inventory so that each request can read directly from Redis.
Why not perform stock decrement in the database?
Updating stock in the database introduces two risks:
Additional overhead to keep Redis and the database synchronized.
Potential overselling because the database cannot update the stock quickly enough, causing many requests to read stale values.
Therefore, stock decrement must be performed atomically in Redis.
Two Redis‑based solutions are commonly used:
Atomic operations via Lua scripts.
Distributed locks.
Post‑Sale
After the sale ends, request volume drops dramatically. Users may still refresh the product page or check order status, but the backend can easily handle the load.
Redis Features Supporting Flash Sales
High Concurrency Support
Redis inherently handles millions of operations per second. For multiple flash‑sale items, a sharded Redis cluster can store each item’s stock on a different instance, preventing a single instance from becoming a bottleneck.
Atomic Stock Check and Decrement
Redis provides atomic commands and Lua scripting to guarantee that stock verification and decrement happen as a single indivisible operation.
Atomic Operations for Flash Sales
Each flash‑sale item stores two fields in a Redis hash:
key: itemID
value: {total: N, ordered: M}A Lua script ensures atomicity:
# Get stock information
local counts = redis.call("HMGET", KEYS[1], "total", "ordered")
local total = tonumber(counts[1])
local ordered = tonumber(counts[2])
local k = tonumber(ARGV[1])
if ordered + k <= total then
redis.call("HINCRBY", KEYS[1], "ordered", k)
return k
end
return 0The client executes the script with EVAL, passing the desired quantity as ARGV[1]. A return value equal to k means success; 0 means failure.
Distributed Locks for Flash Sales
Clients first acquire a Redis‑based lock; only the lock holder can perform stock verification and decrement, eliminating the need for Lua atomicity. A typical lock implementation uses the SET key value NX PX timeout pattern (or the RedLock algorithm).
// Acquire lock
if redis.call("SET", lockKey, clientId, "NX", "PX", lockTTL) then
-- critical section
local counts = redis.call("HMGET", itemKey, "total", "ordered")
local total = tonumber(counts[1])
local ordered = tonumber(counts[2])
local k = tonumber(ARGV[1])
if ordered + k <= total then
redis.call("HINCRBY", itemKey, "ordered", k)
-- proceed with order creation
end
-- Release lock only if still owned
if redis.call("GET", lockKey) == clientId then
redis.call("DEL", lockKey)
end
else
-- lock not acquired, reject or retry
endUsing a dedicated Redis instance for lock handling isolates flash‑sale traffic from regular business traffic.
Additional Operational Considerations
Staticize product pages and serve them via CDN to reduce pre‑sale load.
Cache a zero‑stock flag locally to avoid unnecessary Redis reads.
Do not set expiration on stock keys; they act as a cache of the database and should remain until the sale ends.
Isolate flash‑sale Redis nodes from everyday workloads to prevent interference.
Apply request throttling or IP black‑listing at the gateway to block malicious traffic.
Summary
Redis is a core component of flash‑sale systems, handling high‑concurrency stock checks, atomic stock decrements, and optionally distributed locking. The typical workflow is: read stock from Redis, atomically decrement if available, then create the order in the database. Proper isolation, static content, and flow‑control further ensure system stability during the massive traffic spikes of a flash sale.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaEdge
First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
