从“搞不定”到“稳如老狗”:小厂后端的综合技术突围记

502守望者
2025-12-27 14:18
阅读 2150

大家好,我是阿哲,一家不到百人规模的小厂后端开发,独立负责一条核心业务线快两年了。说“核心”可能有点夸张——毕竟我们老板眼里的“核心”是能带来营收的功能,而我眼里的“核心”是别在大促时崩掉就行。

去年双11前夜,我坐在工位上盯着监控面板,手心全是汗。系统QPS突然飙到平时的5倍,数据库连接池直接打满,Redis缓存击穿,API响应时间从200ms飙升到3s+。产品在群里@我:“阿哲,用户反馈支付卡住了!”运维在隔壁工位小声嘀咕:“这架构撑不住啊……”那一刻,我真的想拔电源跑路。

但跑不了。因为这条业务线只有我一个人维护——全栈?不,是“全背锅”。也正是那次事故之后,我意识到:光会CRUD不行了,得搞点综合性的技术方案,把系统真正稳住。更现实的是,我最近在偷偷看机会,准备求职跳槽。简历上不能只写“用Spring Boot写了几个接口”,得有点硬核内容。于是,我开始了一段边救火、边学习、边重构的技术探索之路。


一、起因:一个“看起来简单”的需求

事情的导火索其实很普通。产品想做一个“用户积分兑换限量商品”的活动,要求:

  • 积分实时扣减
  • 商品库存严格一致(不能超卖)
  • 兑换记录可追溯、不可篡改

乍一听,不就是个典型的秒杀场景吗?加个分布式锁、Redis预减库存、异步落库,搞定!我一开始也是这么想的。

结果上线第一天,测试就报了个诡异Bug:同一个用户两次兑换,第一次成功,第二次居然也成功了,但积分只扣了一次。查日志发现,两次请求几乎同时到达,分布式锁没生效——因为用的是Redis的SETNX,但网络抖动导致锁提前释放。

更糟的是,运营第二天跑来问:“能不能证明这个用户确实兑换了?万一他赖账说没收到呢?” 我心想:数据库里有记录啊。他说:“但你能保证没人改过吗?”

那一刻我愣住了。传统数据库虽然有事务,但记录是可以被DBA手动update甚至delete的。如果真要“不可篡改”,怎么办?

产品经理补刀:“听说现在区块链挺火的,能不能用上?显得我们技术前沿一点。”

我差点一口老血喷出来。区块链?我们这种小厂连K8s都没上全,还上链?但转念一想:如果真能低成本实现一个轻量级的不可篡改日志,说不定是个亮点,对求职也有帮助。


二、技术选型:别被“高大上”带偏了节奏

很多人一听到区块链,脑子里就是比特币、以太坊、智能合约。但对我们这种资源有限的小团队来说,直接上公链或私有链节点成本太高——服务器、运维、学习曲线,样样要命。

我冷静下来想了想,核心诉求其实是“不可篡改的审计日志”,而不是去中心化交易或Token经济。所以,没必要照搬整套区块链架构,而是借鉴其核心思想:哈希链 + 时间戳 + 去中心化存储(可选)

于是,我决定自研一个轻量级的“伪区块链”模块,只保留最核心的特性:

  1. 每条关键操作(如积分兑换)生成一条记录
  2. 每条记录包含前一条记录的哈希值,形成链式结构
  3. 记录写入后无法单独修改(否则哈希断链)
  4. 可选择将哈希根存到公链(如以太坊)作为“锚点”,增强可信度(非必须)

这样既满足了“不可篡改”的业务需求,又避免了引入重型基础设施。我把这个方案叫“ChainLog”。


三、实战:从设计到落地的血泪史

1. 数据结构设计

首先定义记录结构:

{
  "id": "log_12345",
  "timestamp": 1712345678,
  "operation": "exchange_points",
  "userId": "user_67890",
  "details": {
    "pointsUsed": 1000,
    "itemId": "item_abc",
    "quantity": 1
  },
  "prevHash": "a1b2c3d4...", // 上一条记录的哈希
  "currentHash": "e5f6g7h8..." // 当前记录的哈希(含prevHash)
}

关键点在于 currentHash = SHA256(全部字段 + prevHash)。这样,只要任意字段被篡改,或者顺序被打乱,哈希链就断了。

2. 写入流程改造

原来的兑换逻辑是:

// 伪代码
if (user.points >= required) {
    user.points -= required;
    inventory.decrease(itemId);
    exchangeRecord.save();
}

现在要改成:

