用户表设计应避免TEXT存昵称等基础字段,改用VARCHAR;id用BIGINT UNSIGNED;phone/email加唯一索引且允许NULL;password_hash用CHAR(60);status用TINYINT;密码哈希须在应用层用bcrypt/Argon2实现;预留third_party_id等扩展字段;last_login_at等查询字段必须建索引。

用户表设计要避开 TEXT 存储基础字段
小型社交平台的用户表常误用 TEXT 类型存昵称、个性签名等——这会拖慢查询、影响索引效率,且 MySQL 对 TEXT 字段的排序和分组限制多。实际只需:昵称用 VARCHAR(32)(足够覆盖中文 16 字+英文),个性签名用 VARCHAR(255);头像路径、个人简介这类可变长但通常不参与 WHERE/ORDER BY 的字段,才考虑 TEXT。
关键点:
-
id必须是BIGINT UNSIGNED AUTO_INCREMENT,避免未来用户量破千万后溢出 -
phone和email要加唯一索引,但注意允许NULL(比如只用微信登录的用户可能没填邮箱) -
password_hash固定长度,用CHAR(60)(bcrypt 输出恒为 60 字符),别用VARCHAR - 状态字段如
status推荐用TINYINT UNSIGNED(0=禁用,1=正常,2=待验证),比字符串枚举更省内存、更快比较
密码不能明文存,但别自己实现加盐逻辑
看到有人用 MD5(CONCAT(password, salt)) 就直接上线,这是危险信号。MySQL 内置函数如 SHA2() 或 MD5() 是纯计算型,没有自适应迭代、无法抵抗 GPU 暴力破解。
正确做法是:在应用层(Python/Node.js/PHP)用标准库生成 bcrypt 或 Argon2 哈希,再存进数据库。例如 Python 的 bcrypt.hashpw() 输出自带盐值和参数,一条记录里全包含,数据库只负责安全存储,不参与加密过程。
常见错误:
- 把盐值单独存在另一张表或字段里——增加查询开销,且盐值泄露也不影响主哈希安全性
- 用
UUID()当盐——它不是密码学安全随机数,且每次调用都不同,导致无法验证 - 在 SQL 里写
INSERT ... VALUES (..., SHA2(CONCAT(@pwd, RAND()), 256))——RAND()每次执行结果不同,登录时根本没法复现
头像和第三方登录字段要预留扩展性
初期只支持手机号注册,但两周后就上了微信登录——如果当初 user 表没留 third_party_id 和 provider 字段,就得加字段、锁表、改所有 INSERT 逻辑。
千博企业网站管理系统主要面向大中型企业电子商务网站的构建与运营管理进行设计研发,拥有极为灵活的产品架构、极强的可扩展性与可伸缩性,可广泛适合于新闻资讯门户、企业内部知识门户、报社/杂志阅读、影音资讯、视频音频在线播放、法律顾问、政务公开、企业办公信息化等网络业务管理平台的建设,最大限度地满足客户现今乃至未来的应用需求。借助于千博企业网站管理系统极强的灵活性和便捷的可扩展性,企业级客户能够迅速流畅的
建议一步到位:
-
avatar_url用VARCHAR(512),存 CDN 地址(如https://cdn.example.com/avatars/u_123456.jpg),别存本地路径或二进制 blob -
provider用ENUM('phone', 'wechat', 'github', 'apple')或更灵活的VARCHAR(20),避免 ALTER TABLE 加新渠道 -
third_party_id用VARCHAR(255),微信 OpenID、GitHub ID、Apple Subject 长度差异大,CHAR(32)不够用 - 加联合唯一索引:
UNIQUE KEY uk_provider_uid (provider, third_party_id),防止同一微信用户重复绑定
查询活跃用户时别直接 SELECT * 扫全表
运营要看“最近 7 天登录过的用户”,如果写 SELECT * FROM user WHERE last_login_at > DATE_SUB(NOW(), INTERVAL 7 DAY),而 last_login_at 没索引,就会全表扫描——哪怕只有 10 万用户,延迟也可能飙到秒级。
必须做的两件事:
- 给
last_login_at加普通索引:ALTER TABLE user ADD INDEX idx_last_login (last_login_at) - 查活跃用户列表时,只 SELECT 必需字段,比如:
SELECT id, nickname, avatar_url, last_login_at FROM user WHERE ...,别带password_hash或bio这类大字段 - 如果还要按“粉丝数”排序,别临时
JOIN粉丝表算总数——提前用定时任务或触发器把follower_count缓存在用户表里
真实瓶颈往往不在数据量大小,而在字段冗余和索引缺失。一张 5 万行的 user 表,只要三个字段加了索引、两个大字段拆出去,QPS 就能从 50 跳到 800。









