这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
关键决策
设计决策回顾
在整个系统的演进过程中,我们做了很多关键的技术决策。
每个决策都有其背景、权衡和结果。
决策 1:数据库 vs Redis
问题
最开始应该用什么存储计数?
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 数据库 | 实现简单,数据可靠,事务支持 | 性能差,扩展性差 | 小规模,低并发 |
| Redis | 性极高,原子操作,扩展性好 | 内存存储,需持久化 | 大规模,高并发 |
决策
初期:使用数据库
- 用户量 < 1000
- 日均点赞 < 100
- 数据库完全够用
增长后:引入 Redis
- 用户量 > 10000
- 日均点赞 > 5000
- 数据库性能瓶颈
最终:Redis + 数据库
- Redis 处理读写
- 数据库持久化
- 各司其职
经验教训
1. 不要过早优化
- 先用最简单的方案
- 遇到问题再优化
- 避免过度设计
2. 技术选型要匹配业务阶段
- 初期:简单优先
- 增长期:性能优先
- 成熟期:稳定优先
3. 保持架构可演进
- 预留优化空间
- 设计替换接口
- 支持灰度发布
决策 2:强一致 vs 最终一致
问题
计数系统需要多强的一致性?
方案对比
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 强一致 | 立即一致 | 低 | 高 | 金融交易 |
| 最终一致 | 短暂延迟 | 高 | 中 | 社交应用 |
决策
点赞场景分析:
实时性要求:
- 用户点赞后,看到计数 +1
- 允许短暂延迟(1-2 秒)
- 不影响用户体验
决策:最终一致
原因:
1. 性能要求高
2. 用户可容忍延迟
3. 实现相对简单
4. 成本较低
实现方案
1. 更新 Redis(立即返回)
2. 异步写数据库
3. 定期对账修正
延迟时间:< 1 秒
修正频率:每小时
不一致率:< 0.1%
经验教训
1. 一致性不是非黑即白
- 不同场景有不同需求
- 权衡成本和收益
- 选择合适的一致性级别
2. 设计对账机制
- 即使是最终一致
- 也要保证最终会一致
- 对账是最后的防线
3. 监控数据一致性
- 实时监控不一致率
- 超标告警
- 及时修复
决策 3:单机 vs 分布式
问题
什么时候需要分布式?
方案对比
| 方案 | 复杂度 | 成本 | 性能 | 可用性 |
|---|---|---|---|---|
| 单机 | 低 | 低 | 中 | 低(单点故障) |
| 分布式 | 高 | 高 | 高 | 高 |
决策
演进路径:
阶段 1:单机部署
- 用户量 < 10,000
- 单台服务器足够
- 运维简单
阶段 2:主从部署
- 用户量:10,000 ~ 100,000
- 数据库主从
- Redis 主从
- 提高可用性
阶段 3:分布式部署
- 用户量 > 100,000
- 应用服务器集群
- Redis 集群
- 数据库集群
- 高可用
经验教训
1. 分布式不是银弹
- 增加复杂度
- 带来新问题
- 不要为了分布而分布
2. 分阶段演进
- 先解决当前问题
- 为未来预留空间
- 避免过度设计
3. 监控分布式系统
- 监控节点健康
- 监控网络延迟
- 监控数据一致性
决策 4:同步 vs 异步
问题
什么时候用同步,什么时候用异步?
方案对比
| 方案 | 延迟 | 一致性 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 同步 | 低 | 强一致 | 低 | 关键操作 |
| 异步 | 高 | 最终一致 | 高 | 高并发操作 |
决策
同步操作:
- 点赞/取消点赞
- 用户查询
- 排行榜更新
异步操作:
- 数据库写入
- 数据统计
- 通知发送
- Feed 流更新
原因:
1. 提升用户体验(响应快)
2. 降低系统压力
3. 提高吞吐量
经验教训
1. 核心链路同步
- 用户直接感知的操作
- 不能异步化
2. 非核心链路异步
- 统计、分析、通知
- 可以异步化
3. 异步要可靠
- 消息队列持久化
- 重试机制
- 死信队列
决策 5:缓存策略
问题
如何选择缓存策略?
方案对比
| 策略 | 命中率 | 一致性 | 内存占用 |
|---|---|---|---|
| Cache Aside | 中 | 中 | 低 |
| Write Through | 高 | 高 | 中 |
| Write Behind | 低 | 低 | 高 |
决策
点赞计数场景:
读多写少:
- 查询频率 >> 写入频率
- 适合 Cache Aside
写入频繁:
- 点赞操作频繁
- 需要异步写入
决策:Cache Aside + Write Behind
1. 读:Cache Aside(先查缓存,未命中查数据库)
2. 写:Write Behind(先写缓存,异步写数据库)
经验教训
1. 缓存要有 TTL
- 防止冷数据
- 定期刷新
- 合理设置过期时间
2. 缓存要监控
- 监控命中率
- 监控失效时间
- 优化缓存策略
3. 缓存要有降级
- 缓存故障时降级到数据库
- 保证可用性
决策 6:分片 vs 不分片
问题
什么时候需要分片?
方案对比
| 方案 | 复杂度 | 性能 | 扩展性 |
|---|---|---|---|
| 不分片 | 低 | 中 | 差 |
| 分片 | 高 | 高 | 好 |
决策
不分片:
- 单个 key 竞争不大
- Redis QPS < 5000
分片:
- 爆款文章
- 单 key 竞争大
- Redis QPS > 5000
分片数:
- 10 个分片(初期)
- 50 个分片(增长期)
- 100 个分片(成熟期)
经验教训
1. 先监控再分片
- 确认是瓶颈
- 分析热点 key
- 避免过早分片
2. 分片数要适中
- 太少:效果不明显
- 太多:管理复杂
- 建议:10-100 个
3. 支持动态分片
- 根据负载调整
- 自动扩缩容
决策总结
关键决策清单
✅ 已做对的决策:
1. 从简单开始
- 初期用数据库
- 增长后引入 Redis
- 逐步优化
2. 选择最终一致
- 性能优先
- 对账保证一致
- 用户可接受
3. 分阶段演进
- 不追求一步到位
- 根据问题优化
- 保持架构灵活
4. 监控驱动
- 数据说话
- 问题导向
- 及时调整
❌ 可以改进的决策:
1. 早期未考虑 Feed 流
- 后期才补充
- 应该提前规划
2. 分片数固定
- 应该动态调整
- 根据负载变化
3. 监控不够完善
- 应该更细粒度
- 及时发现问题
课后练习
练习 1
如果在项目初期就引入所有技术(Redis、分布式锁、分片等),会有什么问题?
参考答案(3 个标签)
系统设计过度设计架构
问题分析:
过度设计的问题:
1. 开发周期长
- 需要实现各种复杂功能
- 测试成本高
- 延误上线时间
2. 维护成本高
- 需要专业知识
- 问题难以排查
- 团队学习成本高
3. 资源浪费
- 小用户量用分布式
- 服务器资源闲置
- 成本增加
4. 复杂度高
- 出问题难定位
- 修改影响面大
- 团队协作困难正确做法:
MVP 原则(最小可行产品):
1. 先用最简单的方案
2. 快速上线验证
3. 收集真实数据
4. 根据数据优化
优化时机:
- 确实是瓶颈
- 有数据支撑
- 成本收益合理练习 2
如何评估”是否需要引入某个技术”?
参考答案(3 个标签)
技术选型决策方法架构
评估框架:
决策矩阵:
技术引入决策矩阵:
因素 | 权重 | 评分 (1-5) |
|------|------|-----------|
| 当前问题严重性 | 高 | ? |
| 技术成熟度 | 中 | ? |
| 团队能力 | 高 | ? |
| 时间紧迫度 | 中 | ? |
| 资源可用性 | 中 | ? |
总分 > 15:建议引入
总分 < 10:暂不引入练习 3
如何处理”技术债务”?
参考答案(3 个标签)
技术债务重构架构演进
技术债务管理:
什么是技术债务?
技术债务:
- 为了快速上线,选择了简单但不完美的方案
- 欠下的技术债需要偿还
债务分类:
1. 代码质量债务
- 代码混乱
- 缺乏测试
- 文档缺失
2. 架构债务
- 设计不合理
- 扩展性差
- 可维护性差
3. 性能债务
- 有性能瓶颈
- 未优化查询
- 资源浪费
4. 安全债务
- 有安全隐患
- 未做鉴权
- 数据未加密偿还策略:
重构时机:
1. 小步重构
- 每次重构小范围
- 频繁重构
- 持续改进
2. 重构红线
- 不影响功能
- 保持测试通过
- 代码审查
3. 重构优先级
- 高影响高成本低:优先
- 高影响高成本:评估
- 低影响低成本低:暂缓练习 4
如何在”功能完整性”和”开发速度”之间平衡?
参考答案(3 个标签)
MVP敏捷开发产品管理
平衡策略:
MVP(最小可行产品)原则:
第一版(MVP):
核心功能:
- ✅ 点赞功能
- ✅ 点赞计数
- ✅ 基本查询
暂缓功能:
- ❌ 排行榜
- ❌ Feed 流
- ❌ 高级统计
第二版(增长):
- ✅ 排行榜
- ✅ Feed 流
- ❌ 实时通知
第三版(优化):
- ✅ 实时通知
- ✌ 高级统计
- ✅ 数据分析决策标准:
功能优先级评估:
P0(必须有):
- 核心业务功能
- 用户最需要的
- 竞争对手都有
P1(应该有):
- 提升体验的功能
- 用户期望的功能
- 实现成本不高
P2(可以有):
- 锦上添花的功能
- 实现成本高的功能
- ROI 不明确练习 5
如何在”性能”和”可维护性”之间平衡?
参考答案(3 个标签)
代码质量性能优化可维护性
平衡策略:
性能 vs 可维护性:
场景 1:简单但慢
代码:简单易懂
性能:差
→ 优化:增加缓存
场景 2:复杂但快
代码:复杂难懂
性能:好
→ 优化:添加注释,提取函数
场景 3:简单且快
代码:简单易懂
性能:好
→ 保持:这是理想状态
场景 4:复杂且慢
代码:复杂难懂
性能:差
→ 重构:简化并优化代码质量标准:
思考题
-
如果团队技术能力有限,应该如何调整架构设计?
-
如何在”快速上线”和”架构优雅”之间找到平衡?
-
技术选型时,应该参考哪些信息?
💡 提示:这些问题没有标准答案,建议结合实际情况深入思考。