@Transactional
public void exchangePoints(User user, Item item) {
    // 1. 预检查(略)
    
    // 2. 扣减积分 & 库存(原子操作)
    updatePointsAndInventory(user, item);
    
    // 3. 构造ChainLog记录
    ChainLog newLog = buildChainLog(user, item);
    
    // 4. 获取上一条日志的哈希
    String lastHash = chainLogRepository.findLatestHash();
    newLog.setPrevHash(lastHash);
    
    // 5. 计算当前哈希
    newLog.setCurrentHash(calculateHash(newLog));
    
    // 6. 保存到专用表(与业务库同实例,但独立表)
    chainLogRepository.save(newLog);
}

这里有个坑:必须和业务操作在同一个事务里!否则如果业务成功但日志失败,数据就不一致了。好在我们用的是MySQL,支持本地事务,暂时不用上分布式事务。

3. 验证机制

怎么证明没被篡改?提供一个验证接口:

public boolean verifyChain() {
    List<ChainLog> logs = chainLogRepository.findAllOrdered();
    for (int i = 1; i < logs.size(); i++) {
        ChainLog current = logs.get(i);
        ChainLog prev = logs.get(i - 1);
        String expectedHash = calculateHash(current.withPrevHash(prev.getCurrentHash()));
        if (!expectedHash.equals(current.getCurrentHash())) {
            return false; // 链断裂!
        }
    }
    return true;
}

运营现在可以随时点击“验证”按钮,系统返回“链完整”或“第X条记录异常”,再也不用担心用户扯皮了。


四、进阶:要不要真的上链?

做到这一步,业务需求其实已经满足了。但我想再进一步:如果能把哈希根定期提交到公链,是不是更有说服力?

比如,每天凌晨,把当天所有ChainLog的Merkle Root(默克尔树根哈希)写入以太坊的一个智能合约。这样,即使我们的数据库被完全清空,只要公链上的根还在,就能证明历史数据的存在性和完整性。

技术上可行,但成本呢?查了下,以太坊主网一次写入Gas费大概$2~5(波动大)。我们这种小厂,一天一块钱都心疼。于是,我调研了几个替代方案:

方案 成本 可信度 实现难度
以太坊主网 高 ($2+) 极高
Polygon侧链 低 ($0.01)
IPFS + Filecoin 极低
自建私有链 零(仅服务器)

最后我选了Polygon。便宜、兼容EVM、生态成熟。用Web3j写了个定时任务:

// 每天00:10执行
@Scheduled(cron = "0 10 0 * * ?")
public void submitMerkleRootToPolygon() {
    String merkleRoot = computeDailyMerkleRoot();
    String txHash = polygonService.submitRoot(merkleRoot);
    log.info("Submitted root to Polygon: {}", txHash);
}

虽然目前还没真正用上这个功能(老板觉得没必要),但我在简历里写上了“设计并实现基于区块链思想的审计日志系统,并集成Polygon进行数据锚定” ——面试官眼睛都亮了。


五、效果与反思

上线三个月,系统再没出现过积分/库存不一致的问题。运营再也不用半夜打电话问我“用户说没到账”。最重要的是,整个方案只增加了不到200行核心代码,零额外服务器成本

当然,也有教训:

  • 不要为了用技术而用技术。最初我差点直接上Hyperledger Fabric,后来发现杀鸡用了牛刀。
  • 小厂资源有限,优先解决痛点。不可篡改是需求,区块链只是思路之一。
  • 综合能力比单一技术更重要。这次项目涉及数据库事务、密码学、Web3、定时任务调度,逼着我横向学习。

上周五晚上,我又在加班调一个新需求。产品跑过来问:“阿哲,这个新功能能不能也上链?” 我笑了笑:“上什么链?先说清楚业务目标。” 他愣了一下,然后点点头走了。

那一刻,我觉得自己终于从“工具人”变成了“解决方案提供者”。


六、给同行的建议

如果你也在小厂单打独斗,想提升技术深度又苦于没有复杂场景,不妨试试:

  1. 从线上问题反推架构缺陷。每次事故都是重构的契机。
  2. 用“综合”思维解决问题。别局限于后端,前端、DB、运维、安全,都要懂一点。
  3. 把项目经历包装成故事。面试时别说“我用了Redis”,要说“为解决缓存击穿,我设计了多级防护体系,QPS提升3倍”。
  4. 适当蹭热点,但别浮夸。区块链、AI、大模型,都可以作为技术思路的启发,而不是终点。

说到AI,我最近也在学LangChain和向量数据库,想着能不能把用户行为日志做语义分析,提前预测异常。不过那是下一篇的故事了。

最后,如果你也在求职,记住:小厂经历不是劣势,反而是你独立解决问题能力的证明。把每一次“救火”,都变成简历上的“架构优化”。

共勉。

评论 0

最热最新
暂无评论
502守望者Lv.1
0
影响力
0
文章
0
粉丝