项目中不用redis分布式锁,怎么防止用户重复提交?

1. 前端防抖与按钮禁用(辅助手段)

  • 原理:通过JavaScript限制用户频繁点击,如提交后禁用按钮或添加防抖延迟。

  • 优点:简单易实现,减少无效请求。

  • 缺点:无法防止绕过浏览器的请求(如API工具直接调用)。

  • 示例

    javascript 复制代码
    let isSubmitting = false;
    function submitForm() {
      if (isSubmitting) return;
      isSubmitting = true;
      // 提交逻辑...
    }

2. 令牌机制(Token)

  • 原理:页面加载时生成唯一Token,提交时携带Token,服务端校验后删除或标记为已使用。
  • 适用场景:表单提交、防CSRF攻击。
  • 实现
    • 生成Token:服务端在渲染页面时生成Token并存储(Session/DB)。
    • 校验Token:提交时验证Token是否存在,存在则处理并删除,否则拒绝。
  • 注意:分布式环境下需共享Token存储(如数据库)。

3. 数据库唯一约束

  • 原理:利用数据库唯一索引防止重复数据插入。

  • 适用场景:创建具有唯一标识的业务数据(如订单号)。

  • 示例

    sql 复制代码
    CREATE TABLE orders (
      order_id VARCHAR(64) PRIMARY KEY,
      user_id INT,
      ...
    );
    • 插入前检查order_id是否存在,或依赖数据库报错处理。

4. 幂等性设计

  • 原理:同一请求多次执行结果一致,需客户端传递唯一业务ID(如订单ID)。
  • 实现
    1. 客户端生成唯一ID(如UUID)并随请求发送。
    2. 服务端检查该ID是否已处理:
      • 已处理:返回之前的处理结果。
      • 未处理:执行业务并记录ID。
  • 优点:适用于API接口,天然支持分布式环境。

5. 请求参数哈希去重

  • 原理:对用户ID、操作类型、参数等生成唯一哈希值,短时内拒绝相同哈希请求。

  • 实现

    • 服务端维护已处理请求的哈希集合(如内存缓存或数据库)。
    • 设置合理的过期时间(如5秒),避免内存泄漏。
  • 示例

    java 复制代码
    String hash = MD5(userId + operationType + params);
    if (cache.exists(hash)) throw new RepeatException();
    cache.set(hash, 5); // 5秒过期

6. 乐观锁(针对更新操作)

  • 原理:通过版本号或时间戳控制并发更新。

  • 适用场景:防止数据并发修改(如库存扣减)。

  • 实现

    sql 复制代码
    UPDATE products 
    SET stock = stock - 1, version = version + 1 
    WHERE id = 100 AND version = 5;
    • 检查影响行数,若为0则说明数据已变更,拒绝操作。

7. 限流与频率控制

  • 原理:限制同一用户/IP在单位时间内的请求次数。

  • 工具

    • 单机:Guava RateLimiter。
    • 分布式:数据库滑动窗口计数或令牌桶算法。
  • 示例 (滑动窗口):

    sql 复制代码
    SELECT COUNT(*) FROM requests 
    WHERE user_id = 123 AND time > NOW() - INTERVAL 5 SECOND;

8. POST/REDIRECT/GET 模式

  • 原理:提交后重定向到结果页,防止浏览器刷新重复提交。
  • 适用场景:传统Web应用,非API场景。
  • 流程
    1. 用户提交POST请求。
    2. 服务端处理完成后返回302重定向到结果页(GET请求)。
    3. 用户刷新页面仅重复GET请求,不会重复提交数据。

方案选择建议

  • 简单场景:令牌机制 + 前端防抖。
  • 高并发业务:幂等性设计 + 数据库唯一约束。
  • 更新操作:乐观锁。
  • API接口:强制客户端传递唯一请求ID实现幂等性。
相关推荐
宸津-代码粉碎机8 分钟前
Spring AI企业级实战|从RAG优化到Agent多工具调度
java·大数据·人工智能·后端·python·spring
吴佳浩11 分钟前
AI Infra 的真相:Go 没输,rust也不是取代
后端·rust·go
喵个咪27 分钟前
实时游戏网络协议深度对比:KCP vs WebRTC vs WebSocket
后端·websocket·webrtc
普通网友29 分钟前
springboot之集成Elasticsearch
spring boot·后端·elasticsearch
QuZero35 分钟前
Guava Cache Deep Dive
java·后端·算法·guava
leeyi1 小时前
SSE 实时推流 —— Token 怎么一个个蹦出来
后端·agent
leeyi1 小时前
ReAct 循环的 50 行 Go 实现,逐行拆解
后端·agent
leeyi1 小时前
HITL:让人类随时叫停 AI,并且能优雅地继续
后端·agent
用户34232323763171 小时前
采集网关的离线缓存与断点续传——当网络不可靠时,数据一条都不能丢
后端
用户916842202742 小时前
Spring Boot application.yml 最全避坑与多环境配置
java·后端