如何设计多版本内容管理

4,951 阅读6分钟

前言

内容平台通常有多版本管理的需求,试想如果底层只有一份完整的数据,一旦在后台修改了,作者本人还没准备好或还没审核,线上的用户就能看到,十分不安全。为了提升稳定性,减少误操作的带来的影响,需要支持多版本

本文基于博客场景介绍两种常见的多版本解决方案

影子表

当只需要维护两个版本,也就是线上态和草稿态(大多数业务的需求)时,影子表是一个常见的解决方案

所谓影子表,就是把业务实体线上版本和草稿版本的数据分别存到两张表,对博客来说就是blog表和blog_draft表,除了状态字段有区别,其他字段一样

image.png 区分两个状态,是因为草稿态有两个层次的状态

  1. 博客的总状态:第一次发布后不管后面怎么变,只要不下架,都是已发布状态
  2. 草稿版本的状态:第一次发布之后,如果再修改提交审核,当前版本的草稿就是审核中

为啥需要维护两个状态?考虑以下场景:

后台管理搜索总状态为已发布的博客中,title=XXX,按照更新时间降序排序

此时需要用到总状态,而草稿版本的状态则用于标识当前草稿数据的审核状态,

业务流程如下:

  • 新建博客:在两张表中分别初始化两条记录,id相同,用于唯一标识一篇博客。blog_draft.blog_status和blog.status为编辑,content_status也为编辑
  • 修改草稿:将修改的信息更新到blog_draft表
  • 提交审核:修改两张表的3个状态字段为审核中
  • 审核通过将blog_draft中的数据复制到blog表中,两张表的3个状态status字段都修改为审核通过
  • 上线后再修改提交:此时有点区别了,除了修改blog_draft的内容字段外,blog_draft.blog_status不变,还是已发布,但content_status变为审核中

影子表有以下优点:

  1. 符合需求:通过维护两张表,分别保存一份博客线上和草稿态的数据,能实现后台修改时不影响线上数据,当修改通过审核后再发布到线上
  2. 结构简单:后台管理只用修改,查询草稿表,前台展示只用请求线上表,只有第一次发布才会同时修改两张表(草稿表要修改状态),后续的发布只修改线上表

架构简单意味着开发快,好维护,出问题好排查

多版本

影子表虽然架构简单,但如果有进一步的需求则无法满足,例如需要保留每次发布线上版本的数据,方便合规审查,或用户自己查询历史记录等,或者需要回滚到指定历史版本等功能

影子表每次发布都把原来的数据覆盖,以前的数据就丢失了,自然是无法满足该需求

多版本方案将业务实体中:

  • 版本有关(每个版本可能不一样)的数据抽取成一张版本表blog_version,本例中为title,content,coverImage

    • 和影子表方案一样,每个版本有自己的content_status
  • 版本无关的字段放到主表blog,本例中为博客id,status,author

    • status为博客的总状态

image.png blog表每条记录代表一篇博客,online_version_id代表所有用户可见的版本,也就是线上版本,latest_version_id代表只有作者,审核可见的版本

业务流程

业务流程如下:

  • 新建博客:新建blog记录,status为草稿,新建blog_version记录,用于存放草稿态的数据,将其version_id关联到blog.latest_version_id
  • 修改草稿:从blog主表中根据draft_version_id找到对应的记录进行修改即可
  • 发布博客:该设计下的发布博客操作比较简单,修改blog.status为线上,将online_version_id赋值为draft_version_id的值即可,因为此时刚发布,线上态的数据就是草稿态的数据,可以共用一条记录
  • 发布后再修改:重点来了,此时不能直接修改latest_version_id对应的记录,因为会影响线上数据。若发现latest_version_id等于online_version_id,说明该博客发布后还没修改过,此时需要新建一条blog_version记录,用于存放最新版本的草稿数据,再将新记录的version_id放到blog.latest_version_id上,进行关联
  • 前后台展示:根据场景是前台还是后台,根据不同的version_id获取到各自版本有关的数据即可

image.png

如何搜索

目前的实现有个问题,如何进行搜索?

如果搜索条件和版本无关,例如搜索作者等于XXX,按照更新时间降序。这是只用在blog主表中进行搜索,若果搜索结果中需要一些版本有关的字段,例如简介,则再根据latest/online_version_id去博客版本表做一次in查询即可,速度很快

但当搜索条件同时包含了版本无关和版本有关的字段时,单表查询就不能满足要求了

例如搜索状态为草稿,标题为旅游,按照更新时间降序

此时有以下几个解决思路:

  • 接入es:和查询相关的功能都走es。如果遇到要搜索博客的内容,那没办法,必须要借助es这种搜索引擎来完成。若只是为了实现常规查询也不是不可以,相当于采用了CQRS架构,查询命令职责分离。但一般来说建议在满足业务需求的情况下,简单的筛选条件搜索尽量走数据库查询
  • 连表查询:业务数据量小时,可以采用此方案。但当数据量大了后,每次连表查询开销很大,且以后需要分库分表时很难改造,因此不推荐
  • 冗余字段:将需要作为查询条件的数据冗余到主表中,实现单表查询。本例中需要增加以下两个字段

image.png 冗余少量字段后,同时包含了版本无关和版本有关的字段的搜索就能走单表查询,查询性能和扩展性更好,推荐该方案

冗余字段方案的缺点为:当需要作为搜索的条件字段较多时,若同时冗余到主表,维护数据一致性比较麻烦,每次修改需要将这些字段都同步到主表

版本清理

当版本数据增多时,blog_version中未被blog引用的版本数据其实在常规业务中不需要了,若一直占着blog_version表的空间,可能会影响常规业务(查询/修改)的性能

可以定期扫描blog_version表,先将历史版本的数据备份到另一个地方,再物理删除

总结

本文介绍了设计多版本内容管理的常见思路,一般的场景用影子表足以胜任,但这样不会保留每次发布的历史记录,不支持用户查询历史版本,回滚到某个历史版本等需求

此时需要用到多版本方案,给主表添加冗余字段后,就能支持简单查询的单表搜索。版本表数据过多时可以定时清理,以提高查询性能