用 MySQL 实现消息通知系统,关键不在堆字段,而在理清业务路径和查询重心。核心是让“查未读”快、“标已读”稳、“按时间看最新”准——这三点决定了表结构和索引怎么搭。
消息表必须包含的字段
一张轻量但可扩展的通知表,至少要有这些字段:
id:BIGINT 自增主键,避免 INT 溢出,适配千万级用户场景; receiver_id:接收者用户 ID,非空,用于 WHERE receiver_id = ? 查询; sender_id(可选):记录来源,私信或互动类通知需要; type:TINYINT 类型,如 1=系统公告、2=评论提醒、3=点赞通知,方便分类筛选; content:VARCHAR(2048) 足够存短文本;富文本或长内容建议抽离到关联表; is_read:TINYINT(1),0 表示未读,1 表示已读,支持 COUNT(*) WHERE is_read = 0 快速统计; created_at:TIMESTAMP DEFAULT CURRENT_TIMESTAMP,精确到秒,自动写入,时区友好; updated_at:TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,记录状态变更时间(比如标记已读)。必须加的联合索引
没有索引,查未读、分页、按类型过滤都会变慢。重点建这两个:
(receiver_id, is_read, created_at):覆盖最常用查询,例如「查某用户所有未读消息,按时间倒序」; (receiver_id, created_at):支撑「拉取最新 20 条」或「分页加载历史消息」;注意:不要只对 receiver_id 单独建索引——MySQL 在范围查询(如 is_read = 0)后无法高效利用后续字段,联合顺序很关键。
区分单发与群发,用两张表更清晰
系统公告、活动推送这类群发消息,和点对点私信逻辑不同,混在一张表里容易拖慢查询、增加冗余。推荐拆开:
mass_messages:存群发模板,字段含 title、content、status(草稿/已发)、publish_time; user_notifications:存每个用户实际收到的记录,含 user_id、mass_msg_id、is_read、receive_time;发送时用 INSERT … SELECT 批量写入:
INSERT INTO user_notifications (user_id, mass_msg_id, is_read, receive_time)
SELECT id, 123, 0, NOW() FROM users WHERE status = 'active';
时间处理别忽略时区和精度
TIMESTAMP 是首选,不是因为“看起来新”,而是它原生支持:
自动转时区:写入时按连接时区转为 UTC 存储,查询时再转回本地,用户看到的时间一致; 空间省:4 字节 vs DATETIME 的 5–8 字节; 微秒支持(如需更高精度):用 TIMESTAMP(6),但多数通知场景秒级足够。避免用 INT 存时间戳——丢失时区语义,且无法直接用 DATE()、HOUR() 等函数做时间维度分析。
