How to Prevent Duplicate Order Submissions with Token + Redis Lock in Spring Boot
This article explains why duplicate order submissions occur in e‑commerce systems and presents a comprehensive solution using request tokens, interface idempotency, and Redis‑based distributed locks, complete with Spring Boot code examples and advanced optimization tips.
Background: Why duplicate submissions happen?
In e‑commerce platforms, submitting an order triggers inventory deduction, coupon redemption, payment processing, and message sending. Users often click the submit button twice, refresh a lagging page, or use the browser back button, which leads to repeated order submissions that waste resources and can cause business loss.
Common Scenarios of Duplicate Submissions
Core Principles to Prevent Duplicate Submissions
Address the issue through three aspects: interface idempotency, request uniqueness, and server‑side lock control.
Control a unique request identifier (token/nonce).
Make order operations idempotent.
Introduce cache or distributed lock to limit repeated submissions.
Solution: Token + Redis Lock Design
Design Idea
Frontend obtains a unique token from the server before creating an order.
The token is sent together with the order request.
Backend verifies the token’s existence in Redis.
If verification succeeds, the order logic runs and the token is deleted.
If the token has already been used, the request is rejected as a duplicate.
Code Implementation (Spring Boot + Redis)
1. Frontend token acquisition endpoint
@RestController
@RequestMapping("/api/order")
public class OrderTokenController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping("/token")
public ResponseEntity<String> getToken() {
String token = UUID.randomUUID().toString();
String key = "order:token:" + token;
// Set validity 5 minutes
redisTemplate.opsForValue().set(key, "valid", Duration.ofMinutes(5));
return ResponseEntity.ok(token);
}
}2. Order submission endpoint (validate & delete token)
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/submit")
public ResponseEntity<String> submitOrder(@RequestBody OrderRequest request,
@RequestHeader("X-Order-Token") String token) {
boolean success = orderService.submitOrder(request, token);
if (success) {
return ResponseEntity.ok("订单提交成功");
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("请勿重复提交订单");
}
}
}3. OrderService with duplicate‑submission logic
@Service
public class OrderService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* Submit order interface, prevent duplicate submissions
*/
public boolean submitOrder(OrderRequest request, String token) {
String redisKey = "order:token:" + token;
// Atomic delete + check guarantees idempotency
Boolean result = redisTemplate.delete(redisKey);
if (Boolean.TRUE.equals(result)) {
// First submission: token existed and was deleted
createOrder(request);
return true;
} else {
// Token missing → duplicate submission
return false;
}
}
private void createOrder(OrderRequest request) {
// Business logic: generate order number, check stock, deduct stock, persist, send MQ, etc.
System.out.println("处理订单:" + request);
}
}4. OrderRequest DTO
@Data
public class OrderRequest {
private Long userId;
private List<Long> productIds;
private BigDecimal totalAmount;
}Advanced Optimization Suggestions
1. Use Lua script for atomic Redis operations
Redis delete is not strongly atomic; a Lua script can perform “check‑and‑delete” in a single step.
// Lua script for atomic delete
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), "valid");2. Apply rate‑limiting or circuit‑breaker (e.g., Sentinel) to the order API
Prevent malicious request bursts.
Reduce system pressure caused by duplicate submissions.
3. Database‑level idempotent checks
Even if the application layer fails, unique constraints or INSERT IGNORE / ON DUPLICATE KEY can prevent duplicate rows.
Conclusion
To handle duplicate order submissions, rely on request uniqueness, interface idempotency, and server‑side lock mechanisms. Redis is an effective tool for idempotent control, and a simple token strategy works well for ordering, payment, and flash‑sale scenarios. Multi‑layer defense (application + database) provides the safest protection.
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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.
