9 Essential Design Tips for High‑Concurrency Flash Sale Systems
Designing a flash‑sale (秒杀) system for massive traffic requires handling instant spikes, page staticization, CDN acceleration, read‑heavy caching, preventing cache‑penetration and cache‑breakdown, managing pre‑deducted inventory, employing atomic database or Redis operations, leveraging Lua scripts, distributed locks, MQ async processing, and robust rate‑limiting strategies.
Introduction
High concurrency flash‑sale (秒杀) systems are a classic interview topic that appears simple but actually tests a wide range of knowledge from front‑end to back‑end.
1. Instant High Concurrency
During the minutes before the sale start time, user traffic surges dramatically and reaches a peak at the exact second of the flash sale, creating a short‑lived burst of requests.
To cope with this, the system should consider:
Page staticization
CDN acceleration
Caching
MQ asynchronous processing
Rate limiting
Distributed locks
2. Page Staticization
The activity page is the first entry point and receives the highest traffic. Rendering it statically reduces unnecessary server requests; only when the user clicks the flash‑sale button at the exact time does the request reach the back‑end.
3. Flash‑Sale Button
The button is initially disabled (grey) and becomes clickable only at the sale moment. JavaScript controls the state, and static resources (CSS, JS, images) are cached on CDN for fast delivery.
4. Read‑Heavy, Write‑Light
During a flash sale, most requests only read inventory; only a few succeed and perform a write. Therefore, the system should rely on fast reads (e.g., cache) and minimize database writes.
5. Cache Issues
5.1 Cache Breakdown
If many requests miss the cache and hit the database simultaneously, the database can become overloaded. The solution is to use distributed locks or pre‑warm the cache before the sale.
5.2 Cache Penetration
Requests for non‑existent product IDs bypass the cache and hit the database. A Bloom filter can quickly filter out invalid IDs.
For frequently changing data, cache the negative result with a short TTL.
6. Inventory Management
6.1 Database Stock Deduction
Simple SQL update can deduct stock, but it must be atomic to avoid overselling.
update product set stock = stock - 1 where id = 123;Typical pseudo‑code checks stock first, then updates, which is not atomic and can cause oversell.
int stock = mapper.getStockById(123);
if (stock > 0) {
int count = mapper.updateStock(123);
if (count > 0) {
addOrder(123);
}
}Optimistic locking adds a condition to the update:
update product set stock = stock - 1 where id = 123 and stock > 0;6.2 Redis Stock Deduction
Redis incrby is atomic. A typical flow:
boolean exist = redisClient.query(productId, userId);
if (exist) return -1;
int stock = redisClient.queryStock(productId);
if (stock <= 0) return 0;
redisClient.incrby(productId, -1);
redisClient.add(productId, userId);
return 1;Adding synchronized makes the method thread‑safe but hurts performance.
6.3 Lua Script Stock Deduction
Lua scripts guarantee atomicity in Redis. Example script:
StringBuilder lua = new StringBuilder();
lua.append("if (redis.call('exists', KEYS[1]) == 1) then");
lua.append(" local stock = tonumber(redis.call('get', KEYS[1]));");
lua.append(" if (stock == -1) then return 1; end;");
lua.append(" if (stock > 0) then redis.call('incrby', KEYS[1], -1); return stock; end;");
lua.append(" return 0; end; return -1;");7. Distributed Locks
7.1 setNx Lock
if (jedis.setnx(lockKey, val) == 1) {
jedis.expire(lockKey, timeout);
}Separate setnx and expire are not atomic.
7.2 set with NX and PX
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) return true;7.3 Unlock
if (jedis.get(lockKey).equals(requestId)) {
jedis.del(lockKey);
return true;
}
return false;Lua can make unlock atomic:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end7.4 Spin Lock
Repeatedly try set with a timeout until success or the overall timeout expires.
long start = System.currentTimeMillis();
while (true) {
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) return true;
if (System.currentTimeMillis() - start >= timeout) return false;
Thread.sleep(50);
}7.5 Redisson
Redisson handles lock competition, renewal, re‑entrancy, and multi‑node scenarios automatically.
8. MQ Asynchronous Processing
8.1 Message Loss
Before sending a message, write a record to a "message send" table with status *pending*. After successful consumption, update the status to *processed*. A retry job resends pending messages.
8.2 Duplicate Consumption
Maintain a "message processing" table. If a message ID already exists, skip processing. Ensure order creation and table insert are in the same transaction.
8.3 Garbage Messages
Limit retry attempts in the send‑table; when the max count is reached, stop retrying to avoid endless garbage messages.
8.4 Delayed Consumption
Use a delayed queue (e.g., RocketMQ) to automatically cancel unpaid orders after a timeout.
9. Rate Limiting
9.1 User‑Based Limiting
Limit each user ID to a certain number of requests per minute.
9.2 IP‑Based Limiting
Limit each IP address similarly, aware of possible over‑restriction for shared networks.
9.3 Interface‑Based Limiting
Set a global request cap for the flash‑sale API to protect system stability.
9.4 Captcha
Require a one‑time captcha (or sliding‑puzzle) before allowing the request.
9.5 Business Thresholds
Raise participation requirements (e.g., members only, higher user level) to naturally reduce malicious traffic.
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.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
