How JD’s Flash‑Sale System Handles Millions of Requests with Redis‑Lua

This article explains the design of JD’s flash‑sale (抢购) system, covering its business model, core Redis‑based inventory management, Lua scripting for atomic stock deduction, rate‑limiting, asynchronous logging with JMQ, and fail‑over strategies to ensure high‑concurrency reliability.

dbaplus Community
dbaplus Community
dbaplus Community
How JD’s Flash‑Sale System Handles Millions of Requests with Redis‑Lua

Service Overview

Flash sale (限时抢购), also known as Flash sale, originated from the French site Vente Privée. It is a B2C e‑commerce activity that offers well‑known brand products at 10‑50% of the original price for a limited time (typically 5‑10 days) and limited quantity, requiring customers to complete payment within a short window (often 20 minutes).

Key Characteristics

Rich brand selection – domestic and international first‑ and second‑tier brands.

Short duration – each promotion lasts 5‑10 days, first‑come‑first‑served.

Deep discounts – prices are 1‑5% of the original.

Main Functions of the System

Create promotion service: After a promotion is created and approved, the promotion management system calls the flash‑sale system to create the promotion.

Seize service: For qualifying orders, the service deducts the remaining quantity (the “quota”).

Target SKUs

The system mainly handles single‑item promotions such as direct price cuts or fixed‑price offers. Typical channels are mobile app, WeChat, QQ, and the main website. Purchase limits can be based on quantity, IP, user ID (pin), or a combination.

System Design Highlights

The real‑time inventory managed by the flash‑sale system is not the physical stock but the number of purchasable units (quota). After a user obtains qualification, the actual stock is deducted from a separate inventory service.

Traditional row‑level locking in databases cannot handle the massive load during peak promotions, so the system uses a Redis cluster (16 shards, master‑slave replication across two data centers) to cache promotion data such as promotion ID, remaining quota, and purchase count.

Redis Data Structure

o: 100   // original quantity
b: 99    // remaining purchasable quantity
c: 1     // purchase count (used for rate limiting)

Ensuring No Oversell

Quota deduction is performed atomically using a Redis EVAL script. The pseudo‑code is:

local key = KEYS[1]
local tag = "b"
local num = tonumber(ARGV[1])
local lastNum = redis.call('HINCRBY', key, tag, -num)
if lastNum < 0 then
  return -1  -- out of stock
end
return lastNum

Improving Throughput

Reduce network round‑trips by submitting the entire Lua script with EVALSHA in one call.

Asynchronously record logs using JMQ (JD’s message queue).

Availability and Fail‑over

The service is exposed via JSF (JD’s internal SOA framework) and can degrade to a plain web‑service if JSF is unavailable. If Redis fails, the system rebuilds the quota from MySQL logs that record successful and cancelled purchases.

System Architecture

The flash‑sale service follows a typical high‑concurrency architecture: a front‑end receives purchase requests, hashes the promotion ID to select a Redis shard, pipelines the request, and executes the pre‑loaded Lua script ( q.lua) via EVALSHA. The script performs the following steps in order:

Rate limiting.

IP/Pin validation.

Order ID recording for idempotency.

Quota deduction.

Return success flag.

If any shard reports failure, the whole purchase fails.

Rate Limiting Implementation

Method‑level limits are configured in a central configuration service and take effect within a minute. Example Java code:

private static AtomicInteger atomic = new AtomicInteger(0);
public void test() {
    int limitNum = Config.getLimitNum();
    int nowConcurrent = atomic.incrementAndGet();
    if (nowConcurrent > limitNum) {
        // reject request
    }
    // business logic
    atomic.decrementAndGet();
}

Redis+Lua Sub‑process Details

Parse request parameters and map promotion ID to a Redis shard using MurmurHash.

Obtain the SHA1 of the loaded q.lua script.

Execute EVALSHA with the appropriate key list and argument list.

Process the result; any shard failure aborts the purchase.

Asynchronous Logging with JMQ

Successful Redis‑Lua execution only means the Redis operation succeeded. The purchase is considered complete only after JMQ asynchronously updates MySQL. If JMQ fails, the system rolls back the Redis quota to prevent overselling. A fallback path directly writes to MySQL when JMQ is unavailable, with reduced rate‑limit thresholds to avoid database overload.

Additional Considerations

Designers must also handle per‑user purchase limits, real‑stock shortage cancellations, user‑initiated order cancellations (returning quota), Redis recovery, and promotion termination due to time expiry or depleted quota.

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.

Backend Architectureredishigh concurrencyrate limitingflash salee‑commerceLua scripting
dbaplus Community
Written by

dbaplus Community

Enterprise-level professional community for Database, BigData, and AIOps. Daily original articles, weekly online tech talks, monthly offline salons, and quarterly XCOPS&DAMS conferences—delivered by industry experts.

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.