评论表主键必须用 BIGINT UNSIGNED,避免 ID 溢出;外键需加索引,推荐 post_id + created_at 联合索引;content 用 MEDIUMTEXT utf8mb4;软删除用 deleted_at 而非 is_deleted。

评论表必须带 comment_id 主键且用 BIGINT
MySQL 评论表高频插入、长期积累,ID 很快会溢出 INT UNSIGNED(上限约 42 亿),哪怕业务刚起步也该一步到位用 BIGINT UNSIGNED。别信“现在数据少无所谓”,后期改主键类型要锁表、重建索引,线上停写几小时不是玩笑。
常见错误是直接用 INT 或自增起始值设成 1——结果半年后发现 ID 快撞到 2147483647,再加 UNSIGNED 也救不回已用掉的负数空间。
-
comment_id设为BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY - 避免用 UUID 或雪花 ID 当主键:索引碎片高、JOIN 效率低、调试时肉眼难读
- 如果分库分表,主键仍建议本地自增 + 逻辑分片字段(如
post_id % 100),别强依赖全局唯一 ID 生成服务
外键关联必须加索引,且优先用 post_id + created_at 组合索引
查某篇文章下最新 20 条评论,SQL 通常是 SELECT * FROM comments WHERE post_id = ? ORDER BY created_at DESC LIMIT 20。没索引时 MySQL 只能全表扫描,100 万条评论可能扫几十秒。
post_id 单列索引不够——排序靠 created_at,而 MySQL 无法高效利用单列索引做 WHERE + ORDER BY 联合优化。
- 建联合索引:
INDEX idx_post_created (post_id, created_at) - 别把
user_id塞进这个索引:除非你常查“某个用户在某文章下的评论”,否则纯增加写开销 - 如果支持按用户查全部评论,单独建
INDEX idx_user_created (user_id, created_at)
content 字段别用 TEXT,用 MEDIUMTEXT + 显式字符集
用户发个带 5 张图的长评,Base64 图片一塞就超 65535 字节,TEXT 直接截断或报错 Packet too large。更坑的是默认字符集可能是 utf8mb3,存 emoji 就变问号。
MySQL 8.0+ 默认 utf8mb4,但旧表迁移常漏改,导致评论里 ? 变成 。
-
content类型用MEDIUMTEXT(最大 16MB),够撑图文混排 - 建表时显式指定:
content MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 千万别用
LONGTEXT:虽然更大,但 MySQL 内部处理更慢,且多数业务真用不到 4GB
软删除字段 deleted_at 比 is_deleted 更可靠
用 is_deleted TINYINT(1) 看似简单,但很快会遇到问题:想查“未删的热门评论”,SQL 写 WHERE is_deleted = 0 AND likes > 100,结果发现 is_deleted 没索引,执行计划走全表扫描。
加索引?那 is_deleted = 0 区间太大,索引区分度极低,效果差。而 deleted_at 是 NULL 或时间戳,可天然配合索引做范围查询。
- 字段定义:
deleted_at DATETIME NULL DEFAULT NULL - 查有效评论:
WHERE deleted_at IS NULL,配合INDEX idx_deleted_at (deleted_at)能快速定位 - 物理删除前先跑
UPDATE comments SET deleted_at = NOW() WHERE comment_id IN (...),留审计痕迹
真正麻烦的是级联逻辑——比如删除文章时,其下评论要不要同步软删?这得看业务:社区类通常保留,客服工单类则必须清空。没有银弹,得在应用层明确约定。


















