分布式幂等性如何设计?
1 个回答
天一亮,时间就不属于我了。墨尘在高并发场景中,保证幂等性是非常重要的。幂等性是指无论操作被执行多少次,结果都是相同的。以下是一些常见的设计方案来确保分布式系统的幂等性: ### 解决方案 #### 1. 查询和删除操作 - **查询**:查询操作本身是读操作,没有幂等性问题。 - **删除**:第一次删除成功后,再次删除直接返回成功,不会重复执行。 #### 2. 唯一索引 - **唯一索引**:通过唯一索引或唯一组合索引来防止插入重复数据。例如,用户注册时使用邮箱作为唯一索引,确保同一邮箱只能注册一次。 ```sql CREATE TABLE users ( id INT PRIMARY KEY, email VARCHAR(255) UNIQUE, name VARCHAR(255) ); ``` #### 3. Token机制 - **Token机制**:前端在提交数据前向后端申请一个唯一的token,并将其附在请求中。后端校验token的唯一性,并在处理完成后删除token。 ```java import redis.clients.jedis.Jedis; public class TokenService { private static final Jedis jedis = new Jedis("localhost", 6379); public boolean isValidToken(String token) { Long result = jedis.del(token); return result == 1; } public String generateToken() { String token = "unique_token_" + System.currentTimeMillis(); jedis.setex(token, 600, "valid"); // 设置10分钟有效期 return token; } public static void main(String[] args) { TokenService tokenService = new TokenService(); String token = tokenService.generateToken(); System.out.println("Token: " + token); System.out.println("Is Valid: " + tokenService.isValidToken(token)); // true System.out.println("Is Valid Again: " + tokenService.isValidToken(token)); // false } } ``` #### 4. 悲观锁 - **悲观锁**:通过数据库的锁机制(如 `SELECT ... FOR UPDATE`)来防止并发修改。在事务中使用悲观锁会阻止其他事务对同一数据的修改。 ```sql START TRANSACTION; SELECT id, name FROM table_name WHERE id = '##' FOR UPDATE; /* 执行更新操作 */ COMMIT; ``` #### 5. 乐观锁 - **乐观锁**:在数据库表中增加一个 `version` 字段,每次更新时检查版本是否一致。若版本一致,则更新成功并将版本号加1;否则,更新失败。 ```sql UPDATE table_name SET name = 'new_name', version = version + 1 WHERE id = '##' AND version = current_version; ``` #### 6. 分布式锁 - **分布式锁**:使用 Redis 等分布式缓存系统实现分布式锁。以订单号为key,业务代码执行前获取锁,执行完成后释放锁。设置有效期以防锁长期不释放。 ```java import redis.clients.jedis.Jedis; public class DistributedLockService { private static final Jedis jedis = new Jedis("localhost", 6379); public boolean acquireLock(String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime); return "OK".equals(result); } public boolean releaseLock(String lockKey, String requestId) { if (requestId.equals(jedis.get(lockKey))) { jedis.del(lockKey); return true; } return false; } public static void main(String[] args) { DistributedLockService lockService = new DistributedLockService(); String lockKey = "order_lock"; String requestId = "unique_request_id"; int expireTime = 10000; // 10 seconds if (lockService.acquireLock(lockKey, requestId, expireTime)) { try { // 执行业务代码 System.out.println("Lock acquired and business code executed"); } finally { lockService.releaseLock(lockKey, requestId); } } else { System.out.println("Failed to acquire lock"); } } } ``` #### 7. 保底方案 - **保底方案**:在执行支付等操作前,先检查订单是否已存在。若存在则直接返回结果,若不存在则执行支付操作并记录订单。 ```java public class PaymentService { private static final Jedis jedis = new Jedis("localhost", 6379); public String processPayment(String orderId, double amount) { if (jedis.exists(orderId)) { return "Order already processed"; } else { // 执行支付操作 jedis.set(orderId, "processed"); return "Payment successful"; } } public static void main(String[] args) { PaymentService paymentService = new PaymentService(); String orderId = "order_12345"; double amount = 99.99; System.out.println(paymentService.processPayment(orderId, amount)); } } ``` ### 总结 1. **唯一索引**:防止重复插入。 2. **Token机制**:防止重复提交。 3. **悲观锁**:阻止并发修改。 4. **乐观锁**:通过版本号控制并发修改。 5. **分布式锁**:确保分布式环境下的操作唯一性。 6. **保底方案**:通过检查记录防止重复操作。发布于 2024-08-07 16:06 •IP属地 宁夏举报评论收藏
公众号进入网站体验更佳

