找回密码
立即注册
搜索
发回帖 发新帖

6442

积分

0

好友

787

主题
发表于 4 小时前 | 查看: 5| 回复: 0

有网友吐槽,离职前在工作群里发了一段告别消息,结果主管直接回了一句:“你走就走,没必要发这些废话,没人会记得你。”

离职告别被主管回怼的聊天截图

这类冲突在技术团队里其实并不少见。一个开发离开,真正暴露的问题往往不是告别消息有没有人回复,而是某个服务、某段业务规则、某个线上处理流程,是不是也跟着这个人一起消失了。

我之前维护过一个订单系统,某个核心优惠计算模块长期由一名开发负责。平时看不出什么问题,代码也能正常跑,但等到这个开发离职后,新同事第一次接手改规则,线上立刻出现了大量订单金额异常。排查之后发现,系统最大的风险不是代码写得差,而是关键业务知识没有沉淀到代码和机制里。

当时这个优惠服务的逻辑大概类似这样:

public BigDecimal calculateDiscount(Order order) {
    if (order.getUserLevel() == 3
            && order.getCreateTime().isAfter(activityStartTime)) {
        return order.getAmount()
                .multiply(new BigDecimal("0.8"));
    }

    return BigDecimal.ZERO;
}

光看代码只是一个简单判断,但线上规则远比这个复杂。

会员等级、活动时间、渠道来源、历史参与记录都会影响最终优惠结果。以前这些条件都存在开发人员的脑子里,代码只是最后执行的一部分。后来新需求要求增加“同一用户每天最多享受一次优惠”,接手的同事直接加了逻辑:

@Transactional
public void createOrder(Order order) {
    BigDecimal discount = discountService.calculateDiscount(order);

    order.setDiscount(discount);
    orderMapper.insert(order);

    discountRecordMapper.insert(
        new DiscountRecord(order.getUserId(), discount)
    );
}

上线当天就出现了重复优惠。

第一反应是怀疑数据库事务问题,因为优惠记录和订单写入在同一个事务里,乍一看没有明显漏洞。

检查 MySQL 慢查询,没有异常。

检查事务提交日志,也没有回滚。

继续看调用链:

order-service
    |
    |-- discount-service
            |
            |-- redis 查询用户优惠次数
            |
            |-- mysql 保存优惠记录

最终发现问题出在 Redis 查询环节。

原来的逻辑为了降低数据库压力,加了一层缓存:

String key = "discount:" + userId;

Integer count = redisTemplate.opsForValue()
        .get(key);

if (count != null && count >= 1) {
    return false;
}

但是优惠成功后,数据库记录增加了,Redis 却没有同步更新。

结果就是:

第一次请求:

Redis: null
MySQL: insert discount_record

第二次请求:

Redis: null
MySQL: insert discount_record

数据库里出现两条优惠记录。

这里的问题并不是 Redis 用错了,而是原来的设计依赖人工记忆。那个离职开发知道:“优惠成功后必须清缓存。”但系统不知道。

后来我们调整了实现,把业务状态从人的经验里拿出来。优惠记录写入后,通过统一方法更新缓存:

@Transactional
public void saveDiscountRecord(Long userId, BigDecimal amount) {
    DiscountRecord record =
        new DiscountRecord(userId, amount);

    discountRecordMapper.insert(record);

    String key = "discount:" + userId;

    redisTemplate.opsForValue()
            .set(
                key,
                getTodayDiscountCount(userId),
                24,
                TimeUnit.HOURS
            );
}

同时增加数据库唯一约束,避免代码异常情况下产生重复数据:

ALTER TABLE discount_record
ADD UNIQUE KEY uk_user_day(user_id, discount_day);

这样即使缓存失效,数据库也有最后一道保护。

线上系统里,很多所谓“某个人走了没人接”的问题,本质都是知识没有进入系统。一个开发熟悉某个模块,并不代表系统可靠;可靠的系统应该让新人通过代码、日志、监控和约束快速理解,而不是靠老人临走前交代几句“注意清缓存”。

后来我们还补充了关键业务日志:

log.info(
    "discount apply userId={}, orderId={}, amount={}, rule={}",
    userId,
    orderId,
    discountAmount,
    ruleVersion
);

出现问题时,不需要再依赖某个人解释“当时为什么这么写”。

回到离职告别这件事,Redis 这类基础设施上的坑,往往不是技术本身有多难,而是团队把太多隐性知识留在了个人身上。真正需要沉淀的,从来不是某个人的脑内经验,而是代码、流程和机制。




上一篇:SpringBoot 定时清理 Minio 历史文件:Cron、线程池与异步配置实战
下一篇:GPU 和 CPU 的区别是什么?为什么挖矿、大模型训练都用 GPU?
您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-30 07:25 , Processed in 0.077572 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表