Backend Development 14 min read

Design and Technical Solutions for a High‑Concurrency Flash Sale (秒杀) System

This article analyzes the challenges of building a flash‑sale system—such as overselling, massive concurrent requests, URL exposure, and database strain—and presents a comprehensive backend design that includes separate databases, dynamic URLs, Redis clustering, Nginx load balancing, rate‑limiting, asynchronous order processing, and service degradation strategies.

Top Architect
Top Architect
Top Architect
Design and Technical Solutions for a High‑Concurrency Flash Sale (秒杀) System

Problems to Consider in a Flash‑Sale System

Flash‑sale (秒杀) platforms like JD or Taobao face critical issues: overselling when stock is limited, extremely high request concurrency within a few minutes, automated bots repeatedly hitting the API, predictable URLs that can be harvested, database overload when the sale shares the same DB as other services, and the sheer volume of requests that can overwhelm caches and servers.

Overselling

If only 100 items are stocked but 200 orders are placed, the business suffers severe loss; preventing this is the top priority.

High Concurrency

Thousands of users flood the system simultaneously, risking cache breakdown and database crashes.

Interface Abuse

Bots may send hundreds of requests per second; the system must filter out invalid or duplicate calls.

Predictable URLs

Users can discover the sale URL via browser dev tools and bypass front‑end controls, so the URL must be hidden or dynamically generated.

Database Design

Sharing the main database with other services can cause cascading failures; a dedicated flash‑sale database with separate order and product tables isolates the load.

Massive Request Handling

Even a Redis cache may be insufficient for tens of thousands of QPS; a Redis cluster with sentinel mode is recommended.

Design and Technical Solutions

Flash‑Sale Database Schema

Two core tables are required: miaosha_order for orders and miaosha_goods for product details, plus optional product and user tables for richer information.

Dynamic Sale URL

Generate the sale endpoint by MD5‑hashing a random string; the front‑end requests the actual URL from the back‑end, preventing pre‑knowledge of the endpoint.

Static Page Generation

Render product description, parameters, and reviews into a static HTML page (e.g., using FreeMarker) so that user requests bypass the application server and database entirely.

Redis Cluster

Deploy Redis in a clustered or sentinel configuration to handle high read‑write traffic and avoid cache penetration.

Use Nginx as Front‑End Proxy

Nginx can handle tens of thousands of concurrent connections and forward them to a Tomcat cluster, greatly improving throughput.

SQL Optimization

Combine stock check and decrement into a single UPDATE statement with optimistic locking (version field) to prevent overselling.

Redis Pre‑Decrement Stock

Initialize stock in Redis (e.g., redis.set(goodsId, 100) ) and atomically decrement via Lua scripts; on order cancellation, increase the stock while ensuring it never exceeds the original amount.

Rate‑Limiter (Token Bucket)

Apply Guava's RateLimiter to generate tokens at a controlled rate. Example implementation:

public class TestRateLimiter {
    public static void main(String[] args) {
        // 1 token per second
        final RateLimiter rateLimiter = RateLimiter.create(1);
        for (int i = 0; i < 10; i++) {
            double waitTime = rateLimiter.acquire();
            System.out.println("Task " + i + " waited " + waitTime);
        }
        System.out.println("Finished");
    }
}

A second variant uses tryAcquire(timeout, TimeUnit.SECONDS) to reject requests that cannot obtain a token within a short window:

public class TestRateLimiter2 {
    public static void main(String[] args) {
        final RateLimiter rateLimiter = RateLimiter.create(1);
        for (int i = 0; i < 10; i++) {
            long timeout = 0L; // 0.5 seconds in real code
            boolean ok = rateLimiter.tryAcquire(0.5, TimeUnit.SECONDS);
            if (!ok) continue; // drop request
            System.out.println("Task " + i + " executed");
        }
        System.out.println("Ended");
    }
}

Asynchronous Order Processing

After passing rate‑limiting and stock checks, push the order request to a message queue (e.g., RabbitMQ). Consumers handle the actual order creation, allowing the front‑end to return quickly and providing retry or compensation mechanisms for failures.

Service Degradation

If a service crashes during the sale, employ circuit‑breaker tools like Hystrix to fall back to a friendly error response instead of a server error.

Asynchronous Ordering

Queue‑based processing decouples the request path, smooths traffic spikes, and improves reliability.

Service Degradation

Implement fallback services to maintain a graceful user experience during partial outages.

Summary

The presented architecture—dedicated database, dynamic URLs, static pages, Redis cluster, Nginx proxy, optimized SQL, token‑bucket rate limiting, asynchronous queue, and circuit‑breaker degradation—can comfortably support hundreds of thousands of concurrent flash‑sale requests; larger scales would require further sharding, Kafka queues, and additional Redis clusters.

backend architectureRedishigh concurrencyNginxasynchronous processingflash saleRate Limiter
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

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