第二章:库存系统架构演进——从单体到中台的蜕变之路

大家好,我是老张。今天咱们聊聊库存系统架构的演进历程。说实话,我从业这十几年,亲眼看着库存系统从“一个文件搞定”变成了“一个团队都搞不定”的庞然大物。这中间踩过的坑,够写一本小说了。

你想想看,一个电商平台的库存系统,从最初每天几百单,到后来双十一每秒几万单,架构不变能行吗?肯定不行。那咱们就从头捋一捋,看看这四代架构到底是怎么演进的。

库存系统架构演进路线 单体 2005-2010 All in One 垂直 2010-2014 按业务拆分 微服务 2014-2018 独立部署 中台 2018-至今 能力复用 特点:简单、耦合高 痛点:改一处动全身 适用:小团队、低并发 特点:按业务域拆分 痛点:重复建设严重 适用:中型团队 特点:独立部署、弹性 痛点:分布式复杂性 适用:大团队、高并发 特点:能力复用 痛点:组织架构调整 适用:多业务线公司

2.1 单体架构:一切从简

最早期的库存系统,说白了就是一个大杂烩。订单、商品、库存、用户,全写在一个项目里。我记得2008年我刚入行时,公司那个库存系统就是一个Java Web项目,所有代码都在一个WAR包里。

2.1.1 单体架构长什么样?

简单来说,就是所有功能都塞进一个进程里。库存查询、库存扣减、库存预警,全在一个应用里跑。数据库也就一个MySQL实例,几张表搞定。

// 典型的单体库存系统代码结构
com.company.inventory/
├── controller/
│   ├── InventoryController.java    // 库存接口
│   └── OrderController.java        // 订单接口(也在同一个项目里)
├── service/
│   ├── InventoryService.java       // 库存业务逻辑
│   └── OrderService.java           // 订单业务逻辑
├── dao/
│   └── InventoryDao.java           // 数据库操作
└── model/
    └── Inventory.java              // 库存实体类

你看,所有东西都在一起。开发的时候确实爽,本地启动一个Tomcat就能跑。但问题也来了——

2.1.2 单体架构的优缺点

优点 缺点
开发简单,上手快 耦合严重,改一处可能影响全局
部署方便,一个WAR包搞定 无法独立扩展,库存压力大时整个系统都得扩容
调试容易,本地就能跑 团队协作困难,代码冲突频繁
事务好控制,本地事务就够 技术栈绑定,想换语言?重写吧

我踩过的坑: 2012年在一家电商公司,双十一前夜改了一个库存扣减的逻辑,结果把订单模块的某个功能搞崩了。为什么?因为两个模块共享了一个静态变量。嗯,从那以后我养成了一个习惯——任何共享状态都要三思而后行。

2.2 垂直拆分:按业务切一刀

随着业务增长,单体架构扛不住了。我记得当时公司业务线多了起来,有自营、有第三方、有跨境。所有库存都混在一起,谁都不敢动。于是,垂直拆分来了。

2.2.1 什么是垂直拆分?

说白了,就是按业务领域把系统拆成多个子系统。库存系统独立出来,订单系统独立出来,商品系统也独立出来。每个系统有自己的数据库,通过API互相调用。

// 垂直拆分后的库存系统
// 库存系统独立部署,只负责库存相关逻辑
@RestController
@RequestMapping("/inventory")
public class InventoryController {
    
    @Autowired
    private InventoryService inventoryService;
    
    @PostMapping("/deduct")
    public Result deductStock(@RequestBody DeductRequest request) {
        // 只做库存扣减,不关心订单
        return inventoryService.deduct(request.getSkuId(), request.getQuantity());
    }
    
    @GetMapping("/query/{skuId}")
    public Result queryStock(@PathVariable String skuId) {
        return inventoryService.queryStock(skuId);
    }
}

2.2.2 垂直拆分的优缺点

优点 缺点
系统解耦,各团队独立开发 跨系统调用变多,接口设计成关键
可以独立扩缩容,库存系统单独加机器 分布式事务问题出现,库存扣减和订单创建需要协调
技术栈可以不同,库存系统用Java,订单系统用Go都行 重复建设严重,每个系统都要做权限、日志、监控
故障隔离,库存系统挂了不影响订单查询 数据一致性难保证,库存扣减成功但订单创建失败怎么办?

注意: 垂直拆分不是银弹。我见过一个团队把系统拆成了十几个子系统,结果每个子系统都自己写了一套库存扣减逻辑。你想想看,同样的业务逻辑重复了十几遍,这不是更乱了吗?

2.3 微服务架构:更细粒度的拆分

垂直拆分之后,大家发现每个子系统内部还是太臃肿。库存系统里,库存查询、库存扣减、库存预警、库存调拨,全在一个服务里。于是,微服务来了。

