评论表需建复合索引idx_post_time(post_id, created_at desc),mysql 8.0+支持降序索引避免filesort,5.7则依赖前导列优化排序;is_deleted用tinyint(1) default 0并强制where条件;内容字段按需选text或mediumtext;like_count冗余存储,点赞表加联合主键与索引;时间戳优先datetime+utc。

评论表必须带复合索引,否则查询会越来越慢
用户评论量一上来,SELECT * FROM comments WHERE post_id = ? ORDER BY created_at DESC LIMIT 20 就容易变慢。单纯在 post_id 或 created_at 上建单列索引效果有限。
实际应建联合索引:ALTER TABLE comments ADD INDEX idx_post_time (post_id, created_at DESC);
- MySQL 8.0+ 支持降序索引,
created_at DESC能让ORDER BY created_at DESC直接走索引,避免 filesort - 如果用的是 MySQL 5.7,去掉
DESC(它被忽略),靠post_id前导列 + 覆盖排序范围也能显著提速 - 别忘了给
user_id单独加索引——用于查某人发过哪些评论,或做用户维度统计
软删除字段要用 TINYINT(1) + 默认值,别用 NULL
评论被“删除”通常只是隐藏,不是真删。用 is_deleted TINYINT(1) DEFAULT 0 比 is_deleted ENUM('0','1') 或 is_deleted BOOLEAN 更稳妥,也比允许 NULL 更利于索引利用。
-
WHERE is_deleted = 0可走索引;WHERE is_deleted IS NOT NULL在某些版本下可能无法使用索引 - 查询时所有带条件的 SQL 都要显式加上
AND is_deleted = 0,漏掉就会暴露已删评论 - 如果业务需要“回收站”功能,可额外加
deleted_at DATETIME NULL,但注意它不能和is_deleted一起作为联合索引前导列(NULL 值影响索引选择性)
内容字段用 TEXT 还是 MEDIUMTEXT?看是否支持图片链接或长回复
纯文字评论,TEXT(最大 64KB)够用;但如果允许用户粘贴 Markdown、插入图片 URL、甚至内嵌短代码,建议直接上 MEDIUMTEXT(16MB)。
-
TEXT类型字段不会自动加索引,全文搜索需额外建FULLTEXT索引,且仅对 MyISAM 或 InnoDB(5.6+)有效 - 如果后续要支持关键词高亮或模糊搜索,别依赖
LIKE '%xxx%'—— 它无法用普通索引,应考虑MATCH(content) AGAINST('xxx' IN NATURAL LANGUAGE MODE) - 敏感词过滤、XSS 过滤必须在应用层做,数据库不负责内容清洗
点赞数不要实时 COUNT,用冗余字段 + 事务更新
每次查评论都 SELECT c.*, (SELECT COUNT(*) FROM comment_likes WHERE comment_id = c.id) AS like_count 是典型 N+1 和性能陷阱。
- 在
comments表里加like_count INT UNSIGNED DEFAULT 0字段,写入/取消点赞时用UPDATE comments SET like_count = like_count + 1 WHERE id = ? - 点赞记录单独存
comment_likes(user_id, comment_id, created_at),联合主键防重复,再加INDEX idx_comment (comment_id)支持反查谁点过 - 如果点赞量极大(比如单条评论百万赞),
like_count字段可能成为热点行,要考虑分库分表或改用 Redis 计数后定时落库
真实场景里最容易被忽略的是:评论时间戳用 DATETIME 还是 TIMESTAMP?前者无时区转换、范围大(1000–9999),后者自动转时区但范围窄(1970–2038),且受 MySQL 时区配置影响。业务若跨多时区,优先选 DATETIME 并在应用层统一用 UTC 存储。










