Preventing Duplicate Orders under 100k QPS: Full‑Stack Strategies
This article explains how to prevent duplicate orders in a high‑traffic system handling 100,000 QPS by combining frontend safeguards such as button disabling and token mechanisms with backend techniques including NGINX rate limiting, gateway token validation, idempotent database design, sharding, distributed locks, optimistic locking, and comprehensive logging and monitoring.
Introduction
The author discusses a common interview question: how to prevent duplicate orders when a system receives 100,000 QPS, and presents a step‑by‑step solution covering both frontend and backend layers.
1. Frontend Interception
1.1 Button Disable
Disable the submit button after the user clicks it to avoid multiple rapid clicks that cause duplicate requests.
const submitButton = document.getElementById('submit-order');
submitButton.disabled = true; // disable button
submitOrder()
.then(response => { submitButton.disabled = false; })
.catch(error => { submitButton.disabled = false; });Optionally show a friendly message like “Your request has been received, please wait for the result.”
1.2 Token Mechanism
Generate a globally unique token (e.g., UUID) when the page loads.
Attach the token to each form submission; the backend checks token uniqueness and rejects repeated submissions.
2. Backend Design
2.1 NGINX Rate Limiting
Apply rate limiting at the edge to protect the system from script‑driven bursts.
http {
# Basic IP‑only limit (3 requests per minute)
limit_req_zone $binary_remote_addr zone=order_base:10m rate=3r/m;
# Enhanced IP + UserID limit (5 requests per minute)
limit_req_zone $binary_remote_addr$http_user_id zone=order_enhanced:20m rate=5r/m;
server {
listen 80;
server_name tianluoboy.com;
location = /v1/order/create {
limit_req zone=order_enhanced burst=3 nodelay;
error_page 429 @toofast;
location @toofast {
default_type application/json;
return 200 '{"code":429,"msg":"操作过于频繁,请稍后再试"}';
}
proxy_pass http://order_backend;
}
location / { proxy_pass http://default_backend; }
}
}2.2 Gateway (Spring Cloud Gateway)
Use the gateway to validate the token against Redis and optionally apply rate limiting, request queuing, or token bucket algorithms.
// Pseudo‑code: gateway filter validates token
if (redis.get(token) != null) {
return error("重复请求");
} else {
redis.setex(token, 60, "1"); // token valid for 60 s
passToBackend();
}2.3 Idempotent Design
Ensure the order API is idempotent, typically by using a unique index (e.g., userId + productId + timestamp) to generate a unique order number.
2.4 Database Sharding & Partitioning
Split databases by user ID and partition tables to spread write load and avoid single‑table bottlenecks; this is essential when MySQL reaches connection limits or disk capacity.
2.5 Distributed Lock (Redis)
// Pseudo‑code: Redisson distributed lock
RLock lock = redisson.getLock("order:lock:" + userId);
if (lock.tryLock(0, 30, TimeUnit.SECONDS)) {
try { createOrder(); } finally { lock.unlock(); }
}2.6 Optimistic Lock Fallback
If the distributed lock fails, fall back to a database optimistic lock using a version field:
UPDATE order SET version = version + 1
WHERE order_id = ? AND version = :old_version;2.7 Logging & Monitoring
Emit detailed logs and expose monitoring metrics so that anomalies can be detected and investigated quickly.
2.8 Data Reconciliation
Run periodic jobs to verify that order counts match transaction amounts, enabling rapid manual intervention when inconsistencies appear.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
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.