短信验证码表应设计为:id 主键、phone 标准化手机号、code 加密存储、send_at 精确时间、used 标记使用状态、ip 和 scene 可选;建 (phone,used,send_at) 联合索引和 (send_at) 单索引;生产环境存哈希值;有效期 5 分钟动态计算;每日清理已用超 24 小时记录。

短信验证码通常需要短期存储、高频读写、自动过期,MySQL 表设计要兼顾安全性、查询效率和清理便利性。核心原则是:字段精简、索引合理、过期机制明确、避免明文存验证码(尤其生产环境)。
基础表结构设计(含关键字段)
以下是一个推荐的 sms_verify_code 表结构:
- id:BIGINT UNSIGNED AUTO_INCREMENT,主键,便于分页或日志追踪(非必须但建议)
- phone:VARCHAR(16),标准化手机号(如 +8613912345678 或 13912345678),不加索引前需统一格式
- code:CHAR(6) 或 VARCHAR(10),存实际验证码(测试 / 开发可明文;生产建议加密存储或仅存哈希)
- send_at:DATETIME(3) 或 TIMESTAMP(3),精确到毫秒,记录发送时间(用于计算有效期)
- used:TINYINT(1) DEFAULT 0,标记是否已被验证使用(防止重放)
- ip:VARCHAR(45),可选,记录请求 IP,辅助风控
- scene:VARCHAR(20),可选,标识用途(如 “login”, “register”, “reset_pwd”),方便多业务复用
必须添加的索引策略
没有合适索引,查验证码会变慢甚至拖垮数据库:
- 联合索引 (phone, used, send_at):覆盖最常用查询 ——“查该手机号未使用且未过期的最新码”
- 单独索引 (send_at):支撑定时任务快速找出所有过期记录(例如:WHERE send_at
- 避免对
code建索引 —— 无业务意义,还增加写开销
安全与生产注意事项
直接存明文验证码在 MySQL 中存在风险,尤其当数据库被脱库时:
- 开发 / 测试环境可暂存明文,但上线前必须改
- 生产环境建议:存储 code 的 SHA-256 哈希值(如
SHA2(CONCAT(code, salt, phone), 256)),验证时对用户输入做同样哈希比对 - 限制单手机号单位时间内的发送频次(应用层 + 缓存层控制,如 Redis 计数),MySQL 表不承担限流职责
- 验证码有效期统一设为 5 分钟,数据库不存 expire_at 字段,用
send_at + INTERVAL 5 MINUTE动态计算,省空间且不易出错
配套清理机制(别只建表不清理)
验证码表会持续增长,必须定期归档或清理:
- 每日凌晨执行:DELETE FROM sms_verify_code WHERE send_at
- 保留已使用记录 24 小时,便于审计;未使用超时记录可更早清理(如 30 分钟后)
- 大表场景下,考虑按月分区(如按 send_at RANGE 分区),提升删除效率