从一次笔记发布流程,看架构设计的业务思维
一、从一个真实场景说起
假设你正在做一个内容社区产品,用户可以在上面发图文笔记(类似小红书、微博)。
用户点击「发布」按钮后,后端做了这些事情:
- 上传资源:笔记里的图片 / 视频先上传到 OSS 对象存储,拿到资源的访问直链;
- 生成 ID:网关把发布请求转发给笔记服务,笔记服务向分布式 ID 服务要一个全局唯一 ID;
- 校验内容:判断笔记内容是否为空,不为空才继续;
- 存内容(KV):把笔记正文存到 Cassandra(KV 键值服务);
- 存元数据(MySQL):确认 KV 保存成功后,再把笔记元数据(ID、作者、时间、资源链接等)存到 MySQL;
- 返回成功:提示用户"发布成功"。
乍一看,这一套流程绕来绕去:又是 OSS、又是分布式 ID、又是两种数据库,为什么不直接用一台 MySQL 一个自增主键一把梭?
这篇博客想讲的不是某一步的具体技术,而是每一步背后的业务思维:为什么要这样设计?什么时候必须这样?什么时候可以简化?
二、流程总览
用户填写笔记(标题/内容/话题/图片/视频)
│
▼
① 图片/视频 → OSS 对象存储 → 拿到访问直链
│
▼
② 点击发布 → 网关 → 笔记服务
│
▼
③ 向分布式ID服务申请全局唯一ID
│
▼
④ 内容非空校验
│
▼
⑤ 笔记正文 → Cassandra(KV键值服务)
│
▼
⑥ KV确认成功 → 笔记元数据 → MySQL
│
▼
⑦ 提示用户:发布成功
拆开看,每个环节都在回答一个业务问题:这一步在替谁承担什么风险?
三、为什么先把图片 / 视频上传到 OSS?
业务痛点
笔记的核心是内容,而内容里最"重"的是图片和视频。如果把这些二进制大文件直接塞进业务接口、再交给数据库存,会有三个问题:
- 接口又慢又大:一张几 MB 的图片走应用服务器转发,请求体巨大,网关和应用都会被拖垮;
- 数据库爆掉:MySQL、Cassandra 擅长存结构化数据,不适合存大文件二进制,存储成本高、读写慢;
- 并发尖峰:用户上传图片的高峰期,大量二进制流量会让业务服务先挂,反而把最重要的"发布"功能拖死。
为什么这样设计
OSS 对象存储的定位就是"存大文件的仓库":海量、便宜、自带 CDN 加速、支持断点续传和分片上传。
让用户直连 OSS 上传(而不是经过程序员自己写的服务中转),本质是把"传输大文件"这件脏活累活从业务链路里剥离出去:
- 业务接口只收一个几字节的 URL 直链,轻量、快速、稳定;
- OSS 承担大流量,业务服务只在最后用 URL 引用资源;
- 用户上传体验更好(分片、秒传、进度条)。
什么时候这样做
只要内容里包含图片 / 视频等大文件,就应该走对象存储,这是几乎所有内容平台的标配。如果只是纯文本笔记(没有附件),这一步可以直接省掉。
四、为什么要分布式 ID,而不是数据库自增主键?
业务痛点
在单机时代,MySQL 自增主键够用。但内容平台几乎必然要走向分库分表、多服务实例、高并发:
- 笔记数据量大,MySQL 要分库分表,每个分片的自增主键是独立的,会产生重复 ID;
- 多服务实例并发生成 ID,无法用单点计数器保证唯一;
- 业务上还希望 ID 趋势递增(方便排序、分页),并且不泄露业务量(避免被竞争对手从 ID 推断出日活)。
为什么这样设计
分布式 ID 服务的本质,是"把唯一性、有序性、高性能从数据库里抽出来,单独成为一个稳定服务"。
这里用到的典型方案就是雪花算法(Snowflake):时间戳 + 机器 ID + 序列号拼成 64 位 ID,单机每毫秒可生成几千个,全局唯一且趋势递增,不依赖数据库。
什么时候这样做
判断标准很简单:
- 单库单表、量级不大 → 自增主键就够,别过度设计;
- 要分库分表 / 高并发 / 多服务写 → 必须引入分布式 ID;
- 中间态(分库还没做,但预判会做)→ 提前用分布式 ID,避免后面迁移主键的大坑。
五、为什么正文存 Cassandra,元数据存 MySQL?
这是整套流程里最反直觉的一步:一个业务动作,为什么拆到两个数据库?
业务痛点
- 正文数据"写多读多、结构简单":笔记正文本质是"一个 key(笔记 ID)对应一个 value(大段文本/富文本)",读写都是整块操作,不需要复杂的表关联和事务;
- 元数据"结构复杂、强一致":作者信息、发布时间、话题标签、状态字段,需要关联查询、需要事务保证、需要按各种条件检索;
- 两类数据的特点完全不同,硬塞进同一个数据库,会互相拖累。
为什么这样设计(关键业务思维)
这背后是一个重要的架构原则:不同特点的数据,用不同的存储。
- Cassandra(KV 键值服务)存正文:KV 模型天然适配"ID → 内容"的读写模式,水平扩展能力极强(加节点即可),写入吞吐高,非常适合正文这种海量、无关联、整存整取的数据;
- MySQL 存元数据:关系型数据库擅长结构化查询、索引、事务、关联统计,元数据(作者、时间、标签、状态)正好是它的主场。
先写 KV、再写 MySQL,还有一个关键意图:把"核心内容"和"索引元数据"解耦。
即使 MySQL 暂时不可用,笔记正文已经在 Cassandra 里落盘了,数据没有丢;等 MySQL 恢复再补元数据。这就是「核心资产优先落盘」的思维——先保住最不能丢的东西。
什么时候这样做
- 内容体量大、读写并发高、正文与元数据访问模式差异明显 → 冷热/结构分离,用 KV 存正文 + 关系库存元数据;
- 中小规模、数据量可控 → 一台 MySQL 全搞定即可,拆两个库反而增加运维复杂度。
六、为什么最后一步才提示"发布成功"?
业务痛点
如果"点完发布就立刻返回成功",而实际上数据库还没写完,用户刷新就看不到自己的笔记——体验崩塌。但如果"所有库都写完才返回成功",响应时间会被 Cassandra 和 MySQL 两次写入拖长。
为什么这样设计
流程里的顺序很有讲究:先落 KV 正文(核心内容),确认成功后再落 MySQL 元数据,最后返回成功。
这是在「一致性」和「体验」之间取的平衡:
- 保证用户看到"成功"时,最核心的正文一定已经持久化了;
- 元数据写入紧跟其后,整体依然是一套"同步写完后响应"的强一致链路,用户刷新必然能看到完整笔记;
- 相比"先把请求丢进消息队列异步处理、立刻返回成功",这种方案实现更简单、语义更清晰,适合笔记这类强一致要求 > 极致性能的场景。
什么时候这样做
- 要求"发完立刻可见"、数据不能丢 → 同步链路写完再响应(本流程);
- 允许"稍后可见"、追求极致吞吐(如点赞数、阅读量)→ 可以引入 MQ 异步 + 最终一致。
七、这套流程背后的业务思维总结
把所有步骤放到一起看,会发现每一步都在回答同一个问题:「这个系统最怕什么?怎么用最小的代价避免它?」
| 环节 | 最怕的问题 | 设计应对 |
|---|---|---|
| 大文件上传 | 大流量拖垮业务服务 | OSS 直连上传,接口只收 URL |
| 主键生成 | 分库后 ID 重复 | 分布式 ID 服务(雪花算法) |
| 正文存储 | 海量内容拖慢关系库 | Cassandra(KV) 存正文 |
| 元数据存储 | 复杂查询/事务无着落 | MySQL 存元数据 |
| 返回时机 | 数据没落盘就报成功 | 先 KV 后 MySQL 再响应 |
可以提炼成四个核心思维:
- 职责分离思维:大文件交给 OSS、唯一性交给 ID 服务、内容交给 KV、关系交给 MySQL——让每个组件只干自己最擅长的事;
- 核心资产优先思维:先落最不能丢的正文,再落可恢复的元数据,保住数据是底线;
- 按数据特点选存储思维:不要"一个数据库打天下",KV 存无关联大内容、关系库存结构化元数据,各取所长;
- 一致性与体验的平衡思维:同步写完再响应 = 强一致优先;异步 + 最终一致 = 性能优先。业务要什么,就选什么。
八、什么时候可以简化?
架构没有银弹,所有设计都是有代价的。上面这套流程适用于"大流量、海量数据、高并发"的内容平台。如果你的业务是以下情况,完全可以简化:
- 小体量内容社区 / 后台管理:一台 MySQL + 自增主键就够,别上 Cassandra 和分布式 ID;
- 纯文本、无大文件:省掉 OSS 环节;
- 单库单表、量级小:不需要分布式 ID,不需要双库分离;
- 允许异步发布:可以引入消息队列,把"写库"变成异步任务,进一步降低接口耗时。
判断标准就一条:你的业务最怕的问题,是不是上面表格里那些问题?
是,才需要对应的设计;不是,简化就是最好的架构。
九、总结
- 一次笔记发布,背后是 OSS + 分布式 ID + Cassandra + MySQL 四个组件的分工协作;
- 每一步设计都不是炫技,而是在回答"系统最怕什么、怎么防";
- 核心业务思维:职责分离、核心资产优先、按数据特点选存储、一致性与体验的平衡;
- 架构是为业务服务的,规模没到,就别背上不属于自己的复杂度——会简化,同样是架构能力。
看懂一条发布链路,比背十张架构图更有价值。因为链路里藏着的,是真正的业务思维。