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.

Java Backend Technology
Java Backend Technology
Java Backend Technology
9 Essential Design Tips for High‑Concurrency Flash Sale Systems

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
end

7.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.

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.

Cacherate limitingflash sale
Java Backend Technology
Written by

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!

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.