2、单屏架构的黄金时代:单体应用与单一数据库的甜蜜与苦涩
说实话,每次回想起那个年代,我心里都有点复杂。
那时候我刚入行,接手的是一个典型的单体应用。一个项目,一个代码仓库,一个数据库。前端、后端、业务逻辑全揉在一起。部署?一台服务器搞定。开发?一个IDE打开,从头写到尾。
听起来是不是很爽?
嗯,确实爽过。但后来也苦过。
2.1 什么是单体应用?
单体应用,说白了就是把所有功能都塞进一个进程里。用户管理、订单处理、支付逻辑、报表生成……全在一个包里。
我习惯把它比作一个“大箱子”。所有东西都往里扔,找的时候翻一翻就行。早期项目,这招特别好使。
单体应用的核心特征:
- 所有代码在一个项目里编译、打包
- 部署时只产出一个 WAR/JAR 包
- 所有模块共享同一个数据库实例
- 扩展时只能整体水平复制
举个例子,一个典型的电商单体应用,结构大概是这样:
my-shop/
├── src/
│ ├── main/
│ │ ├── java/com/shop/
│ │ │ ├── controller/ # 控制器层
│ │ │ ├── service/ # 业务逻辑层
│ │ │ ├── dao/ # 数据访问层
│ │ │ └── model/ # 实体模型
│ │ └── resources/
│ │ ├── application.yml
│ │ └── mapper/ # MyBatis映射
│ └── webapp/
│ ├── WEB-INF/
│ └── static/ # JS、CSS、图片
└── pom.xml # 一个pom管理所有依赖
你看,所有东西都在一个目录树里。开发时爽,但维护时……嗯,后面再说。
2.2 甜蜜期:为什么大家都爱它?
我记得2015年那会儿,我带一个5人团队做内部管理系统。从立项到上线,只用了三周。为什么这么快?因为单体架构太适合小团队了。
开发效率高
你想想看,一个项目,一个IDE窗口。改个接口,调个页面,全在同一个代码库里。不需要跨服务联调,不需要考虑网络延迟。本地启动,直接调试。
部署简单
打个包,扔到Tomcat的webapps目录下,重启完事。那时候我经常跟运维说:“就一个war包,你看着办。”运维也乐得清闲。
测试成本低
写个集成测试,从Controller到数据库,一条链路跑通。不需要Mock服务,不需要搭建复杂的测试环境。我习惯在测试类里直接注入所有Bean,跑起来特别快。
事务管理方便
这一点太重要了。一个@Transactional注解,整个方法都在一个事务里。扣库存、减余额、生成订单,要么全成功,要么全回滚。我在项目中遇到过好几次因为分布式事务搞不定的情况,那时候特别怀念单体应用的一把锁搞定一切。
个人经验: 如果你的团队不超过10人,业务复杂度不高,用户量在几千级别,单体应用绝对是最优解。别为了“架构先进”而过度设计。
2.3 苦涩期:当甜蜜变成负担
但是,好景不长。
项目上线半年后,用户量从几百涨到了几万。功能从十几个模块膨胀到了五十多个。这时候,问题开始冒出来了。
2.3.1 代码耦合:改一处,崩一片
这是最让人头疼的。我记得有一次,一个同事改了一个工具类的静态方法,结果影响了三个业务模块的订单生成逻辑。线上出了故障,排查了整整一个下午。
为什么会这样?因为所有模块都在同一个进程里,类加载器共享同一个命名空间。你改了一个公共方法,所有调用方都得跟着测一遍。
避坑指南: 我曾经见过一个项目,单体应用的代码量超过50万行。每次发布光编译就要20分钟。更可怕的是,没人敢重构——因为不知道改了哪里会出问题。
2.3.2 数据库瓶颈:单库扛不住了
单一数据库,说白了就是所有业务都挤在一个库里。用户表、订单表、商品表、日志表……全在一个实例上。
一开始还好,数据量上来了,慢查询就多了。我印象最深的一次,是报表模块跑了一个全表扫描,直接把数据库CPU打满,导致用户登录都超时了。
常见问题:
- 连接池争抢:报表查询占着连接不放,用户请求排队等待
- 锁竞争:一个长事务锁住了某张表,其他操作全部阻塞
- 备份困难:单库数据量太大,备份时间窗口不够用
2.3.3 扩展性差:只能垂直扩展
用户量上来了怎么办?加机器。但单体应用只能整体复制。你想想看,如果只是订单模块压力大,你也得把整个应用复制一份。这就导致资源浪费严重。
我习惯把这种扩展方式叫做“大水漫灌”——不管哪里缺水,先灌满再说。成本高,效率低。
2.3.4 发布风险高
这个我深有体会。有一次,我只是改了一个支付页面的文案,但因为和订单模块在同一个包里,不得不把整个应用重新发布。结果发布过程中,订单模块的一个缓存问题导致线上短暂不可用。
说白了,单体应用的发布是“全有或全无”。改一行代码,也得重启整个进程。这在早期还能忍,到了后期,每周发布一次都提心吊胆。
2.4 什么时候该告别单体?
这个问题没有标准答案。但我个人有几个判断指标:
| 指标 | 警戒线 | 说明 |
|---|---|---|
| 代码行数 | 超过20万行 | 编译时间超过5分钟,开发效率明显下降 |
| 团队人数 | 超过15人 | 代码冲突频繁,沟通成本剧增 |
| 数据库QPS | 超过5000 | 单库开始出现性能瓶颈 |
| 发布频率 | 每周超过2次 | 每次发布都是全量发布,风险高 |
当然,这些数字不是绝对的。我见过一个项目,代码量30万行,但团队配合默契,依然跑得挺好。也见过一个项目,代码量才5万行,但因为设计混乱,已经寸步难行。
核心观点: 单体架构本身不是问题,问题在于它是否还适合你当前的业务规模和团队规模。不要为了“微服务”而微服务,也不要死守着单体不放。
2.5 小结
单体应用与单一数据库,是软件架构的起点,也是很多人的初恋。它简单、直接、高效,适合小团队、小项目、早期阶段。
但它的甜蜜是有代价的。当业务复杂到一定程度,代码耦合、数据库瓶颈、扩展困难这些问题就会像滚雪球一样越滚越大。
嗯,下一章我们会聊聊,当单体扛不住的时候,我们是怎么一步步走向多屏架构的。那又是一段新的故事了。