3. 系统架构演进
说实话,我见过太多团队一上来就搞微服务、上分布式,结果把自己搞崩了。架构演进这事儿,得一步步来。今天我就结合自己踩过的坑,聊聊这三条演进路线。
3.1 从单体到微服务:拆还是不拆?
单体架构,说白了就是把所有代码塞进一个进程里。早期项目这么搞没问题,开发快、部署简单。但用户量一上来,问题就来了——改一行代码得重启整个服务,一个小功能出问题可能拖垮全站。
我记得有个电商项目,双十一前夜因为优惠券模块的内存泄漏,整站挂了半小时。那会儿我就在想:要是拆开了,至少用户还能下单啊。
单体架构的痛点:
- 代码耦合严重,改一处动全身
- 无法独立扩缩容——你只是想加搜索节点,却得把整个应用都复制一份
- 技术栈锁定——用了 Java 就不能在某个模块用 Go
微服务怎么拆?我个人的习惯是:按业务边界拆,而不是按技术层拆。比如用户服务、订单服务、支付服务,每个服务有自己的数据库,独立部署。
我的经验:别一上来就拆太细。我见过一个团队把 20 人的项目拆成 30 个微服务,结果光服务间通信就把人搞疯了。建议先拆成 3-5 个核心服务跑通流程,再逐步细化。
拆完之后,服务间怎么通信?常见方案有两种:
| 通信方式 | 适用场景 | 我踩过的坑 |
|---|---|---|
| RESTful API | 查询类、同步操作 | 超时设置不合理,导致连锁雪崩 |
| 消息队列 | 异步任务、事件通知 | 没做幂等处理,重复消费搞出脏数据 |
| gRPC | 内部高频调用 | 版本兼容没做好,升级时服务全挂 |
3.2 从集中式到分布式:数据不在一处放
集中式架构,所有数据都存一个库里。听起来简单,但扛不住啊。我做过一个报表系统,单库单表到了 5000 万行,一个简单的 count 查询就要 30 秒。
分布式说白了就是:把数据和计算分散到多台机器上,对外看起来还像一台。这里面有几个关键点:
- 分库分表:按用户 ID 哈希分片,或者按时间范围分片。我建议用一致性哈希,这样加机器时迁移数据量最小。
- 读写分离:主库写,从库读。但要注意主从延迟——我曾经因为没处理这个,用户刚下单就查不到订单,被投诉惨了。
- 分布式事务:这是最头疼的。强一致性用两阶段提交(2PC),但性能差;最终一致性用 TCC 或 Saga,但实现复杂。
避坑指南:我曾经在一个支付系统里用了 2PC,结果某个资源管理器挂了,事务卡住半小时,所有相关操作都堵死了。后来改成 Saga 模式,虽然最终一致,但至少系统不卡死。
分布式还有个绕不开的问题:CAP 理论。一致性、可用性、分区容忍性,三者只能取其二。实际生产中,大部分系统选 AP(可用性 + 分区容忍性),用最终一致性来弥补。
3.3 从同步到异步:别让调用方等着
同步调用,你想想看:A 调用 B,B 没返回,A 就干等着。如果 B 慢了,A 的线程就挂在那,资源全浪费了。高并发下,线程池一满,整个系统就瘫了。
异步的核心思想是:把耗时的操作丢到后台,先给调用方一个“我收到了”的响应。具体怎么做?
- 消息队列解耦:生产者发消息到队列,消费者慢慢处理。我常用 RabbitMQ 或 Kafka,前者适合业务消息,后者适合日志和流处理。
- 事件驱动架构:服务之间通过事件通信,不直接调用。比如订单创建后发个事件,库存服务、物流服务各自处理。
- 回调与 Future:Java 里的 CompletableFuture,或者 JavaScript 的 Promise,都能实现异步非阻塞。
异步带来的好处:
- 系统吞吐量提升 3-5 倍——线程不再傻等
- 削峰填谷——突发流量先积压在队列里,后端慢慢消化
- 服务解耦——一个服务挂了不影响其他服务
但异步也有代价。调试困难、链路追踪复杂、需要处理消息丢失和重复。我建议在核心链路用同步保证一致性,非核心链路用异步提升性能。
3.4 三条演进路线的关系
这三条线不是独立的,它们往往同时发生。你拆微服务时,自然就要考虑分布式数据;你搞分布式时,异步通信就成了必然选择。
下面这张图是我自己总结的演进路径,你可以参考:
你看,从单体到微服务,从集中式到分布式,从同步到异步——每一步都是为了解决上一阶段的问题。单体扛不住了就拆,集中式扛不住了就分,同步扛不住了就异步。别为了技术而技术,架构演进永远是为业务服务的。
我的建议:如果你现在还在单体阶段,别急着上微服务。先把读写分离做了,再把慢查询优化了,最后考虑拆分。我见过太多团队在单体都没跑明白的情况下搞分布式,结果死得更快。