
做了这么多年系统设计,我最大的体会就是:从来没有所谓的“完美方案”,只有针对当下场景最合适的权衡与取舍。如果你不理解这背后的折衷,就很难真正入门系统设计。今天,我就来聊聊架构师日常工作中绕不开的10个关键选择。
1. 垂直扩展还是水平扩展?
垂直扩展很简单,就是给单台服务器升级——加CPU、加内存、换更快的硬盘。水平扩展则是增加服务器的数量,让它们协同工作。
垂直扩展的好处是立竿见影,配置和管理都相对简单。但硬件总有上限,而且这台“巨无霸”一旦宕机,整个服务可能就瘫痪了。水平扩展理论上没有性能天花板,可分布式系统带来的复杂度会让你头疼:数据一致性怎么保证?服务如何发现彼此?负载怎么均衡?这些都是你必须处理的“麻烦”。
我的建议是:在业务早期、用户量不大的时候,先用垂直扩展快速上线验证想法;等用户量和数据量增长上来,再平滑地过渡到水平扩展的架构。
2. SQL还是NoSQL?
SQL数据库(比如 MySQL、PostgreSQL)擅长处理关系复杂、对事务一致性要求极高的场景,比如电商的订单、支付系统。NoSQL数据库(如MongoDB、Redis)则在数据结构灵活多变、读写吞吐量巨大的场景中表现突出,比如社交媒体的动态流。
现实中,大多数项目都是混合使用的:用MySQL存储核心的、关系严谨的业务数据;用Redis作为高性能缓存,扛住热点访问。
3. 批处理还是流处理?
批处理是把一段时间内累积的数据打包,一次性处理完。比如每天凌晨统计前一天的销售报表。流处理则是数据一来就立刻处理,像实时监控异常交易、或者实时推荐系统。
如果业务对实时性不敏感,又很在意成本,批处理通常就够用了。但对于需要即时响应的场景,比如金融风控、系统监控告警,流处理才是更合适的选择。
4. 规范化还是反规范化?
规范化的目标是消除数据冗余,把数据拆分到不同的表里。比如用户信息单独存一张表,订单表里只保存用户ID。反规范化则是为了追求极致的查询速度,故意冗余存储数据。比如在订单表里直接存上用户名,这样查订单时就不用再去关联用户表了。
一个简单的判断原则:写多读少、对数据一致性要求极高的场景,用规范化。读多写少、对查询性能有极致要求的场景,可以考虑反规范化来换取速度。
5. 一致性还是可用性?
这是CAP定理抛出的经典难题:当网络发生分区时,你优先保证一致性(Consistency)还是可用性(Availability)?
选择一致性,意味着系统宁可返回错误,也要确保你读到的数据是最新、正确的。银行的转账余额必须如此。选择可用性,则意味着系统总能给你一个响应,哪怕这个数据可能不是最新的。社交媒体上的点赞数、阅读量,通常可以接受这种延迟。
涉及到资金、库存等核心业务数据,一致性通常不容妥协。而对于用户体验类、非核心的功能,适当牺牲强一致性来换取更高的可用性,是更务实的选择。
6. 强一致性还是最终一致性?
强一致性要求数据一旦写入成功,后续的读取操作立刻就能读到这个新值。最终一致性则允许一个时间窗口,写入后数据可能需要几毫秒甚至几秒钟,才能同步到所有的副本节点。
你的银行账户余额、订单的支付状态,必须保证强一致。但文章评论区的人数统计、商品的累计销量,采用最终一致性通常就足够了。根据业务场景选择合适的一致性级别,能极大地解放系统的性能潜力。
7. REST还是GraphQL?
RESTful API为每个资源设计独立的接口,想要获取关联数据往往需要发起多次请求。GraphQL则允许客户端在一次查询中精确描述自己需要的数据,服务端按需返回,但它的 Schema 设计和维护成本更高。
对于简单的增删改查应用,REST 足够清晰易用。但如果你的数据关联非常复杂,特别是面对多端(Web、iOS、Android)且各端需求差异大时,GraphQL 能显著减少网络请求次数,提升效率。
8. 有状态还是无状态?
有状态服务会记住客户端的上下文信息,比如维持一个WebSocket连接,或者记住用户的购物车内容。无状态服务则将每次请求视为独立的,服务端不保存任何会话状态。
需要实时双向通信、或保持长时间会话的场景,自然要用有状态服务。而在需要轻松水平扩展的API服务层,保持无状态是黄金法则——把状态信息(如Session)统一存储到Redis这样的外部中间件中。
9. 读穿透还是写穿透缓存?
读穿透策略下,应用先查缓存,如果缓存没有命中,则去查询数据库,并将结果写回缓存。写穿透策略下,应用在更新数据时,会同时更新缓存和底层数据库。
读多写少的场景,比如新闻门户的文章详情,用读穿透就很好。如果是写操作频繁,且对数据一致性要求很高的场景,比如库存扣减,写穿透更能保证缓存与数据库的同步。
10. 同步还是异步处理?
同步处理像排队,前一个任务没完成,后一个就得等着。异步处理则把耗时任务丢到后台去执行,主流程立刻返回,继续处理其他事情。发送短信、生成复杂报表都很适合异步。
需要立即知道结果、且操作简单的逻辑,用同步更直观。对于那些耗时较长、用户不需要立即感知结果的操作,异步处理能大幅提升系统的整体吞吐量和响应速度。消息队列是实现异步解耦的利器。
写在最后
说到底,系统设计就是在各种约束(时间、预算、团队、技术)下做一连串的取舍。每个看似美好的选择,背后都标好了价格,世上没有“银弹”。
给刚入行的朋友几点朴素的建议:
- 业务优先:先解决“有没有”的问题,再优化“好不好”。
- 简单够用:从最简单的方案开始,能支撑业务发展就行。
- 预留弹性:为未来可能的变化留些扩展空间,但切忌过度设计。
- 数据驱动:建立完善的监控体系,用真实的数据指标来指导架构演进。
能扎实解决当前问题、有力支撑业务成长的架构,就是好架构,无论它看起来多么“朴素”。这些关于权衡的思考,也欢迎你来 云栈社区 与我们继续深入探讨。