Flash Sale System Design and Optimization
This article outlines common flash‑sale scenarios, identifies key concerns such as overselling, anti‑fraud, and user experience, and progressively presents system designs from a basic SpringBoot‑MyBatis implementation to pessimistic and optimistic locking, rate‑limiting, token‑bucket algorithms, and various performance and security optimizations.
1. Flash Sale Scenarios
Typical flash‑sale use cases include train ticket reservation on 12306, purchasing expensive liquor, concert tickets, and large‑scale events like Double‑Eleven. The main concerns are strict prevention of overselling, protection against malicious bots, and maintaining a good user experience under high concurrency.
Key Focus Points Strictly prevent overselling: Selling more items than inventory is the most critical problem. Prevent black‑market exploitation: Stop malicious users from abusing the system. Guarantee user experience: Provide a friendly shopping experience even under high QPS.
Below we refine the flash‑sale system according to these focus points.
2. Version 1 – Bare‑metal (SpringBoot + MyBatis)
A straightforward implementation using SpringBoot and MyBatis follows these steps:
Controller receives the flash‑sale request and calls the Service layer.
Service checks whether the sold count matches the inventory; if not, it increments the sold count via the DAO.
DAO updates the sold count and creates an order using MyBatis.
Testing with Postman works, but under concurrent load (e.g., JMeter) the order count can exceed inventory, leading to overselling.
Example of concurrent conflict:
User A reads inventory 64, reduces it to 63, and creates an order.
User B reads the same inventory 64 before A’s update, also reduces it to 63 and creates another order.
The inventory drops by one while two orders are created, causing oversell.
3. Version 2 – Pessimistic Lock
To avoid overselling, a synchronized block (or synchronized) is added around the Service call, ensuring only one thread can modify the inventory at a time. This eliminates oversell but severely limits concurrency because only one request obtains the lock.
@Transactional
@Service
@Transactional
@Slf4j
public class OrderServiceImpl implements OrderService {
// Validate stock
Stock stock = checkStock(id);
// Update stock
updateSale(stock);
// Create order
return createOrder(stock);
}When using Spring’s @Transactional for pessimistic locking, keep in mind two important points:
Place MySQL statements as late as possible in the method because the transaction commits after the first MySQL operation.
Set a reasonable transaction timeout; the default is unlimited (-1).
Note: Pessimistic locking can cause threads to block, leading to poor user experience.
4. Version 3 – Optimistic Lock
Each stock record carries a version number. The Service reads the current sold count and version, then attempts an atomic update that increments both the sold count and version. MySQL’s optimistic concurrency control ensures that only one update succeeds, preventing oversell while allowing higher concurrency.
UPDATE stock_table SET sold = sold + 1, version = version + 1
WHERE id = #{id} AND version = #{version}Note: Because success is probabilistic, some items may remain unsold after the event.
5. Version 4 – Rate Limiting
After solving oversell, the system adds traffic‑shaping mechanisms. Common algorithms include the leaky bucket and token bucket (e.g., Guava’s RateLimiter). Three essential protection tools are caching, degradation, and rate limiting.
Cache : Improves response speed and capacity.
Degradation : Reduces non‑essential services under heavy load.
Rate limiting : Controls request rate per time window, rejecting or queuing excess traffic.
5.1 Leaky Bucket
The leaky bucket treats incoming requests as water poured into a bucket with a fixed outflow rate; excess water overflows (requests are rejected).
5.2 Token Bucket
Tokens are added to a bucket at a steady rate; a request proceeds only if it can take a token. Guava’s RateLimiter implements this algorithm.
private RateLimiter rateLimiter = RateLimiter.create(20);
// Block until a token is available
rateLimiter.acquire();
// Try to acquire a token within 3 seconds
rateLimiter.tryAcquire(3, TimeUnit.SECONDS);6. Version 5 – Detail Optimizations
Further refinements include:
Time‑limited flash sale : Store the sale key in Redis with an expiration (e.g., 180 seconds).
127.0.0.1:6379> set kill1 1 EX 180
OKFlash‑sale interface hiding : Users must first obtain an MD5 token via a getMd5 API; the token is stored in Redis and validated before the actual purchase request.
public String getMd5(Integer id, Integer userId) {
// Validate user and product
User user = userDAO.findById(userId);
if (user == null) throw new RuntimeException("User not found!");
Stock stock = stockDAO.checkStock(id);
if (stock == null) throw new RuntimeException("Invalid product!");
String hashKey = "KEY_" + userId + "_" + id;
String key = DigestUtils.md5DigestAsHex((userId + id + "!AW#").getBytes());
stringRedisTemplate.opsForValue().set(hashKey, key, 3600, TimeUnit.SECONDS);
return key;
}Access‑frequency limiting : Record each user’s request count in Redis with an expiration; reject requests that exceed a threshold.
// Create token bucket instance
private RateLimiter rateLimiter = RateLimiter.create(20);
// Block until token is acquired
rateLimiter.acquire();
// Try to acquire token within 3 seconds
rateLimiter.tryAcquire(3, TimeUnit.SECONDS);7. Version 6 – Additional Optimizations
CDN acceleration for static assets.
Gray‑out flash‑sale button before the start time and limit click frequency during the sale.
Nginx load balancing to handle tens of thousands of QPS.
Store hot data in Redis; use Lua scripts for atomic operations.
Message‑queue based traffic smoothing (RabbitMQ, Kafka) to decouple order creation from the flash‑sale request.
Short‑URL mapping for hidden endpoints.
Industrial‑grade flash‑sale architecture involving MQ, SpringBoot, Redis, Dubbo, Zookeeper, Maven, Lua, etc.
8. References
Bilibili video: https://b23.tv/IsifGk
GitHub project: https://github.com/qiurunze123/miaosha
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.
Wukong Talks Architecture
Explaining distributed systems and architecture through stories. Author of the "JVM Performance Tuning in Practice" column, open-source author of "Spring Cloud in Practice PassJava", and independently developed a PMP practice quiz mini-program.
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.
