LOADING

加载过慢请开启缓存 浏览器默认开启

用户注册简单逻辑实现

2026/1/5 python 设计开发

用户注册功能的设计与实现

一、需求背景

实现用户两步式注册功能:

步骤1:填写注册信息(username, password, nickname, email),点击”下一步”发送验证码
步骤2:输入邮箱验证码,完成注册并自动登录

技术栈:Python + FastAPI + SQLAlchemy + Redis + PostgreSQL

二、核心问题

2.1 验证顺序问题

初版实现先验证邮箱验证码,再检查用户名/邮箱是否存在。问题在于验证码验证成功后会被删除,如果后续检查失败,用户需要重新获取验证码。

解决方案:调整验证顺序,先做轻量数据库查询,最后验证验证码。

# 优化后的顺序
1. 检查用户名是否已存在
2. 检查邮箱是否已注册
3. 验证邮箱验证码(确保前面都通过)
4. 创建用户

2.2 两步式注册的数据存储问题

第一步填写的注册信息需要临时存储,在第二步验证时使用。核心问题:

  1. 存储位置:前端还是后端?
  2. 并发冲突:不同邮箱抢注同一用户名如何处理?
  3. 数据过期:如何清理未完成的注册数据?

三、方案对比

3.1 方案 A:Redis Token 存储

数据结构

Key: register_pending:{register_token}
Value: {username, password, nickname, email}
Expiry: 10分钟

流程

  1. pre-register:检查用户名/邮箱 → 生成 token → 存 Redis → 发验证码 → 返回 token
  2. complete-register:根据 token 获取数据 → 验证码验证 → 创建用户

问题:不同邮箱可以同时预注册相同用户名,导致后完成的注册失败

3.2 方案 A+:双重索引

数据结构

register_pending:email:{email} = {username, password, nickname}
register_pending:username:{username} = {email}  # 索引

改进:在 pre-register 时检查用户名是否被其他邮箱占用

优点:彻底解决并发冲突
缺点:实现复杂,需维护索引一致性和过期

3.3 方案 C:简化存储 + 分布式锁(最终选择)

设计思路:体量较小,决定允许重复预注册,在创建用户时用分布式锁保证并发安全

数据结构

register_pending:{email} = {username, password, nickname}  # 10分钟过期
email_code:{email} = "123456"  # 5分钟过期
register_lock_username:{username}  # 分布式锁,10秒超时
register_lock_email:{email}  # 分布式锁,10秒超时

选择理由

  • 实现简单,不需要维护索引
  • 通过分布式锁保证并发安全
  • Redis 过期机制自动清理数据
  • 并发冲突概率低,用户体验可接受

四、技术实现

4.1 核心流程

pre_register

def pre_register(params):
    # 1. 检查数据库
    # 2. 存储到 Redis(以邮箱为key)
    # 3. 发送验证码

complete_register

def complete_register(params):
    # 1. 获取 Redis 中的注册信息
    # 2. 验证邮箱验证码
    # 3. 获取分布式锁
            # 4. 再次检查(防止并发)
            # 5. 创建用户
            # 6. 清理 Redis
            # 7. 自动登录

4.2 分布式锁设计

为什么需要双重锁?

场景:用户A和B同时用不同邮箱注册相同用户名

只锁邮箱:
  ├─ A获取email:test1@qq.com锁,B获取email:test2@qq.com锁
  ├─ A创建用户testuser,B创建用户testuser
  └─ 数据库报错 ❌

双重锁(用户名+邮箱):
  ├─ A获取username:testuser锁
  ├─ B尝试获取username:testuser锁 → 阻塞
  ├─ A创建用户 → 释放锁
  └─ B获取锁 → 检查 → 用户名已存在 ✅

锁参数

  • timeout=10:锁自动过期时间,防止死锁
  • blocking_timeout=3:获取锁的最大等待时间
  • 嵌套释放:确保锁按顺序释放

4.3 并发场景

场景 处理方式 结果
用户修改信息重新提交 同邮箱覆盖 Redis 数据 使用最新信息注册 ✅
不同邮箱抢注同一用户名 分布式锁串行化 后者检查失败 ✅
同一用户并发点击 第一次创建,第二次 Redis 已清理 RegisterExpiredException ✅

五、方案对比

维度 方案A(Token) 方案A+(双索引) 方案C(分布式锁)
实现复杂度
并发安全
用户体验
可维护性

选择方案C的原因:并发抢注概率低,方案C用较低的实现成本达到可接受的效果。

六、总结

  1. 验证顺序优化:先轻量级检查(数据库查询),后宝贵资源验证(验证码)
  2. 分布式锁应用:通过双重锁(用户名+邮箱)保证并发安全
  3. 双重检查机制:pre-register 检查一次,complete-register 再检查一次
  4. 自动清理:Redis 过期机制自动清理未完成的注册数据