CMS中草稿与正式文章必须物理分离为articles和article_drafts两张表,历史版本用BEFORE UPDATE触发器捕获快照,回滚应通过插入新发布记录+归档旧版实现。

草稿表必须与正式表物理分离,不能共用同一张表
直接在 articles 表里加 status 字段区分草稿/发布,会导致查询变慢、索引失效、权限控制困难。CMS 中草稿频繁增删改,而正式文章读多写少,混合存储会互相干扰。
正确做法是拆成两张表:
-
articles:只存已发布、status = 'published'的最终版本,主键id全局唯一,带全文索引和访问统计字段 -
article_drafts:专用于草稿,含author_id、title、content、updated_at、auto_save_count(避免覆盖冲突) - 两表之间不设外键,避免草稿删除时级联影响正式内容;关联靠业务层用
original_article_id(可为空)显式维护
多版本历史表要用 BEFORE UPDATE 触发器捕获变更前快照
仅靠 AFTER INSERT/UPDATE 记录新值,无法还原「改了什么」。CMS 编辑器常需对比差异、回滚某次修改,必须保存旧状态。
触发器写法关键点:
- 用
BEFORE UPDATE,确保在原记录被覆盖前读取OLD.* - 历史表字段应包含:
article_id、version(自增或时间戳+随机后缀)、title_before、content_before、updated_by、created_at - 避免在触发器里调用函数如
NOW()多次——MySQL 5.7+ 在同一语句中多次调用返回相同值,建议统一用NEW.updated_at或传参 - 不要把
content存为 TEXT 全量复制——大字段拖慢插入,可改用SUBSTRING(content, 1, 200)存摘要,或另建压缩日志表
查询某篇文章的最新草稿 + 当前发布版 + 历史版本,要避免 N+1 和全表扫描
用户打开编辑页时,前端通常需要一次性拉取:当前草稿(如果有)、最新发布版、最近 5 条历史变更。一次查三张表容易写成三次独立查询,或用 UNION 混淆语义。
推荐组合查询结构:
- 查草稿:
SELECT * FROM article_drafts WHERE author_id = ? AND original_article_id = ? ORDER BY updated_at DESC LIMIT 1 - 查发布版:
SELECT * FROM articles WHERE id = ? AND status = 'published'(注意加status索引) - 查历史:
SELECT * FROM article_versions WHERE article_id = ? ORDER BY version DESC LIMIT 5(article_id必须有索引) - 三者用应用层并发请求,比 JOIN 更可控;若强求单 SQL,可用
UNION ALL但需统一字段数和类型,并加source_type区分来源
版本回滚不是 UPDATE 原表,而是 INSERT 新发布记录 + 标记旧版过期
很多人回滚时直接 UPDATE articles SET title=?, content=? WHERE id=?,这会丢失本次发布前的状态,且无法审计「谁在何时回滚了哪一版」。
安全回滚流程应是原子操作:
- 从
article_versions查出目标版本的title和content - INSERT 一条新记录到
articles,status = 'published',version = (SELECT MAX(version) FROM articles WHERE article_id = ?) + 1 - UPDATE 原
articles记录的status = 'archived'(不是 DELETE),并记录archived_by和archived_at - 最后向
article_versions插入一条新记录,operation = 'rollback',关联新旧version
这种设计下,任意版本都可被多次回滚,所有操作留痕,且不影响线上缓存一致性——只需监听 articles 表的 status 变更即可刷新 CDN。


















