Designing a High‑Concurrency Flash Sale (秒杀) System: Key Techniques and Best Practices
This article explains how to design a flash‑sale system that can handle instant massive traffic by using page staticization, CDN acceleration, caching strategies, distributed locks, message‑queue async processing, rate limiting, and inventory management techniques such as pre‑deduction, optimistic locking, and Lua scripts.
Flash‑sale (秒杀) activities are a classic high‑concurrency interview scenario where a limited number of items are sold at a very low price, causing a sudden surge of requests at the sale moment.
Because the traffic spikes only for a few seconds, traditional architectures struggle; the article proposes six main mitigation areas: page staticization, CDN acceleration, caching, asynchronous MQ handling, rate limiting, and distributed locking.
Page staticization moves most of the activity page content to static files, reducing server load, while the actual purchase request is only sent when the user clicks the flash‑sale button at the exact time.
CDN distributes static assets to edge nodes, allowing users nationwide to fetch the page quickly and reducing network congestion.
The flash‑sale button is initially disabled (grey) and becomes clickable only at the sale moment; JavaScript controls this state, and the JS file is cached on CDN with a random parameter to force refresh when the sale starts.
In the read‑many/write‑few pattern, the system first checks the cache (e.g., Redis) for stock; if missing, it falls back to the database, then warms the cache. This avoids overwhelming the DB during the spike.
Cache problems such as cache breakdown and cache penetration are addressed by using distributed locks, pre‑warming the cache, and employing Bloom filters to filter non‑existent product IDs.
Inventory management covers pre‑deduction and rollback concepts. Database stock deduction can use optimistic locking (e.g., update product set stock=stock-1 where id=? and stock>0), while Redis can atomically decrement stock via incrby or Lua scripts to ensure consistency.
Example Lua script for atomic stock deduction:
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;");Distributed locks are illustrated with Redis setNx, the atomic SET key value NX PX expire, lock release using a requestId check, spin‑lock retry loops, and Redisson as a higher‑level solution.
MQ asynchronous processing separates order creation from the flash‑sale request. The article discusses handling message loss (using a send‑log table and retry jobs), duplicate consumption (idempotent processing tables), garbage messages (retry limits), and delayed consumption for order timeout (using delayed queues such as RocketMQ).
Rate limiting strategies include Nginx‑based limits, Redis token‑bucket limits, per‑user, per‑IP, per‑API limits, captchas (including sliding‑puzzle captchas), and business‑level gating (e.g., member‑only participation) to protect the system from abusive bots.
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.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.