2.3.1 微服务怎么拆?

微服务的核心思想是:每个服务只做一件事,并且做好。库存系统可以拆成:

  • 库存查询服务:只负责读,可以加缓存,扛高并发
  • 库存扣减服务:负责写,需要保证数据一致性
  • 库存预警服务:定时检查库存水位,触发补货
  • 库存调拨服务:处理仓库间的库存转移
// 微服务架构下的库存扣减服务
// 独立部署,独立数据库
@Service
public class DeductStockService {
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Autowired
    private InventoryMapper inventoryMapper;
    
    @Transactional
    public boolean deduct(String skuId, int quantity) {
        // 1. 先查缓存
        Integer stock = (Integer) redisTemplate.opsForValue().get("stock:" + skuId);
        if (stock == null || stock < quantity) {
            return false;
        }
        
        // 2. 用分布式锁防止超卖
        String lockKey = "lock:stock:" + skuId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(5, TimeUnit.SECONDS);
            
            // 3. 扣减数据库
            int affected = inventoryMapper.deductStock(skuId, quantity);
            if (affected > 0) {
                // 4. 更新缓存
                redisTemplate.opsForValue().decrement("stock:" + skuId, quantity);
                return true;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }
}

2.3.2 微服务的优缺点

优点 缺点
独立部署,快速迭代 分布式复杂性剧增,服务发现、配置中心、熔断降级都得搞
弹性伸缩,库存扣减服务可以单独扩到100台 数据一致性更难,最终一致性方案需要精心设计
故障隔离,库存查询服务挂了不影响扣减 运维成本高,几十个服务需要监控、日志、链路追踪
技术多样性,不同服务可以用不同语言 团队沟通成本增加,一个需求可能要协调多个服务

我的建议: 微服务不是越细越好。我曾经见过一个团队把库存服务拆成了20多个微服务,结果每次上线都要协调十几个团队。说白了,微服务的粒度应该以“业务边界”为准,而不是技术上的“能拆就拆”。

2.4 中台架构:能力复用

微服务之后,大家发现了一个新问题:公司有多个业务线,每个业务线都自己搞了一套库存服务。自营业务有库存,第三方业务有库存,跨境业务也有库存。虽然都是库存,但实现各不相同。于是,中台思想来了。

2.4.1 中台的核心是什么?

中台说白了就是“共享服务层”。把各个业务线通用的能力抽出来,形成一个统一的中台。库存中台提供统一的库存查询、扣减、预占等能力,所有业务线都调用同一个中台。

// 库存中台:统一对外接口
// 所有业务线都通过这个接口操作库存
@RestController
@RequestMapping("/inventory-center")
public class InventoryCenterController {
    
    @Autowired
    private InventoryCenterService centerService;
    
    // 统一库存扣减接口
    @PostMapping("/deduct")
    public Result deduct(@RequestBody DeductRequest request) {
        // request中包含业务线标识
        String bizLine = request.getBizLine();
        // 不同业务线可能有不同的扣减策略
        return centerService.deduct(bizLine, request.getSkuId(), request.getQuantity());
    }
    
    // 统一库存查询接口
    @GetMapping("/query")
    public Result query(@RequestParam String bizLine, @RequestParam String skuId) {
        return centerService.query(bizLine, skuId);
    }
}

2.4.2 中台的优缺点

优点 缺点
能力复用,避免重复建设 中台容易变成“大泥球”,什么逻辑都往里塞
统一管控,库存规则一致 组织架构调整困难,需要打破部门墙
降低业务线开发成本 中台团队容易成为瓶颈,所有需求都得排队
数据统一,全局库存视图 中台变更影响所有业务线,上线风险大

我踩过的坑: 曾经参与过一个中台项目,中台团队把所有的业务逻辑都抽象成了“通用能力”。结果呢?通用是通用了,但每个业务线都要做定制化开发,最后中台代码里全是if-else判断业务线。嗯,中台不是万能药,过度抽象反而会害了自己。

2.5 总结:没有最好的架构,只有最合适的

说了这么多,你可能会问:到底该选哪种架构?我的答案是:看阶段。

  • 初创期:单体架构就够了,别折腾。快速验证业务才是王道。
  • 成长期:垂直拆分,按业务域切分。这时候团队开始扩张,需要明确分工。
  • 爆发期:微服务架构,细粒度拆分。高并发、高可用是核心诉求。
  • 成熟期:中台架构,能力复用。多业务线并行,需要统一管控。

记住一句话:架构是演进而来的,不是设计出来的。别一开始就想着搞个完美的中台,先把业务跑起来再说。


公众号:蓝海资料掘金营,微信deep3321